A map of values by class.
/*
* SubclassMap.java Created Jul 9, 2009 by Andrew Butler, PSL
*/
//package prisms.util;
import java.lang.reflect.Array;
/**
* A map of values by class. This differs significantly from a normal key-value
* map in that a value may be returned for a class input if an entry exists for
* one of the class's superclasses (or inherited interfaces); an entry is not
* required for the class itself.
*
* @param <C>
* The type whose subtypes are used as keys in this map
* @param <V>
* The type of value stored in this map
*/
public class SubClassMap<C, V> {
class MapItem {
final Class<? extends C> theClass;
V theValue;
MapItem(Class<? extends C> clazz, V value) {
theClass = clazz;
theValue = value;
}
}
private MapItem[] theItems;
/**
* Creates a SubClassMap
*/
public SubClassMap() {
theItems = new SubClassMap.MapItem[0];
}
/**
* Adds or sets a value in this map
*
* @param clazz
* The class to set the value for. After this, calls to
* {@link #get(Class)} with an argument that is
* <code>clazz</code> or one of its subclasses will return
* <code>value</code> unless there is another entry in the map
* for a subtype of <code>clazz</code> that is also a supertype
* of the argument to get(Class).
* @param value
* The value to store in the map
*/
public void put(Class<? extends C> clazz, V value) {
for (MapItem item : theItems)
if (item.theClass == clazz) {
item.theValue = value;
return;
}
theItems = ArrayUtils.add(theItems, new MapItem(clazz, value));
}
/**
* Removes a value from this map
*
* @param clazz
* The class to remove the value for. After this. calls to
* {@link #get(Class)} with an argument that is
* <code>clazz</code> or one of its subclasses will return the
* value associated with <code>clazz</code>'s nearest superclass
* unless there is another entry in the map for a subtype of
* <code>clazz</code> that is also a supertype of the argument to
* get(Class).
* @param allDescending
* Whether all subclasses of <code>clazz</code> should be removed
* as well
*/
public void remove(Class<? extends C> clazz, boolean allDescending) {
for (int i = 0; i < theItems.length; i++) {
if (theItems[i].theClass == clazz
|| (allDescending && clazz
.isAssignableFrom(theItems[i].theClass)))
theItems = ArrayUtils.remove(theItems, i);
}
}
/**
* Gets the value assigned to <code>subClass</code> or its nearest supertype
* for which there is an entry in this map.
*
* @param subClass
* The type to get the value for
* @return The value associated with the given type or its nearest
* superclass for which this map has an entry
*/
public V get(Class<? extends C> subClass) {
MapItem[] matches = new SubClassMap.MapItem[0];
for (MapItem item : theItems) {
if (item.theClass == subClass)
return item.theValue;
else if (item.theClass.isAssignableFrom(subClass))
matches = ArrayUtils.add(matches, item);
}
if (matches.length == 0)
return null;
int minDist = 0;
MapItem match = null;
for (MapItem item : matches) {
int dist = getTypeDistance(item.theClass, subClass);
if (match == null || dist < minDist) {
minDist = dist;
match = item;
}
}
return match.theValue;
}
private static int getTypeDistance(Class<?> clazz, Class<?> subClass) {
if (clazz == subClass)
return 0;
int dist = getTypeDistance(clazz, subClass.getSuperclass());
if (dist >= 0)
return dist + 1;
for (Class<?> intf : subClass.getInterfaces()) {
dist = getTypeDistance(clazz, intf);
if (dist >= 0)
return dist + 1;
}
return -1;
}
}
/**
* ArrayUtils provides some static methods for manipulating arrays easily when
* using a tool such as {@link java.util.ArrayList} is inconvenient.
*/
final class ArrayUtils {
/**
* Gets the first element in the given array for which the
* {@link EqualsChecker#equals(Object, Object)} returns true, or null if
* this never occurs. The equals method is called with the element as the
* first argument and the test parameter as the second.
*
* @param <T>
* The type of array to get an element of
* @param array
* The array to get the element of
* @param test
* The object to compare the others with. This may be null.
* @param checker
* The checker to determine which element to get
* @return The first element in the array for which the
* {@link EqualsChecker#equals(Object, Object)} returns true, or
* null if this never occurs
*/
public static <T> T get(T[] array, Object test, EqualsChecker checker) {
for (T el : array)
if (checker.equals(el, test))
return el;
return null;
}
/**
* Inserts an element into the array--for primitive types
*
* @param anArray
* The array to insert into
* @param anElement
* The element to insert
* @param anIndex
* The index for the new element
* @return The new array with all elements of <code>anArray</code>, but with
* <code>anElement</code> inserted at index <code>anIndex</code>
*/
public static Object addP(Object anArray, Object anElement, int anIndex) {
Object ret;
int length;
if (anArray == null) {
if (anIndex != 0)
throw new ArrayIndexOutOfBoundsException("Cannot set "
+ anIndex + " element in a null array");
ret = Array.newInstance(anElement.getClass(), 1);
Array.set(ret, 0, anElement);
return ret;
} else {
length = Array.getLength(anArray);
ret = Array.newInstance(anArray.getClass().getComponentType(),
length + 1);
}
System.arraycopy(anArray, 0, ret, 0, anIndex);
put(ret, anElement, anIndex);
System.arraycopy(anArray, anIndex, ret, anIndex + 1, length - anIndex);
return ret;
}
/**
* Inserts an element into the array
*
* @param <T>
* The type of the object array
* @param anArray
* The array to insert into
* @param anElement
* The element to insert
* @param anIndex
* The index for the new element
* @return The new array with all elements of <code>anArray</code>, but with
* <code>anElement</code> inserted at index <code>anIndex</code>
*/
public static <T> T[] add(T[] anArray, T anElement, int anIndex) {
T[] ret;
if (anArray == null) {
if (anIndex != 0)
throw new ArrayIndexOutOfBoundsException("Cannot set "
+ anIndex + " element in a null array");
ret = (T[]) Array.newInstance(anElement.getClass(), 1);
ret[0] = anElement;
return ret;
} else
ret = (T[]) Array.newInstance(
anArray.getClass().getComponentType(), anArray.length + 1);
System.arraycopy(anArray, 0, ret, 0, anIndex);
put(ret, anElement, anIndex);
System.arraycopy(anArray, anIndex, ret, anIndex + 1, anArray.length
- anIndex);
return ret;
}
/**
* @param anArray
* The array to extend
* @param anElement
* The element to add into <code>anArray</code>
* @return A new array of length <code>anArray.length+1</code> whose
* contents are those of <code>anArray</code> followed by
* <code>anElement</code>
*/
public static Object addP(Object anArray, Object anElement) {
int len = anArray == null ? 0 : Array.getLength(anArray);
return addP(anArray, anElement, len);
}
/**
* @param <T>
* The type of the array
* @param anArray
* The array to extend
* @param anElement
* The element to add into <code>anArray</code>
* @return A new array of length <code>anArray.length+1</code> whose
* contents are those of <code>anArray</code> followed by
* <code>anElement</code>
*/
public static <T> T[] add(T[] anArray, T anElement) {
int len = anArray == null ? 0 : Array.getLength(anArray);
return add(anArray, anElement, len);
}
/**
* Moves an element in an array from one index to another
*
* @param <T>
* The type of the array
* @param anArray
* The array to move an element within
* @param from
* The index of the element to move
* @param to
* The index to move the element to
* @return The original array
*/
public static <T> T[] move(T[] anArray, int from, int to) {
final T element = anArray[from];
for (; from < to; from++)
anArray[from] = anArray[from + 1];
for (; from > to; from--)
anArray[from] = anArray[from - 1];
anArray[to] = element;
return anArray;
}
/**
* Moves an element in a primitive array from one index to another
*
* @param anArray
* The array to move an element within
* @param from
* The index of the element to move
* @param to
* The index to move the element to
* @return The original array
*/
public static Object moveP(Object anArray, int from, int to) {
if (anArray instanceof Object[])
return move((Object[]) anArray, from, to);
final Object element = Array.get(anArray, from);
for (; from < to; from++)
Array.set(anArray, from, Array.get(anArray, from + 1));
for (; from > to; from--)
Array.set(anArray, from, Array.get(anArray, from - 1));
Array.set(anArray, to, element);
return anArray;
}
private static void put(Object array, Object element, int index) {
try {
if (array instanceof Object[]) {
try {
((Object[]) array)[index] = element;
} catch (ArrayStoreException e) {
throw new IllegalArgumentException(e.getMessage()
+ ": "
+ (element == null ? "null" : element.getClass()
.getName()) + " into "
+ array.getClass().getName());
}
} else {
try {
Array.set(array, index, element);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException(e.getMessage()
+ ": "
+ (element == null ? "null" : element.getClass()
.getName()) + " into "
+ array.getClass().getName(), e);
}
}
} catch (ArrayIndexOutOfBoundsException e) {
throw new ArrayIndexOutOfBoundsException(index + " into "
+ Array.getLength(array));
}
}
/**
* Merges all elements of a set of arrays into a single array with no
* duplicates. For primitive types.
*
* @param type
* The type of the result
* @param arrays
* The arrays to merge
* @return A new array containing all elements of <code>array1</code> and
* all elements of <code>array2</code> that are not present in
* <code>array1</code>
* @throws NullPointerException
* If either array is null
* @throws ArrayStoreException
* If elements in the arrays are incompatible with
* <code>type</code>
*/
public static Object mergeInclusiveP(Class<?> type, Object... arrays) {
java.util.LinkedHashSet<Object> set = new java.util.LinkedHashSet<Object>();
int i, j;
for (i = 0; i < arrays.length; i++) {
int len = Array.getLength(arrays[i]);
for (j = 0; j < len; j++)
set.add(Array.get(arrays[i], j));
}
Object ret = Array.newInstance(type, set.size());
i = 0;
for (Object el : set) {
put(ret, el, i);
i++;
}
return ret;
}
/**
* Merges all elements of a set of arrays into a single array with no
* duplicates.
*
* @param <T1>
* The type of the result
* @param <T2>
* The type of the input arrays
* @param type
* The type of the result
* @param arrays
* The arrays to merge
* @return A new array containing all elements of <code>array1</code> and
* all elements of <code>array2</code> that are not present in
* <code>array1</code>
* @throws NullPointerException
* If either array is null
*/
public static <T1, T2 extends T1> T1[] mergeInclusive(Class<T1> type,
T2[]... arrays) {
java.util.LinkedHashSet<T1> set = new java.util.LinkedHashSet<T1>();
int i, j;
for (i = 0; i < arrays.length; i++) {
for (j = 0; j < arrays[i].length; j++)
set.add(arrays[i][j]);
}
return set.toArray((T1[]) Array.newInstance(type, set.size()));
}
/**
* Merges elements found in each of a set of arrays into a single array with
* no duplicates. For primitive types.
*
* @param type
* The type of the result
* @param arrays
* The arrays to merge
* @return A new array containing all common elements between
* <code>array1</code> and <code>array2</code>
* @throws NullPointerException
* If either array is null
* @throws ArrayStoreException
* If elements in the arrays are incompatible with
* <code>type</code>
*/
public static Object mergeExclusiveP(Class<?> type, Object... arrays) {
if (arrays.length == 0)
return Array.newInstance(type, 0);
java.util.ArrayList<Object> retSet = new java.util.ArrayList<Object>();
int i, j, k;
int len = Array.getLength(arrays[0]);
for (j = 0; j < len; j++)
retSet.add(Array.get(arrays[0], j));
for (i = 1; i < arrays.length; i++) {
for (j = 0; j < retSet.size(); j++) {
len = Array.getLength(arrays[i]);
boolean hasEl = false;
for (k = 0; k < len; k++)
if (equalsUnordered(retSet.get(j), Array.get(arrays[i], k))) {
hasEl = true;
break;
}
if (!hasEl) {
retSet.remove(j);
j--;
}
}
}
Object ret = Array.newInstance(type, retSet.size());
for (i = 0; i < retSet.size(); i++)
Array.set(ret, i, retSet.get(i));
return ret;
}
/**
* Merges elements found in both of two arrays into a single array with no
* duplicates.
*
* @param <T1>
* The type of the result
* @param <T2>
* The type of the input arrays
* @param type
* The type of the result
* @param arrays
* The arrays to merge
* @return A new array containing all common elements between
* <code>array1</code> and <code>array2</code>
* @throws NullPointerException
* If either array is null
*/
public static <T1, T2 extends T1> T1[] mergeExclusive(Class<T1> type,
T2[]... arrays) {
if (arrays.length == 0)
return (T1[]) Array.newInstance(type, 0);
java.util.ArrayList<Object> retSet = new java.util.ArrayList<Object>();
int i, j, k;
for (j = 0; j < arrays[0].length; j++)
retSet.add(arrays[0][j]);
for (i = 1; i < arrays.length; i++) {
for (j = 0; j < retSet.size(); j++) {
boolean hasEl = false;
for (k = 0; k < arrays[i].length; k++)
if (equalsUnordered(retSet.get(j), arrays[i][k])) {
hasEl = true;
break;
}
if (!hasEl) {
retSet.remove(j);
j--;
}
}
}
return retSet.toArray((T1[]) Array.newInstance(type, retSet.size()));
}
/**
* Concatenates a series of arrays of any type
*
* @param type
* The type of the arrays
* @param arrays
* The arrays to concatenate
* @return An array containing all elements of the arrays
*/
public static Object concatP(Class<?> type, Object... arrays) {
if (arrays.length == 0)
return Array.newInstance(type, 0);
if (arrays.length == 1)
return arrays[0];
int size = 0;
int[] sizes = new int[arrays.length];
for (int a = 0; a < arrays.length; a++) {
sizes[a] = Array.getLength(arrays[a]);
size += sizes[a];
}
Object ret = Array.newInstance(type, size);
int aLen = 0;
for (int a = 0; a < arrays.length; a++) {
System.arraycopy(arrays[a], 0, ret, aLen, sizes[a]);
aLen += sizes[a];
}
return ret;
}
/**
* Concatenates a series of arrays of a non-primitive type
*
* @param <T>
* The type of the arrays
* @param type
* The type of the arrays
* @param arrays
* The arrays to concatenate
* @return An array containing all elements of the arrays
*/
public static <T> T[] concat(Class<T> type, T[]... arrays) {
if (arrays.length == 0)
return (T[]) Array.newInstance(type, 0);
if (arrays.length == 1)
return arrays[0];
int size = 0;
for (int a = 0; a < arrays.length; a++) {
if (arrays[a] != null)
size += arrays[a].length;
}
T[] ret = (T[]) Array.newInstance(type, size);
int aLen = 0;
for (int a = 0; a < arrays.length; a++) {
if (arrays[a] == null)
continue;
System.arraycopy(arrays[a], 0, ret, aLen, arrays[a].length);
aLen += arrays[a].length;
}
return ret;
}
/**
* @param <T>
* The type of the array to search
* @param anArray
* The array to search
* @param anElement
* The element to search for
* @return The first index <code>0<=idx<anArray.length</code> such
* that {@link #equals(Object, Object)} returns true for both
* <code>anArray[idx]</code> and <code>anElement</code>, or -1 if no
* such index exists
*/
public static <T> int indexOf(T[] anArray, T anElement) {
if (anArray == null)
return -1;
for (int i = 0; i < anArray.length; i++)
if (equals(anArray[i], anElement))
return i;
return -1;
}
/**
* @param anArray
* The array to search
* @param anElement
* The element to search for
* @return The first index <code>0<=idx<anArray.length</code> such
* that {@link #equals(Object, Object)} returns true for both
* <code>anArray[idx]</code> and <code>anElement</code>, or -1 if no
* such index exists
*/
public static int indexOfP(Object anArray, Object anElement) {
if (anArray == null)
return -1;
if (anArray instanceof Object[]) {
Object[] array2 = (Object[]) anArray;
for (int i = 0; i < array2.length; i++) {
if (equals(array2[i], anElement))
return i;
}
return -1;
} else {
int i, len;
len = Array.getLength(anArray);
for (i = 0; i < len; i++) {
if (equals(Array.get(anArray, i), anElement))
return i;
}
return -1;
}
}
/**
* Removes the specified object from the array. For primitive types.
*
* @param anArray
* The array to remove an element from
* @param anIndex
* The index of the element to remove
* @return A new array with all the elements of <code>anArray</code> except
* the element at <code>anIndex</code>
*/
public static Object removeP(Object anArray, int anIndex) {
Object ret;
int length;
if (anArray == null)
return null;
else {
length = Array.getLength(anArray);
ret = Array.newInstance(anArray.getClass().getComponentType(),
length - 1);
}
System.arraycopy(anArray, 0, ret, 0, anIndex);
System.arraycopy(anArray, anIndex + 1, ret, anIndex, length - anIndex
- 1);
return ret;
}
/**
* Removes the specified object from the array
*
* @param <T>
* The type of the array
* @param anArray
* The array to remove an element from
* @param anIndex
* The index of the element to remove
* @return A new array with all the elements of <code>anArray</code> except
* the element at <code>anIndex</code>
*/
public static <T> T[] remove(T[] anArray, int anIndex) {
T[] ret;
if (anArray == null)
return null;
else {
ret = (T[]) Array.newInstance(
anArray.getClass().getComponentType(), anArray.length - 1);
}
System.arraycopy(anArray, 0, ret, 0, anIndex);
System.arraycopy(anArray, anIndex + 1, ret, anIndex, anArray.length
- anIndex - 1);
return ret;
}
/**
* Removes the specified object from the array
*
* @param anArray
* The array to remove an element from
* @param anElement
* The element to remove
* @return A new array with all the elements of <code>anArray</code> except
* <code>anElement</code>
*/
public static Object removeP(Object anArray, Object anElement) {
int idx = indexOfP(anArray, anElement);
if (idx >= 0)
return removeP(anArray, idx);
else
return anArray;
}
/**
* Removes the specified object from the array
*
* @param <T>
* The type of the array
* @param anArray
* The array to remove an element from
* @param anElement
* The element to remove
* @return A new array with all the elements of <code>anArray</code> except
* <code>anElement</code>
*/
public static <T> T[] remove(T[] anArray, T anElement) {
int idx = indexOf(anArray, anElement);
if (idx >= 0)
return remove(anArray, idx);
else
return anArray;
}
/**
* Removes all contents of <code>array2</code> from <code>array1</code>. All
* instances of <code>array2</code> will also be removed from
* <code>array1</code>. For primitive types.
*
* @param array1
* The array to remove elements from
* @param array2
* The array containing the elements to remove; or the element to
* remove itself
* @return <code>array1</code> missing all the contents of
* <code>array2</code>
*/
public static Object removeAllP(Object array1, Object array2) {
if (array1 == null || array2 == null)
return array1;
if (!array1.getClass().isArray())
return null;
if (!array2.getClass().isArray())
array2 = new Object[] { array2 };
else
array2 = addP(array2, array2);
java.util.BitSet remove = new java.util.BitSet();
int len1 = Array.getLength(array1);
int len2 = Array.getLength(array2);
int i, j;
for (i = 0; i < len1; i++) {
for (j = 0; j < len2; j++) {
if (equals(Array.get(array1, i), Array.get(array2, j))) {
remove.set(i);
break;
}
}
}
Object ret = Array.newInstance(array1.getClass().getComponentType(),
len1 - remove.cardinality());
// This copying section might be replaced by a more efficient version
// using System.arraycopy()--this would be much faster than reflection,
// especially for large arrays needing only a few elements removed
for (i = 0, j = 0; i < len1; i++) {
if (!remove.get(i)) {
put(ret, Array.get(array1, i), j);
j++;
}
}
return ret;
}
/**
* Removes all contents of <code>array2</code> from <code>array1</code>. All
* instances of <code>array2</code> will also be removed from
* <code>array1</code>.
*
* @param <T>
* The type of the array
* @param array1
* The array to remove elements from
* @param array2
* The array containing the elements to remove; or the element to
* remove itself
* @return <code>array1</code> missing all the contents of
* <code>array2</code>
*/
public static <T> T[] removeAll(T[] array1, Object array2) {
if (array1 == null || array2 == null)
return array1;
if (!array1.getClass().isArray())
return null;
if (!array2.getClass().isArray())
array2 = new Object[] { array2 };
java.util.BitSet remove = new java.util.BitSet();
int len1 = array1.length;
int len2 = Array.getLength(array2);
int i, j;
for (i = 0; i < len1; i++) {
for (j = 0; j < len2; j++) {
if (equals(array1[i], Array.get(array2, j))) {
remove.set(i);
break;
}
}
}
T[] ret = (T[]) Array.newInstance(array1.getClass().getComponentType(),
len1 - remove.cardinality());
// This copying section might be replaced by a more efficient version
// using System.arraycopy()--this would be much faster than reflection,
// especially for large arrays needing only a few elements removed
for (i = 0, j = 0; i < len1; i++) {
if (!remove.get(i)) {
ret[j] = array1[i];
j++;
}
}
return ret;
}
/**
* Searches an array (possibly primitive) for an element
*
* @param anArray
* The array to search
* @param anElement
* The element to search for
* @return True if <code>anArray</code> contains <code>anElement</code>,
* false otherwise
*/
public static boolean containsP(Object anArray, Object anElement) {
if (anArray == null)
return false;
if (anArray instanceof Object[]) {
Object[] oa = (Object[]) anArray;
for (int i = 0; i < oa.length; i++) {
if (equals(oa[i], anElement))
return true;
}
} else {
int len = Array.getLength(anArray);
for (int i = 0; i < len; i++) {
if (equals(Array.get(anArray, i), anElement))
return true;
}
}
return false;
}
/**
* @param <T>
* The type of the array to search
* @param anArray
* The array to search
* @param anElement
* The element to search for
* @return True if <code>anArray</code> contains <code>anElement</code>,
* false otherwise
*/
public static <T> boolean contains(T[] anArray, T anElement) {
if (anArray == null)
return false;
for (int i = 0; i < anArray.length; i++) {
if (equals(anArray[i], anElement))
return true;
}
return false;
}
/**
* Like {@link #containsP(Object, Object)} but the equality test is by
* identity instead of the equals method
*
* @param anArray
* The array to search
* @param anElement
* The element to search for
* @return True if <code>anArray</code> contains <code>anElement</code> by
* identity, false otherwise
*/
public static boolean containspID(Object anArray, Object anElement) {
if (anArray == null)
return false;
if (anArray instanceof Object[]) {
Object[] oa = (Object[]) anArray;
for (int i = 0; i < oa.length; i++) {
if (oa[i] == anElement)
return true;
}
} else {
int len = Array.getLength(anArray);
for (int i = 0; i < len; i++) {
if (Array.get(anArray, i) == anElement)
return true;
}
}
return false;
}
/**
* Like {@link #contains(Object[], Object)} but the equality test is by
* identity instead of the equals method
*
* @param <T>
* The type of the array to search
* @param <E>
* The type of the element to search for
* @param anArray
* The array to search
* @param anElement
* The element to search for
* @return True if <code>anArray</code> contains <code>anElement</code> by
* identity, false otherwise
*/
public static <T, E extends T> boolean containsID(T[] anArray, E anElement) {
if (anArray == null)
return false;
for (int i = 0; i < anArray.length; i++) {
if (anArray[i] == anElement)
return true;
}
return false;
}
/**
* A utility version of equals that allows comparison of null objects and
* arrays. WARNING: Does not account for an array that contains a reference
* to itself, directly or indirectly
*
* @param o1
* The first object to compare
* @param o2
* The second object to compare
* @return true if <code>o1</code> and <code>o2</code> are arrays and their
* elements are equivalent or if either
* <code>o1==null && o2==null</code> or <code>o1.equals(o2)</code>,
* false otherwise
*/
public static boolean equals(Object o1, Object o2) {
if (o1 == null)
return o2 == null;
if (o2 == null)
return false;
if (o1 instanceof Object[] && o2 instanceof Object[])
return equals((Object[]) o1, (Object[]) o2);
if (o1.getClass().isArray() && o2.getClass().isArray()) {
if (!o1.getClass().equals(o2.getClass()))
return false;
int len = Array.getLength(o1);
if (len != Array.getLength(o2))
return false;
for (int i = 0; i < len; i++) {
if (!equals(Array.get(o1, i), Array.get(o2, i)))
return false;
}
return true;
}
return o1.equals(o2);
}
private static boolean equals(Object[] o1, Object[] o2) {
if (o1 == null)
return o2 == null;
if (o2 == null)
return false;
if (!o1.getClass().equals(o2.getClass()))
return false;
if (o1.length != o2.length)
return false;
for (int i = 0; i < o1.length; i++) {
if (!equals(o1[i], o2[i]))
return false;
}
return true;
}
/**
* Like {@link #equals(Object, Object)}, but compares arrays without regard
* to order
*
* @param o1
* The first object to compare
* @param o2
* The second object to compare
* @return true if <code>o1</code> and <code>o2</code> are arrays and their
* elements are equivalent or if either
* <code>o1==null && o2==null</code> or <code>o1.equals(o2)</code>,
* false otherwise
*/
public static boolean equalsUnordered(Object o1, Object o2) {
return equalsUnordered(o1, o2, new EqualsChecker() {
public boolean equals(Object o3, Object o4) {
if (o3 == null)
return o4 == null;
if (o4 == null)
return false;
if (!o3.getClass().isArray()) {
if (o4.getClass().isArray())
return false;
return o3.equals(o4);
} else if (!o4.getClass().isArray())
return false;
return equalsUnordered(o3, o4);
}
});
}
/**
* Checks two values for equality
*/
public static interface EqualsChecker {
/**
* Checks two values for equality
*
* @param o1
* The first value to check
* @param o2
* The second value to check
* @return Whether the two values are equal
*/
boolean equals(Object o1, Object o2);
}
/**
* Like {@link #equals(Object, Object)}, but compares arrays without regard
* to order and uses an independent checker to check for equality of
* elements
*
* @param o1
* The first object to compare
* @param o2
* The second object to compare
* @param checker
* The checker to check elements for equality
* @return true if <code>o1</code> and <code>o2</code> are arrays and their
* elements are equivalent or if either
* <code>o1==null && o2==null</code> or <code>o1.equals(o2)</code>,
* false otherwise
*/
public static boolean equalsUnordered(Object o1, Object o2,
EqualsChecker checker) {
if (o1 == null)
return o2 == null;
if (o2 == null)
return false;
if (o1 instanceof Object[] && o2 instanceof Object[])
return equalsUnordered((Object[]) o1, (Object[]) o2, checker);
if (o1.getClass().isArray() && o2.getClass().isArray()) {
if (!o1.getClass().equals(o2.getClass()))
return false;
int len = Array.getLength(o1);
if (len != Array.getLength(o2))
return false;
if (len == 0)
return true;
long[] bs = new long[(len - 1) / 64 + 1];
long mask;
int i, j, bsIdx;
o1Loop: for (i = 0; i < len; i++) {
mask = 0;
for (j = 0; j < len; j++) {
bsIdx = j / 64;
if (mask == 0)
mask = 0x8000000000000000L;
if ((bs[bsIdx] & mask) == 0
&& checker.equals(Array.get(o1, i),
Array.get(o2, j))) {
bs[bsIdx] |= mask;
continue o1Loop;
}
mask >>>= 1;
}
return false;
}
return true;
}
return checker.equals(o1, o2);
}
private static boolean equalsUnordered(Object[] o1, Object[] o2,
EqualsChecker checker) {
if (o1 == null)
return o2 == null;
if (o2 == null)
return false;
if (o1.length != o2.length)
return false;
if (o1.length == 0)
return true;
long[] bs = new long[(o2.length - 1) / 64 + 1];
long mask;
int i, j, bsIdx;
o1Loop: for (i = 0; i < o1.length; i++) {
mask = 0;
for (j = 0; j < o2.length; j++) {
bsIdx = j / 64;
if (mask == 0)
mask = 0x8000000000000000L;
if ((bs[bsIdx] & mask) == 0 && checker.equals(o1[i], o2[j])) {
bs[bsIdx] |= mask;
continue o1Loop;
}
mask >>>= 1;
}
return false;
}
return true;
}
/**
* A compliment to the {@link #equals(Object, Object)} method, this method
* returns a hashcode for <code>array</code> such that it is consistent with
* the equals contract for the {@link Object#equals(Object)} method,
* represented by the {@link #equals(Object, Object)} method.
*
* @param array
* The array to get a hashcode for
* @return A hashcode based on <code>array</code> and its contents, if any
*/
public static int hashCode(Object array) {
if (array == null)
return 0;
if (!array.getClass().isArray())
return array.hashCode();
int ret = array.getClass().getComponentType().hashCode();
int len = Array.getLength(array);
for (int i = 0; i < len; i++) {
ret *= hashCode(Array.get(array, i));
ret += 29;
}
return ret;
}
/**
* A utility method for printing out the contents of an array (not deeply)
*
* @param array
* The array to print
* @return A String containing the representation of the contents of
* <code>array</code>
*/
public static String toString(Object array) {
if (array == null)
return "" + null;
else if (array instanceof Object[]) {
Object[] oa = (Object[]) array;
StringBuffer ret = new StringBuffer("[");
for (int i = 0; i < oa.length; i++) {
ret.append(toString(oa[i]));
if (i < oa.length - 1)
ret.append(", ");
}
ret.append("]");
return ret.toString();
} else if (array.getClass().isArray()) {
StringBuffer ret = new StringBuffer("[");
int len = Array.getLength(array);
for (int i = 0; i < len; i++) {
ret.append(Array.get(array, i));
if (i < len - 1)
ret.append(", ");
}
ret.append("]");
return ret.toString();
} else
return array.toString();
}
/**
* Replaces <code>toReplace</code> with <code>replacement</code> in
* <code>array</code> one time at the most
*
* @param array
* The array to search and replace in
* @param toReplace
* The object to replace
* @param replacement
* The object to replace <code>toReplace</code> with the first
* time it is found
* @return The index where <code>toReplace</code> was found and replaced, or
* -1 if it was not found in <code>array</code>
*/
public static int replaceOnce(Object array, Object toReplace,
Object replacement) {
if (array == null)
return -1;
if (array instanceof Object[]) {
Object[] array2 = (Object[]) array;
for (int i = 0; i < array2.length; i++) {
if (equals(array2[i], toReplace)) {
array2[i] = replacement;
return i;
}
}
return -1;
} else {
int i, len;
len = Array.getLength(array);
for (i = 0; i < len; i++) {
if (equals(Array.get(array, i), toReplace)) {
put(array, replacement, i);
return i;
}
}
return -1;
}
}
/**
* Replaces <code>toReplace</code> with <code>replacement</code> in
* <code>array</code> as many times as it occurs
*
* @param array
* The array to search and replace in
* @param toReplace
* The object to replace
* @param replacement
* The object to replace <code>toReplace</code> with each time it
* is found
* @return The number of times <code>toReplace</code> was found and replaced
*/
public static int replaceAll(Object array, Object toReplace,
Object replacement) {
int count = 0;
if (array == null)
return count;
if (array instanceof Object[]) {
Object[] array2 = (Object[]) array;
for (int i = 0; i < array2.length; i++) {
if (equals(array2[i], toReplace)) {
array2[i] = replacement;
count++;
}
}
} else {
int i, len;
len = Array.getLength(array);
for (i = 0; i < len; i++) {
if (equals(Array.get(array, i), toReplace)) {
put(array, replacement, i);
count++;
}
}
}
return count;
}
/**
* Gets the elements that the two arrays have in common. Elements are
* retrieved from the first array.
*
* @param <T>
* The type of the first array and the return array
* @param <T2>
* The type of the second array
* @param array1
* The first array
* @param array2
* The second array
* @return The elements in <code>array1</code> that occur at least once in
* <code>array2</code>
*/
public static <T, T2 extends T> T[] commonElements(T[] array1, T2[] array2) {
int count = 0;
if (array1 == null || array2 == null)
return (T[]) Array.newInstance(
array1.getClass().getComponentType(), 0);
int i, j;
for (i = 0; i < array1.length; i++)
for (j = 0; j < array2.length; j++)
if (equals(array1[i], array2[j])) {
count++;
break;
}
T[] ret = (T[]) Array.newInstance(array1.getClass().getComponentType(),
count);
count = 0;
for (i = 0; i < array1.length; i++)
for (j = 0; j < array2.length; j++)
if (equals(array1[i], array2[j])) {
ret[count] = array1[i];
count++;
break;
}
return ret;
}
/**
* Gets the elements that the two arrays have in common. Elements are
* retrieved from the first array.
*
* @param array1
* The first array
* @param array2
* The second array
* @return The elements in <code>array1</code> that occur at least once in
* <code>array2</code>
*/
public static Object[] commonElementsP(Object array1, Object array2) {
int count = 0;
if (array1 == null || array2 == null)
return new Object[0];
if (!array1.getClass().isArray() || array2.getClass().isArray())
return new Object[0];
int len1 = Array.getLength(array1);
int len2 = Array.getLength(array2);
int i, j;
for (i = 0; i < len1; i++)
for (j = 0; j < len2; j++)
if (equals(Array.get(array1, i), Array.get(array2, j))) {
count++;
break;
}
Object[] ret = new Object[count];
count = 0;
for (i = 0; i < len1; i++)
for (j = 0; j < len2; j++)
if (equals(Array.get(array1, i), Array.get(array2, j))) {
ret[count] = Array.get(array1, i);
count++;
break;
}
return ret;
}
/**
* Gets the elements that are in array1, but not array 2.
*
* @param <T>
* The type of the arrays
* @param array1
* The first array
* @param array2
* The second array
* @return The elements in <code>array1</code> that do not occur in
* <code>array2</code>
*/
public static <T> T[] removedElements(T[] array1, T[] array2) {
int count = 0;
if (array1 == null || array2 == null)
return array1;
int len1 = array1.length;
int len2 = array2.length;
int i, j;
for (i = 0; i < len1; i++) {
count++;
for (j = 0; j < len2; j++) {
if (equals(array1[i], array2[j])) {
count--;
break;
}
}
}
T[] ret = (T[]) Array.newInstance(array1.getClass().getComponentType(),
count);
count = 0;
for (i = 0; i < len1; i++) {
count++;
for (j = 0; j < len2; j++) {
if (equals(array1[i], array2[j])) {
count--;
break;
}
ret[count] = array1[i];
}
}
return ret;
}
/**
* Gets the elements that are in array1, but not array 2.
*
* @param array1
* The first array
* @param array2
* The second array
* @return The elements in <code>array1</code> that do not occur in
* <code>array2</code>
*/
public static Object[] removedElementsP(Object array1, Object array2) {
int count = 0;
if (array1 == null || array2 == null)
return new Object[0];
if (!array1.getClass().isArray() || array2.getClass().isArray())
return new Object[0];
int len1 = Array.getLength(array1);
int len2 = Array.getLength(array2);
int i, j;
for (i = 0; i < len1; i++) {
count++;
for (j = 0; j < len2; j++) {
if (equals(Array.get(array1, i), Array.get(array2, j))) {
count--;
break;
}
}
}
Object[] ret = new Object[count];
count = 0;
for (i = 0; i < len1; i++) {
count++;
for (j = 0; j < len2; j++) {
if (equals(Array.get(array1, i), Array.get(array2, j))) {
count--;
break;
}
ret[count] = Array.get(array1, i);
}
}
return ret;
}
/**
* Gets the elements that are in array2, but not array 1.
*
* @param array1
* The first array
* @param array2
* The second array
* @return The elements in <code>array2</code> that do not occur in
* <code>array1</code>
*/
public static Object[] addedElements(Object array1, Object array2) {
int count = 0;
if (array1 == null || array2 == null)
return new Object[0];
if (!array1.getClass().isArray() || array2.getClass().isArray())
return new Object[0];
int len1 = Array.getLength(array1);
int len2 = Array.getLength(array2);
int i, j;
for (i = 0; i < len1; i++) {
count++;
for (j = 0; j < len2; j++) {
if (equals(Array.get(array2, i), Array.get(array1, j))) {
count--;
break;
}
}
}
Object[] ret = new Object[count];
count = 0;
for (i = 0; i < len1; i++) {
count++;
for (j = 0; j < len2; j++) {
if (equals(Array.get(array2, i), Array.get(array1, j))) {
count--;
break;
}
ret[count] = Array.get(array2, i);
}
}
return ret;
}
/**
* Reverses the order of an array
*
* @param <T>
* The type of the array to reverse
* @param array
* The array to reverse
* @return The reversed array, same as the original reference
*/
public static <T> T[] reverse(T[] array) {
if (array == null)
return array;
T temp;
final int aLen = array.length - 1;
for (int i = 0; i < array.length / 2; i++) {
temp = array[i];
array[i] = array[aLen - i];
array[aLen - i] = temp;
}
return array;
}
/**
* Reverses the order of an array. Similar to {@link #reverse(Object[])} but
* works for primitive arrays as well.
*
* @param array
* The array to reverse
* @return The reversed array, same as the original reference
*/
public static Object reverseP(Object array) {
if (array == null)
return array;
if (array instanceof Object[])
return reverse((Object[]) array);
Object temp;
final int aLen = Array.getLength(array);
for (int i = 0; i < aLen / 2; i++) {
temp = Array.get(array, i);
put(array, Array.get(array, aLen - i - 1), i);
put(array, temp, aLen - i - 1);
}
return array;
}
/**
* @see DifferenceListenerE
*
* @param <T1>
* The type of the original array
* @param <T2>
* The type of the modifying array
*/
public static interface DifferenceListener<T1, T2> extends
DifferenceListenerE<T1, T2, RuntimeException> {
/**
* @see prisms.util.ArrayUtils.DifferenceListenerE#identity(java.lang.Object,
* java.lang.Object)
*/
boolean identity(T1 o1, T2 o2);
/**
* @see prisms.util.ArrayUtils.DifferenceListenerE#added(java.lang.Object,
* int, int)
*/
T1 added(T2 o, int mIdx, int retIdx);
/**
* @see prisms.util.ArrayUtils.DifferenceListenerE#removed(java.lang.Object,
* int, int, int)
*/
T1 removed(T1 o, int oIdx, int incMod, int retIdx);
/**
* @see prisms.util.ArrayUtils.DifferenceListenerE#set(java.lang.Object,
* int, int, java.lang.Object, int, int)
*/
T1 set(T1 o1, int idx1, int incMod, T2 o2, int idx2, int retIdx);
}
/**
* <p>
* This listener contains all information needed to reconcile two arrays
* using the
* {@link ArrayUtils#adjust(Object[], Object[], prisms.util.ArrayUtils.DifferenceListenerE)}
* method.
* </p>
*
* <p>
* There are many different indices that may or may not be relevant to a
* particular listener:
* <ul>
* <li><b>oIdx:</b> The index of the element in the original array</li>
* <li><b>incMod:</b> The index of the element in a copy of the original
* array that has been modified incrementally for each add, remove, and
* reorder operation</li>
* <li><b>mIdx:</b> The index of the element in the modifier array</li>
* <li><b>retIdx:</b> The index that the returned element (if not null) will
* be at in the result array</li>
* </ul>
* oIdx and mIdx are self-explanatory. incMod and retIdx are useful for
* listeners that modify an external ordered set incrementally with each
* listener method invocation.
* </p>
*
* <p>
* <b>An Example</b><br />
* Suppose there is a list, <b>L</b>, whose data can only be modified
* incrementally by the methods add(value, index), remove(index), and
* move(fromIndex, toIndex), but can be accessed in batch by the getData
* method, which returns an array of all data in the list. Suppose the list
* needs to be modified to represent a new arbitrary set of data, <b>d</b>.
* <b>d</b> may or may not contain elements represented in <b>L</b>. These
* elements, if present, may be in different order and new items not present
* in <b>L</b> may be present in <b>d</b>. To modify <b>L</b> to represent
* the data in <b>d</b> in the correct order, the
* {@link ArrayUtils#adjust(Object[], Object[], DifferenceListenerE)} method
* should be called with <b>L</b>.getData(), <b>d</b>, and a listener whose
* methods perform the following operations:
* <ul>
* <li>{@link #identity(Object, Object)}: Tests the two elements to see if
* the item from <b>L</b> represents the item in <b>d</b>, possibly by
* simply calling {@link Object#equals(Object)}</li>
* <li>{@link #added(Object, int, int)}: Invokes <b>L</b>.add(o2, retIdx).</li>
* <li>{@link #removed(Object, int, int, int)}: Invokes
* <b>L</b>.remove(incMod). Using incMod accounts for any previous
* modifications that may have been performed on the list from the listener.
* </li>
* <li>{@link #set(Object, int, int, Object, int, int)}: Invokes
* <b>L</b>.move(incMod, retIdx). This again accounts for all previous
* changes to the list and moves the element to the index intended by the
* adjust method.</li>
* </ul>
* The end result of this invocation will be that <b>L</b> contains all the
* data in <b>d</b>, only the data in <b>d</b>, and is ordered identically
* to <b>d</b>. The returned array may be ignored.
* </p>
*
* <p>
* <b>Alternate Operation:</b><br />
* <ul>
* <li>If it is intended that the modification operation should only add
* data to <b>L</b>, the remove method may do nothing and return o. The
* return value will cause the adjust method to update future indexes,
* assuming that the value is kept and not removed.</li>
* <li>If order is not important or no move(fromIndex, toIndex) method
* exists, the set method may do nothing and return o1. This will have no
* effect on the result of the operation other than leaving the items
* originally in <b>L</b> (those that were also present in <b>d</b>) in
* their original order.</li>
* <li>Other permutations may exist depending on the business logic used by
* the listener.</li>
* </ul>
* </p>
*
* <p>
* The listener is entirely free to choose whether items are added or
* removed from the original array and its representations by modifying
* whether the return value from those methods is null. The adjust method
* does not care what is returned from the add/remove/set methods except
* whether the value is null. The listener is free to perform index moving
* operations or not. This method will never cause an index out of bounds
* error assuming the related data sets are not modified by anything other
* than the listener and the data in the arrays passed into the adjust
* method do not change.
* </p>
*
* @param <T1>
* The type of the original array
* @param <T2>
* The type of the modifying array
* @param <E>
* The type of exception that may be thrown
*/
public static interface DifferenceListenerE<T1, T2, E extends Throwable> {
/**
* Tests the identity of an item from each of the two sets. This method
* should return true if one item is a representation of the other or
* both are a representation of the same piece of data.
*
* @param o1
* The object from the first set
* @param o2
* The object from the second set
* @return Whether the two objects are fundamentally equivalent
* @throws E
* If an error occurs in this method
*/
boolean identity(T1 o1, T2 o2) throws E;
/**
* Called when a value is found in the second set that is not present in
* the first set
*
* @param o
* The unmatched object
* @param mIdx
* The index in the second array where the unmatched object
* was found
* @param retIdx
* The index that the new element will be inserted into the
* final array
* @return The new T1-type element to insert into the return array, or
* null if the new element is not to be inserted
* @throws E
* If an error occurs in this method
*/
T1 added(T2 o, int mIdx, int retIdx) throws E;
/**
* Called when a value is found in the first set that is not present in
* the second set
*
* @param o
* The unmatched object
* @param oIdx
* The index in the first array where the unmatched object
* was found
* @param incMod
* The index where the unmatched element would occur the
* incrementally modified original array
* @param retIdx
* The index in the final array where the replacement will be
* located if a non-null value is returned
* @return null if the original object is to be removed, otherwise its
* replacement
* @throws E
* If an error occurs in this method
*/
T1 removed(T1 o, int oIdx, int incMod, int retIdx) throws E;
/**
* Called when elements in the two arrays match
*
* @param o1
* The element in the first array
* @param idx1
* The index of the element in the first array
* @param incMod
* The index where the element in the original array would
* occur the incrementally modified original array
* @param o2
* The element in the second array
* @param idx2
* The index of the element in the second array
* @param retIdx
* The index of the returned element in the final array
* @return The element to be inserted in the final array, or null if the
* element is to be removed
* @throws E
* If an error occurs in this method
*/
T1 set(T1 o1, int idx1, int incMod, T2 o2, int idx2, int retIdx)
throws E;
}
/**
* <p>
* Reconciles differences between two ordered sets of objects. Allows a
* programmer highly customized control between two different
* representations of a data set. The arrays are compared and the listener
* is notified when any differences are encountered, allowing it to
* determine the composition of the returned array and/or perform
* well-defined operations represented by the differences or similarities.
* </p>
*
* @param <T1>
* The type of the original array
* @param <T2>
* The type of the modifying array
* @param <E>
* The type of exception that may be thrown
* @param original
* The original array
* @param modifier
* The modifying array
* @param dl
* The listener to determine how to deal with differences between
* the two arrays
* @return A final array that is the result of applying select changes
* between the original and the modifying arrays
* @throws E
* If the {@link DifferenceListenerE} throws an exception
*/
public static <T1, T2, E extends Throwable> T1[] adjust(T1[] original,
T2[] modifier, DifferenceListenerE<T1, T2, E> dl) throws E {
ArrayAdjuster<T1, T2, E> adjuster = new ArrayAdjuster<T1, T2, E>(
original, modifier, dl);
return adjuster.adjust();
}
static final Object NULL = new Object();
/**
* Adjusts arrays. This is the more complicated and capable structure used
* by {@link ArrayUtils#adjust(Object[], Object[], DifferenceListenerE)}
*
* @param <T1>
* The type of the original array
* @param <T2>
* The type of the modifying array
* @param <E>
* The type of exception that may be thrown
*/
public static class ArrayAdjuster<T1, T2, E extends Throwable> {
private final T1[] original;
private final int[] oIdxAdj;
private final int[] oMappings;
private final T2[] modifier;
private final int[] mMappings;
private final boolean[] entriesSet;
private final DifferenceListenerE<T1, T2, E> dl;
private int maxLength;
private boolean isNullElement;
/**
* Creates an adjuster that can adjust one array by another
*
* @param o
* The original array
* @param m
* The modified array
* @param listener
* The listener to determine how to deal with differences
* between the two arrays
* @see ArrayUtils#adjust(Object[], Object[], DifferenceListenerE)
*/
public ArrayAdjuster(T1[] o, T2[] m,
DifferenceListenerE<T1, T2, E> listener) {
original = o;
oIdxAdj = new int[o.length];
oMappings = new int[o.length];
modifier = m;
mMappings = new int[m.length];
entriesSet = new boolean[m.length];
dl = listener;
}
private void init() throws E {
int o, m, r = original.length + modifier.length;
for (m = 0; m < modifier.length; m++)
mMappings[m] = -1;
for (o = 0; o < original.length; o++) {
oIdxAdj[o] = o;
oMappings[o] = -1;
for (m = 0; m < modifier.length; m++) {
if (mMappings[m] >= 0)
continue;
if (dl.identity(original[o], modifier[m])) {
oMappings[o] = m;
mMappings[m] = o;
r--;
break;
}
}
}
maxLength = r;
}
/**
* Adjusts the arrays set from the constructor
*
* @return The adjusted array
* @throws E
* If an error occurs in one of the listener's methods
* @see ArrayUtils#adjust(Object[], Object[], DifferenceListenerE)
*/
public T1[] adjust() throws E {
init();
int m, o, r = 0;
Object[] ret = new Object[maxLength];
for (o = 0; o < original.length && oMappings[o] < 0; o++)
if (remove(o, -1, ret, r))
r++;
for (m = 0; m < modifier.length; m++) {
// Add or set each modifier
o = mMappings[m];
if (o >= 0) {
if (set(o, m, ret, r))
r++;
/* Remove the originals that occur before the next match */
for (o++; o < original.length && oMappings[o] < 0; o++) {
if (ret[r] != null) {
Object temp = ret[r];
for (int r2 = r; r < ret.length - 1 && temp != null; r2++) {
Object temp2 = ret[r2 + 1];
ret[r2 + 1] = temp;
temp = temp2;
}
}
if (remove(o, m + 1, ret, r))
r++;
}
} else {
if (ret[r] != null) {
Object temp = ret[r];
for (int r2 = r; r < ret.length - 1 && temp != null; r2++) {
Object temp2 = ret[r2 + 1];
ret[r2 + 1] = temp;
temp = temp2;
}
}
if (add(m, ret, r))
r++;
}
}
for (int i = 0; i < r; i++)
if (ret[i] == NULL)
ret[i] = null;
T1[] actualRet = (T1[]) Array.newInstance(original.getClass()
.getComponentType(), r);
System.arraycopy(ret, 0, actualRet, 0, r);
return actualRet;
}
/**
* Marks the current value as a null value. If an actual null were
* returned, adjust would interpret this as meaning the element should
* be removed from the array, with subsequent indices being affected. If
* this method is called from {@link DifferenceListenerE}.add, remove,
* or set, the element's place will be saved and that element in the
* returned array will be null.
*/
public void nullElement() {
isNullElement = true;
}
static void mergeSort(int[] order, int[] distances, int start, int end) {
if (end - start <= 1)
return;
int mid = (start + end + 1) / 2;
mergeSort(order, distances, start, mid);
mergeSort(order, distances, mid, end);
while (start < mid && mid < end) {
if (distances[start] < distances[mid]) // Reverse order
{
int temp = distances[mid];
int temp2 = order[mid];
for (int i = mid; i > start; i--) {
distances[i] = distances[i - 1];
order[i] = order[i - 1];
}
distances[start] = temp;
order[start] = temp2;
mid++;
}
start++;
}
}
private boolean add(int m, Object[] ret, int r) throws E {
entriesSet[m] = true;
T1 item = dl.added(modifier[m], m, r);
if (isNullElement) {
item = (T1) NULL;
isNullElement = false;
}
// Adjust the incremental modification indexes
if (item != null) {
ret[r] = item;
for (int i = 0; i < oIdxAdj.length; i++)
if (oIdxAdj[i] >= r)
oIdxAdj[i]++;
} else {
for (; r < ret.length - 1; r++)
ret[r] = ret[r + 1];
}
return item != null;
}
private boolean remove(int o, int m, Object[] ret, int r) throws E {
T1 item = dl.removed(original[o], o, oIdxAdj[o], r);
if (isNullElement) {
item = (T1) NULL;
isNullElement = false;
}
// Adjust the incremental modification indexes
if (item == null) {
oIdxAdj[o] = -1;
for (; o < oIdxAdj.length; o++)
oIdxAdj[o]--;
} else
ret[r] = item;
return item != null;
}
private boolean set(int o, int m, Object[] ret, int r) throws E {
if (entriesSet[m])
return ret[r] != null;
if (oIdxAdj[o] > r && oIdxAdj[o] - r <= 10) {
/*
* If a few elements have been moved forward several indices, we
* want to fire a few move events on those elements rather than
* many move events for a few index differences.
*/
int o2;
for (o2 = o - 1; o2 >= 0; o2--)
if (oMappings[o2] > m)
set(o2, oMappings[o2], ret, r + oMappings[o2] - m);
}
entriesSet[m] = true;
T1 item = dl.set(original[o], o, oIdxAdj[o], modifier[m], m, r);
if (isNullElement) {
item = (T1) NULL;
isNullElement = false;
}
// Adjust the incremental modification indexes
if (item != null) {
ret[r] = item;
int oAdj = oIdxAdj[o];
if (r > oAdj) { // Element moved forward--decrement incMods up
// to the new index
for (int i = 0; i < oIdxAdj.length; i++)
if (oIdxAdj[i] >= oAdj && oIdxAdj[i] <= r)
oIdxAdj[i]--;
} else if (r < oAdj) { // Element moved backward--increment
// incMods up to the original index
for (int i = 0; i < oIdxAdj.length; i++)
if (oIdxAdj[i] >= r && oIdxAdj[i] < oAdj)
oIdxAdj[i]++;
}
} else {
oIdxAdj[o] = -1;
for (int i = 0; i < oIdxAdj.length; i++)
if (oIdxAdj[i] > r)
oIdxAdj[i]--;
for (; r < ret.length - 1; r++)
ret[r] = ret[r + 1];
}
return item != null;
}
}
/**
* A listener used for {@link #sort(Object [], SortListener)}
*
* @param <T>
* The type of object to sort
*/
public static interface SortListener<T> extends java.util.Comparator<T> {
/**
* Called just <u>after</u> two elements are swapped.
*
* @param o1
* The first element being moved
* @param idx1
* The index (in the pre-swap array) of the first element
* @param o2
* The second element being moved
* @param idx2
* The index (in the pre-swap array) of the second element
*/
void swapped(T o1, int idx1, T o2, int idx2);
}
/**
* Sorts an array, allowing for complex operations during the sort, such as
* sorting arrays in parallel.
*
* This is an implementation of selection sort. Selection sort is used
* because although it may perform many more comparison operations than
* other methods, there is no other method that performs fewer swaps. It is
* anticipated that this algorithm will be used most often in the case that
* multiple operations must take place during a swap, while comparison
* should be fairly quick.
*
* @param <T>
* The type of the array to sort
* @param array
* The array to sort
* @param listener
* The listener to use for comparisons and to notify of swapping
*/
public static <T> void sort(T[] array, SortListener<T> listener) {
for (int i = 0; i < array.length - 1; i++) {
int min = findMin(array, i, listener);
if (min != i) {
T ta = array[i];
T tb = array[min];
array[i] = tb;
array[min] = ta;
listener.swapped(ta, i, tb, min);
}
}
}
private static <T> int findMin(T[] array, int start,
SortListener<T> listener) {
int minIdx = start;
for (int i = start + 1; i < array.length; i++)
if (listener.compare(array[i], array[minIdx]) < 0)
minIdx = i;
return minIdx;
}
/**
* Represents some statistical information calculated on a set of float data
*/
public static class ArrayStatistics {
/**
* The smallest non-NaN, non-infinite datum encountered by this
* statistics set
*/
public float min;
/**
* The largest non-NaN, non-infinite datum encountered by this
* statistics set
*/
public float max;
/**
* The number of NaN data encountered by this statistics set
*/
public int naNCount;
/**
* The number of +Inf data encountered by this statistics set
*/
public int posInfCount;
/**
* The number of -Inf data encountered by this statistics set
*/
public int negInfCount;
/**
* The number of non-NaN, non-infinite data encountered by this
* statistics set
*/
public int validCount;
/**
* The mean value of all non-NaN, non-infinite data encountered by this
* statistics set
*/
public float mean;
/**
* A running data measure that allows the standard deviation of all
* non-NaN, non-infinite data encountered by this statistics set
*/
private float q;
/**
* Creates an ArrayStatistics set
*/
public ArrayStatistics() {
min = Float.MAX_VALUE;
max = Float.MIN_VALUE;
mean = 0;
q = Float.NaN;
validCount = 0;
naNCount = 0;
posInfCount = 0;
negInfCount = 0;
}
/**
* Adds a value to this statistics set
*
* @param value
* The value to analyze
*/
public void inputValue(float value) {
if (Float.isNaN(value))
naNCount++;
else if (Float.isInfinite(value)) {
if (value > 0)
posInfCount++;
else
negInfCount++;
} else if (validCount == 0) {
mean = value;
q = 0;
validCount++;
} else {
if (value < min)
min = value;
if (value > max)
max = value;
q = q + (validCount * (mean - value) * (mean - value))
/ (validCount + 1);
validCount++;
mean = mean + (value - mean) / validCount;
}
}
/**
* @return The standard deviation of all non-NaN, non-infinite data
* encountered by this statistics set
*/
public float getSigma() {
return (float) Math.sqrt(q / (validCount - 1));
}
/**
* @return The total number of data encountered by this statistics set
*/
public int getTotalCount() {
return validCount + naNCount + posInfCount + negInfCount;
}
@Override
public String toString() {
StringBuilder ret = new StringBuilder();
ret.append(getTotalCount());
ret.append(" values ranged ");
ret.append(min);
ret.append(" to ");
ret.append(max);
ret.append("; mean=");
ret.append(mean);
ret.append(", st.dev.=");
ret.append(getSigma());
ret.append("; ");
ret.append(naNCount);
ret.append("NaN, ");
ret.append(posInfCount);
ret.append("+Inf, ");
ret.append(negInfCount);
ret.append("-Inf");
return ret.toString();
}
}
/**
* Performs a simple statistical analysis of an array of floats
*
* @param array
* An n-dimensional array of floats
* @param stats
* The ArrayStatistics object to populate, or null to create a
* new one
* @return The statistics object
*/
public static ArrayStatistics getStatistics(Object array,
ArrayStatistics stats) {
if (array == null)
return null;
if (stats == null)
stats = new ArrayStatistics();
if (array instanceof Object[]) {
for (Object subArray : (Object[]) array)
getStatistics(subArray, stats);
return stats;
}
if (!(array instanceof float[]))
throw new IllegalArgumentException(
"Cannot statistically analyze a "
+ array.getClass().getName());
for (float f : (float[]) array)
stats.inputValue(f);
return stats;
}
/**
* A testing method
*
* @param args
* The command-line arguments. The first argument is used to
* determine what test to run.
*/
public static void main(String[] args) {
String test;
if (args.length == 0)
test = "intSort";
else
test = args[0];
if (test.equals("intSort")) {
int[] random = new int[10000];
int[] random2 = new int[random.length];
for (int i = 0; i < random.length; i++)
random[i] = (int) (Math.random() * Integer.MAX_VALUE);
ArrayAdjuster.mergeSort(random2, random, 0, random.length);
boolean sorted = true;
for (int i = 0; i < random.length - 1; i++)
if (random[i] < random[i + 1]) {
sorted = false;
break;
}
System.out.println("Sort " + (sorted ? "succeeded" : "failed"));
} else if (test.equals("adjust")) {
final Integer[] start = new Integer[25];
for (int i = 0; i < start.length; i++)
start[i] = new Integer(i);
Integer[] modifier = new Integer[start.length];
for (int i = 0; i < modifier.length; i++) {
if (i % 5 != 0)
modifier[i] = new Integer(i);
else
modifier[i] = new Integer(i / 5 * start.length);
}
Integer[] result = adjust(start, modifier,
new DifferenceListener<Integer, Integer>() {
public boolean identity(Integer o1, Integer o2) {
return o1.equals(o2);
}
public Integer added(Integer o, int mIdx, int retIdx) {
return o;
}
public Integer removed(Integer o, int oIdx,
int oIdxAdj, int retIdx) {
return o;
}
public Integer set(Integer o1, int idx1, int oIdxAdj,
Integer o2, int idx2, int retIdx) {
if (o1.intValue() % 5 == 2)
return null;
return o1;
}
});
System.out.println("Original array=" + toString(start));
System.out.println("Modifier array=" + toString(modifier));
System.out.println("Adjusted array=" + toString(result));
} else
throw new IllegalArgumentException("Unrecognized test: " + test);
}
}
Related examples in the same category