blob: 134dc4a2a5e768e7ace6a4d9aca1c32766ca645a [file] [log] [blame]
/*
* 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 + ")");
}
}
}