/* * Copyright (c) 2002-2015, JIDE Software Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. */ package jidefx.utils; import java.lang.reflect.Array; import java.lang.reflect.InvocationTargetException; import java.util.Comparator; import java.util.List; import java.util.Objects; import java.util.function.Function; /** * Utils that contains methods that are not depending on JavaFX classes. */ public class CommonUtils { /** * Checks if the two objects equal. If both are null, they are equal. If o1 and o2 both are Comparable, we will use * compareTo method to see if it equals 0. At last, we will use {@code o1.equals(o2)} to compare. If none of * the above conditions match, we return false. * * @param o1 the first object to compare * @param o2 the second object to compare * @return true if the two objects are equal. Otherwise false. */ public static boolean equals(Object o1, Object o2) { return equals(o1, o2, false); } /** * Checks if the two objects equal. If both are the same instance, they are equal. If both are null, they are equal. * If o1 and o2 both are Comparable, we will use compareTo method to see if it equals 0. If considerArrayOrList is * true and o1 and o2 are both array, we will compare each element in the array. At last, we will use * {@code o1.equals(o2)} to compare. If none of the above conditions match, we return false. * * @param o1 the first object to compare * @param o2 the second object to compare * @param considerArrayOrList If true, and if o1 and o2 are both array, we will compare each element in the array * instead of just compare the two array objects. * @return true if the two objects are equal. Otherwise false. */ public static boolean equals(Object o1, Object o2, boolean considerArrayOrList) { if (o1 == o2) { return true; } else if (o1 != null && o2 == null) { return false; } else if (o1 == null) { return false; } else if (o1 instanceof CharSequence && o2 instanceof CharSequence) { return equals((CharSequence) o1, (CharSequence) o2, true); } else if (o1 instanceof Comparable && o2 instanceof Comparable && o1.getClass().isAssignableFrom(o2.getClass())) { return ((Comparable) o1).compareTo(o2) == 0; } else if (o1 instanceof Comparable && o2 instanceof Comparable && o2.getClass().isAssignableFrom(o1.getClass())) { return ((Comparable) o2).compareTo(o1) == 0; } else if (considerArrayOrList && o1 instanceof List && o2 instanceof List) { int length1 = ((List) o1).size(); int length2 = ((List) o2).size(); if (length1 != length2) { return false; } for (int i = 0; i < length1; i++) { if (!equals(((List) o1).get(i), ((List) o2).get(i), true)) { return false; } } return true; } else { if (considerArrayOrList && o1.getClass().isArray() && o2.getClass().isArray()) { int length1 = Array.getLength(o1); int length2 = Array.getLength(o2); if (length1 != length2) { return false; } for (int i = 0; i < length1; i++) { if (!equals(Array.get(o1, i), Array.get(o2, i), true)) { return false; } } return true; } else { return o1.equals(o2); } } } public static boolean equals(CharSequence s1, CharSequence s2, boolean caseSensitive) { if (s1 == s2) return true; if (s1 == null || s2 == null) return false; // Algorithm from String.regionMatches() if (s1.length() != s2.length()) return false; int to = 0; int po = 0; int len = s1.length(); while (len-- > 0) { char c1 = s1.charAt(to++); char c2 = s2.charAt(po++); if (c1 == c2) { continue; } if (!caseSensitive && charsEqualIgnoreCase(c1, c2)) continue; return false; } return true; } public static boolean charsEqualIgnoreCase(char a, char b) { return a == b || toUpperCase(a) == toUpperCase(b) || toLowerCase(a) == toLowerCase(b); } /** * A toUpperCase routine which is faster to process the ASCII lowercase letters than Character.toUpperCase. * * @param a the character to be converted. * @return the uppercase equivalent of the character, if any; otherwise, the character itself. */ public static char toUpperCase(char a) { if (a < 'a') { return a; } if (a >= 'a' && a <= 'z') { return (char) (a + ('A' - 'a')); } return Character.toUpperCase(a); } /** * A toLowerCase routine which is faster to process the ASCII lowercase letters then Character.toLowerCase. * * @param a the character to be converted. * @return the lowercase equivalent of the character, if any; otherwise, the character itself. */ public static char toLowerCase(final char a) { if (a < 'A' || a >= 'a' && a <= 'z') { return a; } if (a >= 'A' && a <= 'Z') { return (char) (a + ('a' - 'A')); } return Character.toLowerCase(a); } /** * Ignore the exception. This method does nothing. However it's a good practice to use this method so that we can * easily find out the place that ignoring exception. In development phase, we can log a message in this method so * that we can verify if it makes sense to ignore. * * @param e the exception */ @SuppressWarnings("UnusedParameters") public static void ignoreException(Exception e) { // e.printStackTrace(); } /** * Prints out the message of the exception. * * @param e the exception */ public static void printException(Exception e) { System.err.println(e.getLocalizedMessage()); } /** * Throws the exception. If the exception is RuntimeException, just throw it. Otherwise, wrap it in RuntimeException * and throw it. * * @param e the exception */ public static void throwException(Exception e) { if (e instanceof RuntimeException) { throw (RuntimeException) e; } else { throw new RuntimeException(e); } } /** * Throws the InvocationTargetException. Usually InvocationTargetException has a nested exception as target * exception. If the target exception is a RuntimeException or Error, we will throw it. Otherwise, we will wrap it * inside RuntimeException and throw it. * * @param e the exception */ public static void throwInvocationTargetException(InvocationTargetException e) { // in most cases, target exception will be RuntimeException // but to be on safer side (it may be Error) we explicitly check it if (e.getTargetException() instanceof RuntimeException) { throw (RuntimeException) e.getTargetException(); } else if (e.getTargetException() instanceof Error) { throw (Error) e.getTargetException(); } else { throw new RuntimeException(e.getTargetException()); } } /** * Perform a binary search over a sorted list for the given key. * * @param a the array to search * @param key the key to search for * @return the index of the given key if it exists in the list, otherwise -1 times the index value at the insertion * point that would be used if the key were added to the list. */ @SuppressWarnings("unchecked") public static int binarySearch(List<Object> a, Object key) { int x1 = 0; int x2 = a.size(); int i = x2 / 2, c; while (x1 < x2) { if (!(a.get(i) instanceof Comparable)) { return i; } c = ((Comparable) a.get(i)).compareTo(key); if (c == 0) { return i; } else if (c < 0) { x1 = i + 1; } else { x2 = i; } i = x1 + (x2 - x1) / 2; } return -1 * i; } /** * Perform a binary search over a sorted array for the given key. * * @param a the array to search * @param key the key to search for * @return the index of the given key if it exists in the array, otherwise -1 times the index value at the insertion * point that would be used if the key were added to the array. */ @SuppressWarnings("unchecked") public static int binarySearch(Object[] a, Object key) { int x1 = 0; int x2 = a.length; int i = x2 / 2, c; while (x1 < x2) { if (!(a[i] instanceof Comparable)) { return i; } c = ((Comparable) a[i]).compareTo(key); if (c == 0) { return i; } else if (c < 0) { x1 = i + 1; } else { x2 = i; } i = x1 + (x2 - x1) / 2; } return -1 * i; } /** * Perform a binary search over a sorted array for the given key. * * @param a the array to search * @param key the key to search for * @return the index of the given key if it exists in the array, otherwise -1 times the index value at the insertion * point that would be used if the key were added to the array. */ public static int binarySearch(int[] a, int key) { return binarySearch(a, key, 0, a.length); } /** * Perform a binary search over a sorted array for the given key. * * @param a the array to search * @param key the key to search for * @param start the start index to search inclusive * @param end the end index to search exclusive * @return the index of the given key if it exists in the array, otherwise -1 times the index value at the insertion * point that would be used if the key were added to the array. */ public static int binarySearch(int[] a, int key, int start, int end) { int x1 = start; int x2 = end; int i = x2 / 2; while (x1 < x2) { if (a[i] == key) { return i; } else if (a[i] < key) { x1 = i + 1; } else { x2 = i; } i = x1 + (x2 - x1) / 2; } return -1 * i; } /** * Accepts a function that extracts an object of a type {@code U} as sort key from a type {@code T}, and returns the * object that compares by the specified comparator. For example, if a class {@code Element} has a {@code value} * which has a getter, but we only have a Comparator for the value, not for the {@code Element}. We can use this * method to call {@code Person} objects to get the value, then use the existing Comparator for the value. * * @param <T> the original element type * @param <U> the actual type for comparison * @param comparator a comparator that can sort the value. * @param keyExtractor the function used to extract the {@code value} sort key for the Comparator */ public static <T, U> Comparator<T> comparing(Comparator<? super U> comparator, Function<? super T, ? extends U> keyExtractor) { Objects.requireNonNull(comparator); Objects.requireNonNull(keyExtractor); return new Comparator<T>() { @Override public int compare(T c1, T c2) { return comparator.compare(keyExtractor.apply(c1), keyExtractor.apply(c2)); } }; } }