SubClassMap.java Source code

Java tutorial

Introduction

Here is the source code for SubClassMap.java

Source

/*
 * 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&lt;=idx&lt;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&lt;=idx&lt;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);
    }
}