/* * Copyright 2008 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package java.util; import com.google.gwt.core.client.JavaScriptObject; import com.google.gwt.core.client.UnsafeNativeLong; import com.google.gwt.lang.Array; import java.io.Serializable; /** * Utility methods related to native arrays. <a * href="http://java.sun.com/j2se/1.5.0/docs/api/java/util/Arrays.html">[Sun * docs]</a> */ public class Arrays { private static final class ArrayList<E> extends AbstractList<E> implements RandomAccess, Serializable { /** * The only reason this is non-final is so that E[] (and E) will be exposed * for serialization. */ private E[] array; ArrayList(E[] array) { assert (array != null); this.array = array; } @Override public boolean contains(Object o) { return (indexOf(o) != -1); } @Override public E get(int index) { checkIndex(index, size()); return array[index]; } @Override public E set(int index, E value) { checkIndex(index, size()); E was = array[index]; array[index] = value; return was; } @Override public int size() { return array.length; } /* * Semantics are to return an array of identical type. */ @Override public Object[] toArray() { return Array.clone(array); } /* * Faster than the iterator-based implementation in AbstractCollection. */ @SuppressWarnings("unchecked") @Override public <T> T[] toArray(T[] out) { int size = size(); if (out.length < size) { out = Array.createFrom(out, size); } for (int i = 0; i < size; ++i) { out[i] = (T) array[i]; } if (out.length > size) { out[size] = null; } return out; } } public static <T> List<T> asList(T... array) { return new ArrayList<T>(array); } /** * Perform a binary search on a sorted byte array. * * @param sortedArray byte array to search * @param key value to search for * @return the index of an element with a matching value, or a negative number * which is the index of the next larger value (or just past the end * of the array if the searched value is larger than all elements in * the array) minus 1 (to ensure error returns are negative) */ public static int binarySearch(final byte[] sortedArray, final byte key) { int low = 0; int high = sortedArray.length - 1; while (low <= high) { final int mid = low + ((high - low) >> 1); final byte midVal = sortedArray[mid]; if (midVal < key) { low = mid + 1; } else if (midVal > key) { high = mid - 1; } else { // key found return mid; } } // key not found. return -low - 1; } /** * Perform a binary search on a sorted char array. * * @param a char array to search * @param key value to search for * @return the index of an element with a matching value, or a negative number * which is the index of the next larger value (or just past the end * of the array if the searched value is larger than all elements in * the array) minus 1 (to ensure error returns are negative) */ public static int binarySearch(final char[] a, final char key) { int low = 0; int high = a.length - 1; while (low <= high) { final int mid = low + ((high - low) >> 1); final char midVal = a[mid]; if (midVal < key) { low = mid + 1; } else if (midVal > key) { high = mid - 1; } else { // key found return mid; } } // key not found. return -low - 1; } /** * Perform a binary search on a sorted double array. * * @param sortedArray double array to search * @param key value to search for * @return the index of an element with a matching value, or a negative number * which is the index of the next larger value (or just past the end * of the array if the searched value is larger than all elements in * the array) minus 1 (to ensure error returns are negative) */ public static int binarySearch(final double[] sortedArray, final double key) { int low = 0; int high = sortedArray.length - 1; while (low <= high) { final int mid = low + ((high - low) >> 1); final double midVal = sortedArray[mid]; if (midVal < key) { low = mid + 1; } else if (midVal > key) { high = mid - 1; } else { // key found return mid; } } // key not found. return -low - 1; } /** * Perform a binary search on a sorted float array. * * Note that some underlying JavaScript interpreters do not actually implement * floats (using double instead), so you may get slightly different behavior * regarding values that are very close (or equal) since conversion errors * to/from double may change the values slightly. * * @param sortedArray float array to search * @param key value to search for * @return the index of an element with a matching value, or a negative number * which is the index of the next larger value (or just past the end * of the array if the searched value is larger than all elements in * the array) minus 1 (to ensure error returns are negative) */ public static int binarySearch(final float[] sortedArray, final float key) { int low = 0; int high = sortedArray.length - 1; while (low <= high) { final int mid = low + ((high - low) >> 1); final float midVal = sortedArray[mid]; if (midVal < key) { low = mid + 1; } else if (midVal > key) { high = mid - 1; } else { // key found return mid; } } // key not found. return -low - 1; } /** * Perform a binary search on a sorted int array. * * @param sortedArray int array to search * @param key value to search for * @return the index of an element with a matching value, or a negative number * which is the index of the next larger value (or just past the end * of the array if the searched value is larger than all elements in * the array) minus 1 (to ensure error returns are negative) */ public static int binarySearch(final int[] sortedArray, final int key) { int low = 0; int high = sortedArray.length - 1; while (low <= high) { final int mid = low + ((high - low) >> 1); final int midVal = sortedArray[mid]; if (midVal < key) { low = mid + 1; } else if (midVal > key) { high = mid - 1; } else { // key found return mid; } } // key not found. return -low - 1; } /** * Perform a binary search on a sorted long array. * * Note that most underlying JavaScript interpreters do not actually implement * longs, so the values must be stored in doubles instead. This means that * certain legal values cannot be represented, and comparison of two unequal * long values may result in unexpected results if they are not also * representable as doubles. * * @param sortedArray long array to search * @param key value to search for * @return the index of an element with a matching value, or a negative number * which is the index of the next larger value (or just past the end * of the array if the searched value is larger than all elements in * the array) minus 1 (to ensure error returns are negative) */ public static int binarySearch(final long[] sortedArray, final long key) { int low = 0; int high = sortedArray.length - 1; while (low <= high) { final int mid = low + ((high - low) >> 1); final long midVal = sortedArray[mid]; if (midVal < key) { low = mid + 1; } else if (midVal > key) { high = mid - 1; } else { // key found return mid; } } // key not found. return -low - 1; } /** * Perform a binary search on a sorted object array, using natural ordering. * * @param sortedArray object array to search * @param key value to search for * @return the index of an element with a matching value, or a negative number * which is the index of the next larger value (or just past the end * of the array if the searched value is larger than all elements in * the array) minus 1 (to ensure error returns are negative) * @throws ClassCastException if <code>key</code> is not comparable to * <code>sortedArray</code>'s elements. */ public static int binarySearch(final Object[] sortedArray, final Object key) { return binarySearch(sortedArray, key, Comparators.natural()); } /** * Perform a binary search on a sorted short array. * * @param sortedArray short array to search * @param key value to search for * @return the index of an element with a matching value, or a negative number * which is the index of the next larger value (or just past the end * of the array if the searched value is larger than all elements in * the array) minus 1 (to ensure error returns are negative) */ public static int binarySearch(final short[] sortedArray, final short key) { int low = 0; int high = sortedArray.length - 1; while (low <= high) { final int mid = low + ((high - low) >> 1); final short midVal = sortedArray[mid]; if (midVal < key) { low = mid + 1; } else if (midVal > key) { high = mid - 1; } else { // key found return mid; } } // key not found. return -low - 1; } /** * Perform a binary search on a sorted object array, using a user-specified * comparison function. * * @param sortedArray object array to search * @param key value to search for * @param comparator comparision function, <code>null</code> indicates * <i>natural ordering</i> should be used. * @return the index of an element with a matching value, or a negative number * which is the index of the next larger value (or just past the end * of the array if the searched value is larger than all elements in * the array) minus 1 (to ensure error returns are negative) * @throws ClassCastException if <code>key</code> and * <code>sortedArray</code>'s elements cannot be compared by * <code>comparator</code>. */ public static <T> int binarySearch(final T[] sortedArray, final T key, Comparator<? super T> comparator) { if (comparator == null) { comparator = Comparators.natural(); } int low = 0; int high = sortedArray.length - 1; while (low <= high) { final int mid = low + ((high - low) >> 1); final T midVal = sortedArray[mid]; final int compareResult = comparator.compare(midVal, key); if (compareResult < 0) { low = mid + 1; } else if (compareResult > 0) { high = mid - 1; } else { // key found return mid; } } // key not found. return -low - 1; } public static boolean deepEquals(Object[] a1, Object[] a2) { if (a1 == a2) { return true; } if (a1 == null || a2 == null) { return false; } if (a1.length != a2.length) { return false; } for (int i = 0, n = a1.length; i < n; ++i) { Object obj1 = a1[i]; Object obj2 = a2[i]; if (obj1 == obj2) { continue; } if (obj1 == null || obj2 == null) { return false; } if (obj1.equals(obj2)) { continue; } Class<?> class1 = obj1.getClass(); Class<?> class2 = obj2.getClass(); // We have to test and see if these are two arrays of the same type, // then see what types of arrays they are and dispatch to the // appropriate equals if (!class1.isArray() || !class1.equals(class2)) { return false; } if (obj1 instanceof Object[]) { if (!deepEquals((Object[]) obj1, (Object[]) obj2)) { return false; } } else if (obj1 instanceof boolean[]) { if (!equals((boolean[]) obj1, (boolean[]) obj2)) { return false; } } else if (obj1 instanceof byte[]) { if (!equals((byte[]) obj1, (byte[]) obj2)) { return false; } } else if (obj1 instanceof char[]) { if (!equals((char[]) obj1, (char[]) obj2)) { return false; } } else if (obj1 instanceof short[]) { if (!equals((short[]) obj1, (short[]) obj2)) { return false; } } else if (obj1 instanceof int[]) { if (!equals((int[]) obj1, (int[]) obj2)) { return false; } } else if (obj1 instanceof long[]) { if (!equals((long[]) obj1, (long[]) obj2)) { return false; } } else if (obj1 instanceof float[]) { if (!equals((float[]) obj1, (float[]) obj2)) { return false; } } else if (obj1 instanceof double[]) { if (!equals((double[]) obj1, (double[]) obj2)) { return false; } } } return true; } public static int deepHashCode(Object[] a) { if (a == null) { return 0; } int hashCode = 1; for (int i = 0, n = a.length; i < n; ++i) { Object obj = a[i]; int hash; if (obj instanceof Object[]) { hash = deepHashCode((Object[]) obj); } else if (obj instanceof boolean[]) { hash = hashCode((boolean[]) obj); } else if (obj instanceof byte[]) { hash = hashCode((byte[]) obj); } else if (obj instanceof char[]) { hash = hashCode((char[]) obj); } else if (obj instanceof short[]) { hash = hashCode((short[]) obj); } else if (obj instanceof int[]) { hash = hashCode((int[]) obj); } else if (obj instanceof long[]) { hash = hashCode((long[]) obj); } else if (obj instanceof float[]) { hash = hashCode((float[]) obj); } else if (obj instanceof double[]) { hash = hashCode((double[]) obj); } else if (obj != null) { hash = obj.hashCode(); } else { hash = 0; } // nasty trick related to JS and lack of integer rollover hashCode = (31 * hashCode + hash) | 0; } return hashCode; } public static String deepToString(Object[] a) { return deepToString(a, new HashSet<Object[]>()); } public static boolean equals(boolean[] array1, boolean[] array2) { if (array1 == array2) { return true; } if (array1 == null || array2 == null) { return false; } if (array1.length != array2.length) { return false; } for (int i = 0; i < array1.length; ++i) { if (array1[i] != array2[i]) { return false; } } return true; } public static boolean equals(byte[] array1, byte[] array2) { if (array1 == array2) { return true; } if (array1 == null || array2 == null) { return false; } if (array1.length != array2.length) { return false; } for (int i = 0; i < array1.length; ++i) { if (array1[i] != array2[i]) { return false; } } return true; } public static boolean equals(char[] array1, char[] array2) { if (array1 == array2) { return true; } if (array1 == null || array2 == null) { return false; } if (array1.length != array2.length) { return false; } for (int i = 0; i < array1.length; ++i) { if (array1[i] != array2[i]) { return false; } } return true; } public static boolean equals(double[] array1, double[] array2) { if (array1 == array2) { return true; } if (array1 == null || array2 == null) { return false; } if (array1.length != array2.length) { return false; } for (int i = 0; i < array1.length; ++i) { if (array1[i] != array2[i]) { return false; } } return true; } public static boolean equals(float[] array1, float[] array2) { if (array1 == array2) { return true; } if (array1 == null || array2 == null) { return false; } if (array1.length != array2.length) { return false; } for (int i = 0; i < array1.length; ++i) { if (array1[i] != array2[i]) { return false; } } return true; } public static boolean equals(int[] array1, int[] array2) { if (array1 == array2) { return true; } if (array1 == null || array2 == null) { return false; } if (array1.length != array2.length) { return false; } for (int i = 0; i < array1.length; ++i) { if (array1[i] != array2[i]) { return false; } } return true; } public static boolean equals(long[] array1, long[] array2) { if (array1 == array2) { return true; } if (array1 == null || array2 == null) { return false; } if (array1.length != array2.length) { return false; } for (int i = 0; i < array1.length; ++i) { if (array1[i] != array2[i]) { return false; } } return true; } public static boolean equals(Object[] array1, Object[] array2) { if (array1 == array2) { return true; } if (array1 == null || array2 == null) { return false; } if (array1.length != array2.length) { return false; } for (int i = 0; i < array1.length; ++i) { Object val1 = array1[i]; Object val2 = array2[i]; if (!Utility.equalsWithNullCheck(val1, val2)) { return false; } } return true; } public static boolean equals(short[] array1, short[] array2) { if (array1 == array2) { return true; } if (array1 == null || array2 == null) { return false; } if (array1.length != array2.length) { return false; } for (int i = 0; i < array1.length; ++i) { if (array1[i] != array2[i]) { return false; } } return true; } public static void fill(boolean[] a, boolean val) { fill(a, 0, a.length, val); } public static void fill(boolean[] a, int fromIndex, int toIndex, boolean val) { for (int i = fromIndex; i < toIndex; ++i) { a[i] = val; } } public static void fill(byte[] a, byte val) { fill(a, 0, a.length, val); } public static void fill(byte[] a, int fromIndex, int toIndex, byte val) { for (int i = fromIndex; i < toIndex; ++i) { a[i] = val; } } public static void fill(char[] a, char val) { fill(a, 0, a.length, val); } public static void fill(char[] a, int fromIndex, int toIndex, char val) { for (int i = fromIndex; i < toIndex; ++i) { a[i] = val; } } public static void fill(double[] a, double val) { fill(a, 0, a.length, val); } public static void fill(double[] a, int fromIndex, int toIndex, double val) { for (int i = fromIndex; i < toIndex; ++i) { a[i] = val; } } public static void fill(float[] a, float val) { fill(a, 0, a.length, val); } public static void fill(float[] a, int fromIndex, int toIndex, float val) { for (int i = fromIndex; i < toIndex; ++i) { a[i] = val; } } public static void fill(int[] a, int val) { fill(a, 0, a.length, val); } public static void fill(int[] a, int fromIndex, int toIndex, int val) { for (int i = fromIndex; i < toIndex; ++i) { a[i] = val; } } public static void fill(long[] a, int fromIndex, int toIndex, long val) { for (int i = fromIndex; i < toIndex; ++i) { a[i] = val; } } public static void fill(long[] a, long val) { fill(a, 0, a.length, val); } public static void fill(Object[] a, int fromIndex, int toIndex, Object val) { for (int i = fromIndex; i < toIndex; ++i) { a[i] = val; } } public static void fill(Object[] a, Object val) { fill(a, 0, a.length, val); } public static void fill(short[] a, int fromIndex, int toIndex, short val) { for (int i = fromIndex; i < toIndex; ++i) { a[i] = val; } } public static void fill(short[] a, short val) { fill(a, 0, a.length, val); } public static int hashCode(boolean[] a) { if (a == null) { return 0; } int hashCode = 1; for (int i = 0, n = a.length; i < n; ++i) { hashCode = (31 * hashCode + (Boolean.valueOf(a[i]).hashCode())) | 0; } return hashCode; } public static int hashCode(byte[] a) { if (a == null) { return 0; } int hashCode = 1; for (int i = 0, n = a.length; i < n; ++i) { hashCode = (31 * hashCode + Byte.hashCode(a[i])) | 0; } return hashCode; } public static int hashCode(char[] a) { if (a == null) { return 0; } int hashCode = 1; for (int i = 0, n = a.length; i < n; ++i) { hashCode = (31 * hashCode + Character.hashCode(a[i])) | 0; } return hashCode; } public static int hashCode(double[] a) { if (a == null) { return 0; } int hashCode = 1; for (int i = 0, n = a.length; i < n; ++i) { hashCode = (31 * hashCode + Double.hashCode(a[i])) | 0; } return hashCode; } public static int hashCode(float[] a) { if (a == null) { return 0; } int hashCode = 1; for (int i = 0, n = a.length; i < n; ++i) { hashCode = (31 * hashCode + Float.hashCode(a[i])) | 0; } return hashCode; } public static int hashCode(int[] a) { if (a == null) { return 0; } int hashCode = 1; for (int i = 0, n = a.length; i < n; ++i) { hashCode = (31 * hashCode + Integer.hashCode(a[i])) | 0; } return hashCode; } public static int hashCode(long[] a) { if (a == null) { return 0; } int hashCode = 1; for (int i = 0, n = a.length; i < n; ++i) { hashCode = (31 * hashCode + Long.hashCode(a[i])) | 0; } return hashCode; } public static int hashCode(Object[] a) { if (a == null) { return 0; } int hashCode = 1; for (Object e : a) { hashCode = (31 * hashCode + (e == null ? 0 : e.hashCode())) | 0; } return hashCode; } public static int hashCode(short[] a) { if (a == null) { return 0; } int hashCode = 1; for (int i = 0, n = a.length; i < n; ++i) { hashCode = (31 * hashCode + Short.hashCode(a[i])) | 0; } return hashCode; } public static void sort(byte[] array) { nativeNumberSort(array); } public static void sort(byte[] array, int fromIndex, int toIndex) { verifySortIndices(fromIndex, toIndex, array.length); nativeNumberSort(array, fromIndex, toIndex); } public static void sort(char[] array) { nativeNumberSort(array); } public static void sort(char[] array, int fromIndex, int toIndex) { verifySortIndices(fromIndex, toIndex, array.length); nativeNumberSort(array, fromIndex, toIndex); } public static void sort(double[] array) { nativeNumberSort(array); } public static void sort(double[] array, int fromIndex, int toIndex) { verifySortIndices(fromIndex, toIndex, array.length); nativeNumberSort(array, fromIndex, toIndex); } public static void sort(float[] array) { nativeNumberSort(array); } public static void sort(float[] array, int fromIndex, int toIndex) { verifySortIndices(fromIndex, toIndex, array.length); nativeNumberSort(array, fromIndex, toIndex); } public static void sort(int[] array) { nativeNumberSort(array); } public static void sort(int[] array, int fromIndex, int toIndex) { verifySortIndices(fromIndex, toIndex, array.length); nativeNumberSort(array, fromIndex, toIndex); } public static void sort(long[] array) { nativeLongSort(array); } public static void sort(long[] array, int fromIndex, int toIndex) { verifySortIndices(fromIndex, toIndex, array.length); nativeLongSort(array, fromIndex, toIndex); } public static void sort(Object[] array) { // Can't use native JS sort because it isn't stable. // -- Commented out implementation that uses the native sort with a fixup. // nativeObjSort(array, 0, array.length, getNativeComparator(array, // Comparators.natural())); mergeSort(array, 0, array.length, Comparators.natural()); } public static void sort(Object[] x, int fromIndex, int toIndex) { // Can't use native JS sort because it isn't stable. // -- Commented out implementation that uses the native sort with a fixup. // nativeObjSort(x, fromIndex, toIndex, getNativeComparator(x, // Comparators.natural())); mergeSort(x, fromIndex, toIndex, Comparators.natural()); } public static void sort(short[] array) { nativeNumberSort(array); } public static void sort(short[] array, int fromIndex, int toIndex) { verifySortIndices(fromIndex, toIndex, array.length); nativeNumberSort(array, fromIndex, toIndex); } public static <T> void sort(T[] x, Comparator<? super T> c) { // Commented out implementation that uses the native sort with a fixup. // nativeObjSort(x, 0, x.length, getNativeComparator(x, c != null ? c : // Comparators.natural())); mergeSort(x, 0, x.length, c != null ? c : Comparators.natural()); } public static <T> void sort(T[] x, int fromIndex, int toIndex, Comparator<? super T> c) { // Commented out implementation that uses the native sort with a fixup. verifySortIndices(fromIndex, toIndex, x.length); // nativeObjSort(x, fromIndex, toIndex, getNativeComparator(x, c != null ? c // : Comparators.natural())); mergeSort(x, fromIndex, toIndex, c != null ? c : Comparators.natural()); } public static String toString(boolean[] a) { if (a == null) { return "null"; } StringBuffer b = new StringBuffer("["); for (int i = 0; i < a.length; i++) { if (i != 0) { b.append(", "); } b.append(String.valueOf(a[i])); } b.append("]"); return b.toString(); } public static String toString(byte[] a) { if (a == null) { return "null"; } StringBuffer b = new StringBuffer("["); for (int i = 0; i < a.length; i++) { if (i != 0) { b.append(", "); } b.append(String.valueOf(a[i])); } b.append("]"); return b.toString(); } public static String toString(char[] a) { if (a == null) { return "null"; } StringBuffer b = new StringBuffer("["); for (int i = 0; i < a.length; i++) { if (i != 0) { b.append(", "); } b.append(String.valueOf(a[i])); } b.append("]"); return b.toString(); } public static String toString(double[] a) { if (a == null) { return "null"; } StringBuffer b = new StringBuffer("["); for (int i = 0; i < a.length; i++) { if (i != 0) { b.append(", "); } b.append(String.valueOf(a[i])); } b.append("]"); return b.toString(); } public static String toString(float[] a) { if (a == null) { return "null"; } StringBuffer b = new StringBuffer("["); for (int i = 0; i < a.length; i++) { if (i != 0) { b.append(", "); } b.append(String.valueOf(a[i])); } b.append("]"); return b.toString(); } public static String toString(int[] a) { if (a == null) { return "null"; } StringBuffer b = new StringBuffer("["); for (int i = 0; i < a.length; i++) { if (i != 0) { b.append(", "); } b.append(String.valueOf(a[i])); } b.append("]"); return b.toString(); } public static String toString(long[] a) { if (a == null) { return "null"; } StringBuffer b = new StringBuffer("["); for (int i = 0; i < a.length; i++) { if (i != 0) { b.append(", "); } b.append(String.valueOf(a[i])); } b.append("]"); return b.toString(); } public static String toString(Object[] x) { if (x == null) { return "null"; } return Arrays.asList(x).toString(); } public static String toString(short[] a) { if (a == null) { return "null"; } StringBuffer b = new StringBuffer("["); for (int i = 0; i < a.length; i++) { if (i != 0) { b.append(", "); } b.append(String.valueOf(a[i])); } b.append("]"); return b.toString(); } /** * Recursive helper function for {@link Arrays#deepToString(Object[])}. */ private static String deepToString(Object[] a, Set<Object[]> arraysIveSeen) { if (a == null) { return "null"; } if (arraysIveSeen.contains(a)) { return "[...]"; } arraysIveSeen.add(a); StringBuffer b = new StringBuffer("["); for (int i = 0; i < a.length; i++) { if (i != 0) { b.append(", "); } Object obj = a[i]; if (obj == null) { b.append("null"); } else if (obj.getClass().isArray()) { if (obj instanceof Object[]) { if (arraysIveSeen.contains(obj)) { b.append("[...]"); } else { Object[] objArray = (Object[]) obj; HashSet<Object[]> tempSet = new HashSet<Object[]>(arraysIveSeen); b.append(deepToString(objArray, tempSet)); } } else if (obj instanceof boolean[]) { b.append(toString((boolean[]) obj)); } else if (obj instanceof byte[]) { b.append(toString((byte[]) obj)); } else if (obj instanceof char[]) { b.append(toString((char[]) obj)); } else if (obj instanceof short[]) { b.append(toString((short[]) obj)); } else if (obj instanceof int[]) { b.append(toString((int[]) obj)); } else if (obj instanceof long[]) { b.append(toString((long[]) obj)); } else if (obj instanceof float[]) { b.append(toString((float[]) obj)); } else if (obj instanceof double[]) { b.append(toString((double[]) obj)); } assert false : "Unexpected array type: " + obj.getClass().getName(); } else { b.append(String.valueOf(obj)); } } b.append("]"); return b.toString(); } /** * Return a JavaScript function object which will compare elements of the * specified object array. * * Note that this function isn't currently used but is kept because the native * sort/fixup approach is faster everywhere but IE. In the future, we may * choose to use deferred binding in the JRE to make those platforms faster. * * @param array the array of objects to compare * @param comp the Comparator to use to compare individual objects. * @return a JavaScript function object taking indices into the array to * compare. Returns the result of the comparator, or the comparison of * the indices if the comparator indicates equality so the sort is * stable. The comparator has a property <code>swap</code> which is * true if any elements were discovered to be out of order. */ @SuppressWarnings("unused") // see above private static native JavaScriptObject getNativeComparator(Object array, Comparator<?> comp) /*-{ function compare(a, b) { var elementCompare = comp.@java.util.Comparator::compare(Ljava/lang/Object;Ljava/lang/Object;)(array[a], array[b]); var indexCompare = a - b; // If elements compare equal, use the index comparison. elementCompare = elementCompare || indexCompare; // Keep track of having seen out-of-order elements. Note that we don't // have to worry about the sort algorithm comparing an element to itself // since it can't be swapped anyway, so we can just check for less-than. compare.swap = compare.swap || (elementCompare < 0 != indexCompare < 0); return elementCompare; } compare.swap = false; return compare; }-*/; /** * Sort a small subsection of an array by insertion sort. * * @param array array to sort * @param low lower bound of range to sort * @param high upper bound of range to sort * @param comp comparator to use */ private static void insertionSort(Object[] array, int low, int high, Comparator<Object> comp) { for (int i = low + 1; i < high; ++i) { for (int j = i; j > low && comp.compare(array[j - 1], array[j]) > 0; --j) { Object t = array[j]; array[j] = array[j - 1]; array[j - 1] = t; } } } /** * Merge the two sorted subarrays (srcLow,srcMid] and (srcMid,srcHigh] into * dest. * * @param src source array for merge * @param srcLow lower bound of bottom sorted half * @param srcMid upper bound of bottom sorted half & lower bound of top sorted * half * @param srcHigh upper bound of top sorted half * @param dest destination array for merge * @param destLow lower bound of destination * @param destHigh upper bound of destination * @param comp comparator to use */ private static void merge(Object[] src, int srcLow, int srcMid, int srcHigh, Object[] dest, int destLow, int destHigh, Comparator<Object> comp) { // can't destroy srcMid because we need it as a bound on the lower half int topIdx = srcMid; while (destLow < destHigh) { if (topIdx >= srcHigh || (srcLow < srcMid && comp.compare(src[srcLow], src[topIdx]) <= 0)) { dest[destLow++] = src[srcLow++]; } else { dest[destLow++] = src[topIdx++]; } } } /** * Performs a merge sort on the specified portion of an object array. * * Uses O(n) temporary space to perform the merge, but is stable. */ @SuppressWarnings("unchecked") private static void mergeSort(Object[] x, int fromIndex, int toIndex, Comparator<?> comp) { Object[] temp = Array.cloneSubrange(x, fromIndex, toIndex); mergeSort(temp, x, fromIndex, toIndex, -fromIndex, (Comparator<Object>) comp); } /** * Recursive helper function for * {@link Arrays#mergeSort(Object[], int, int, Comparator)}. * * @param temp temporary space, as large as the range of elements being * sorted. On entry, temp should contain a copy of the sort range * from array. * @param array array to sort * @param low lower bound of range to sort * @param high upper bound of range to sort * @param ofs offset to convert an array index into a temp index * @param comp comparison function */ private static void mergeSort(Object[] temp, Object[] array, int low, int high, int ofs, Comparator<Object> comp) { int length = high - low; // insertion sort for small arrays if (length < 7) { insertionSort(array, low, high, comp); return; } // recursively sort both halves, using the array as temp space int tempLow = low + ofs; int tempHigh = high + ofs; int tempMid = tempLow + ((tempHigh - tempLow) >> 1); mergeSort(array, temp, tempLow, tempMid, -ofs, comp); mergeSort(array, temp, tempMid, tempHigh, -ofs, comp); // Skip merge if already in order - just copy from temp if (comp.compare(temp[tempMid - 1], temp[tempMid]) <= 0) { // TODO(jat): use System.arraycopy when that is implemented and more // efficient than this while (low < high) { array[low++] = temp[tempLow++]; } return; } // merge sorted halves merge(temp, tempLow, tempMid, tempHigh, array, low, high, comp); } /** * Sort an entire array of number primitives. */ @UnsafeNativeLong private static native void nativeLongSort(Object array) /*-{ array.sort(@com.google.gwt.lang.LongLib::compare(Lcom/google/gwt/lang/LongLibBase$LongEmul;Lcom/google/gwt/lang/LongLibBase$LongEmul;)); }-*/; /** * Sort a subset of an array of number primitives. */ @UnsafeNativeLong private static native void nativeLongSort(Object array, int fromIndex, int toIndex) /*-{ var temp = array.slice(fromIndex, toIndex); temp.sort(@com.google.gwt.lang.LongLib::compare(Lcom/google/gwt/lang/LongLibBase$LongEmul;Lcom/google/gwt/lang/LongLibBase$LongEmul;)); var n = toIndex - fromIndex; // Do the equivalent of array.splice(fromIndex, n, temp) except // flattening the temp slice. Array.prototype.splice.apply(array, [fromIndex, n].concat(temp)); }-*/; /** * Sort an entire array of number primitives. */ private static native void nativeNumberSort(Object array) /*-{ array.sort(function(a, b) { return a - b; }); }-*/; /** * Sort a subset of an array of number primitives. */ private static native void nativeNumberSort(Object array, int fromIndex, int toIndex) /*-{ var temp = array.slice(fromIndex, toIndex); temp.sort(function(a, b) { return a - b; }); var n = toIndex - fromIndex; // Do the equivalent of array.splice(fromIndex, n, temp) except // flattening the temp slice. Array.prototype.splice.apply(array, [fromIndex, n].concat(temp.slice(0, n))); }-*/; /** * Sort a subset of an array with the specified comparison function. Note that * the array is also referenced via closure in the comparison function. * * This implementation sorts it using the native (unstable) sort using an * index array and comparing the indices if they are otherwise equal, then * making another pass through the array to put them into the proper order. * This adds O(2*n) space for the index array and a temporary copy for * re-ordering (one of which is required anyway since JavaScript can't sort * subsets of an array), and the re-order pass takes O(n) time. * * Note that this function isn't currently used but is kept because the native * sort/fixup approach is faster everywhere but IE. In the future, we may * choose to use deferred binding in the JRE to make those platforms faster. * * @param array an array of either Java primitives or Object references * @param fromIndex the start of the range to sort * @param toIndex one past the end of the range to sort * @param comp a JavaScript comparison function (which holds reference to the * array to sort), which will be passed indices into the array. The * comparison function must also have a property swap which is true * if any elements were out of order. */ @SuppressWarnings("unused") // Currently unused, but useful for future; see above comment. private static native void nativeObjSort(Object array, int fromIndex, int toIndex, JavaScriptObject comp) /*-{ var n = toIndex - fromIndex; var indexArray = new Array(n); var arrayIdx = fromIndex; for ( var i = 0; i < n; ++i) { indexArray[i] = arrayIdx++; } indexArray.sort(comp); if (comp.swap) { // only reorder elements if we made a swap var temp = array.slice(fromIndex, toIndex); arrayIdx = fromIndex; for ( var i = 0; i < n; ++i) { array[arrayIdx++] = temp[indexArray[i] - fromIndex]; } } }-*/; /** * Performs the checks specified by the JRE docs and throws appropriate * exceptions. * * @param fromIndex beginning of the range to sort * @param toIndex past the end of the range to sort * @param length size of the array to sort * * @throws IllegalArgumentException if fromIndex > toIndex * @throws ArrayIndexOutOfBoundsException if fromIndex < 0 or toIndex > length */ private static void verifySortIndices(int fromIndex, int toIndex, int length) { if (fromIndex > toIndex) { throw new IllegalArgumentException("fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")"); } if (fromIndex < 0 || toIndex > length) { throw new ArrayIndexOutOfBoundsException("fromIndex(" + fromIndex + ") or toIndex(" + toIndex + ") out of bounds (0 - " + length + ")"); } } }