/******************************************************************************* * Copyright (c) 2009, 2010 Fraunhofer IWU and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Fraunhofer IWU - initial API and implementation *******************************************************************************/ package net.enilink.komma.common.util; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.lang.reflect.Array; import java.util.AbstractList; import java.util.Collection; import java.util.Collections; import java.util.ConcurrentModificationException; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.NoSuchElementException; import java.util.RandomAccess; /** * A highly extensible list implementation. */ public class ExtensibleList<E> extends AbstractList<E> implements IList<E>, RandomAccess, Cloneable, Serializable { private static final long serialVersionUID = 1L; /** * The size of the list. */ protected int size; /** * The underlying data storage of the list. */ protected transient Object[] data; /** * Creates an empty instance with no initial capacity. The data storage will * be null. */ public ExtensibleList() { super(); } /** * Creates an empty instance with the given capacity. * * @param initialCapacity * the initial capacity of the list before it must grow. * @exception IllegalArgumentException * if the <code>initialCapacity</code> is negative. */ public ExtensibleList(int initialCapacity) { if (initialCapacity < 0) { throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity); } data = newData(initialCapacity); } /** * Creates an instance that is a copy of the collection. * * @param collection * the initial contents of the list. */ public ExtensibleList(Collection<? extends E> collection) { size = collection.size(); // Conditionally create the data. // if (size > 0) { // Allow for a bit-shift of growth. // data = newData(size + size / 8 + 1); collection.toArray(data); } } /** * Creates an initialized instance that directly uses the given arguments. * * @param size * the size of the list. * @param data * the underlying storage of the list. */ protected ExtensibleList(int size, Object[] data) { this.size = size; this.data = data; } /** * Returns new allocated data storage. Clients may override this to create * typed storage. The cost of type checking via a typed array is negligible. * * @return new data storage. */ protected Object[] newData(int capacity) { return new Object[capacity]; } /** * Returns whether <code>equals</code> rather than <code>==</code> should be * used to compare members. The default is to return <code>true</code> but * clients can optimize performance by returning <code>false</code>. The * performance difference is highly significant. * * @return whether <code>equals</code> rather than <code>==</code> should be * used. */ protected boolean useEquals() { return true; } /** * Returns whether two objects are equal using the {@link #useEquals * appropriate} comparison mechanism. * * @return whether two objects are equal. */ protected boolean equalObjects(Object firstObject, Object secondObject) { return useEquals() && firstObject != null ? firstObject .equals(secondObject) : firstObject == secondObject; } /** * Returns whether <code>null</code> is a valid object for the list. The * default is to return <code>true</code>, but clients can override this to * exclude <code>null</code>. * * @return whether <code>null</code> is a valid object for the list. */ protected boolean canContainNull() { return true; } /** * Returns whether objects are constrained to appear at most once in the * list. The default is to return <code>false</code>, but clients can * override this to ensure uniqueness of contents. The performance impact is * significant: operations such as <code>add</code> are O(n) as a result * requiring uniqueness. * * @return whether objects are constrained to appear at most once in the * list. */ protected boolean isUnique() { return false; } /** * Validates a new content object and returns the validated object. This * implementation checks for null, if {@link #canContainNull necessary} and * returns the argument object. Clients may throw additional types of * runtime exceptions in order to handle constraint violations. * * @param index * the position of the new content. * @param object * the new content. * @return the validated content. * @exception IllegalArgumentException * if a constraint prevents the object from being added. */ protected E validate(int index, E object) { if (!canContainNull() && object == null) { throw new IllegalArgumentException( "The 'no null' constraint is violated"); } return object; } /** * Assigns the object into the data storage at the given index and returns * the object that's been stored. Clients can monitor access to the storage * via this method. * * @param index * the position of the new content. * @param object * the new content. * @return the object that's been stored. * */ protected E assign(int index, E object) { data[index] = object; return object; } /** * Resolves the object at the index and returns the result. This * implementation simply returns the <code>object</code>; clients can use * this to transform objects as they are fetched. * * @param index * the position of the content. * @param object * the content. * @return the resolved object. */ protected E resolve(int index, E object) { return object; } /** * Called to indicate that the data storage has been set. This * implementation does nothing; clients can use this to monitor settings to * the data storage. * * @param index * the position that was set. * @param newObject * the new object at the position. * @param oldObject * the old object at the position. */ protected void didSet(int index, E newObject, E oldObject) { // Do nothing. } /** * Called to indicate that an object has been added to the data storage. * This implementation does nothing; clients can use this to monitor * additions to the data storage. * * @param index * the position object the new object. * @param newObject * the new object at the position. */ protected void didAdd(int index, E newObject) { // Do nothing. } /** * Called to indicate that an object has been removed from the data storage. * This implementation does nothing; clients can use this to monitor * removals from the data storage. * * @param index * the position of the old object. * @param oldObject * the old object at the position. */ protected void didRemove(int index, E oldObject) { // Do nothing. } /** * Called to indicate that the data storage has been cleared. This * implementation calls {@link #didRemove didRemove} for each object; * clients can use this to monitor clearing of the data storage. * * @param size * the original size of the list. * @param oldObjects * the old data storage being discarded. * @see #didRemove */ protected void didClear(int size, Object[] oldObjects) { if (oldObjects != null) { for (int i = 0; i < size; ++i) { @SuppressWarnings("unchecked") E object = (E) oldObjects[i]; didRemove(i, object); } } } /** * Called to indicate that an object has been moved in the data storage. * This implementation does nothing; clients can use this to monitor * movement in the data storage. * * @param index * the position of the moved object. * @param movedObject * the moved object at the position. * @param oldIndex * the position the object was at before the move. */ protected void didMove(int index, E movedObject, int oldIndex) { // Do nothing. } /** * Called to indicate that the data storage has been changed. This * implementation does nothing; clients can use this to monitor change in * the data storage. */ protected void didChange() { // Do nothing. } /** * Returns the number of objects in the list. * * @return the number of objects in the list. */ @Override public int size() { return size; } /** * Returns whether the list has zero size. * * @return whether the list has zero size. */ @Override public boolean isEmpty() { return size == 0; } /** * Returns whether the list contains the object. This implementation uses * either <code>equals</code> or <code>"=="</code> depending on * {@link #useEquals useEquals}. * * @param object * the object in question. * @return whether the list contains the object. * @see #useEquals */ @Override public boolean contains(Object object) { if (useEquals() && object != null) { for (int i = 0; i < size; ++i) { if (object.equals(data[i])) { return true; } } } else { for (int i = 0; i < size; ++i) { if (data[i] == object) { return true; } } } return false; } /** * Returns whether the list contains each object in the collection. This * implementation delegates to {@link #contains contains}, which may use * either <code>equals</code> or <code>"=="</code> depending on * {@link #useEquals useEquals}. * * @param collection * the collection of objects in question. * @return whether the list contains each object in the collection. * @see #contains * @see #useEquals */ @Override public boolean containsAll(Collection<?> collection) { for (Object o : collection) { if (!contains(o)) { return false; } } return true; } /** * Returns the position of the first occurrence of the object in the list. * This implementation uses either <code>equals</code> or <code>"=="</code> * depending on {@link #useEquals useEquals}. * * @param object * the object in question. * @return the position of the first occurrence of the object in the list. */ @Override public int indexOf(Object object) { if (useEquals() && object != null) { for (int i = 0; i < size; ++i) { if (object.equals(data[i])) { return i; } } } else { for (int i = 0; i < size; ++i) { if (data[i] == object) { return i; } } } return -1; } /** * Returns the position of the last occurrence of the object in the list. * This implementation uses either <code>equals</code> or <code>"=="</code> * depending on {@link #useEquals useEquals}. * * @param object * the object in question. * @return the position of the last occurrence of the object in the list. */ @Override public int lastIndexOf(Object object) { if (useEquals() && object != null) { for (int i = size - 1; i >= 0; --i) { if (object.equals(data[i])) { return i; } } } else { for (int i = size - 1; i >= 0; --i) { if (data[i] == object) { return i; } } } return -1; } /** * Returns an array containing all the objects in sequence. Clients may * override {@link #newData newData} to create typed storage in this case. * * @return an array containing all the objects in sequence. * @see #newData */ @Override public Object[] toArray() { Object[] result = newData(size); // Guard for no data. // if (size > 0) { System.arraycopy(data, 0, result, 0, size); } return result; } /** * Returns an array containing all the objects in sequence. * * @param array * the array that will be filled and returned, if it's big * enough; otherwise, a suitably large array of the same type * will be allocated and used instead. * @return an array containing all the objects in sequence. * @see #newData */ @Override public <T> T[] toArray(T[] array) { // Guard for no data. // if (size > 0) { if (array.length < size) { @SuppressWarnings("unchecked") T[] newArray = (T[]) Array.newInstance(array.getClass() .getComponentType(), size); array = newArray; } System.arraycopy(data, 0, array, 0, size); } if (array.length > size) { array[size] = null; } return array; } /** * Returns direct <b>unsafe</b> access to the underlying data storage. * Clients may <b>not</b> modify this and may <b>not</b> assume that the * array remains valid as the list is modified. * * @return direct <b>unsafe</b> access to the underlying data storage. */ public Object[] data() { return data; } /** * Updates directly and <b>unsafely</b> the underlying data storage. Clients * <b>must</b> be aware that this subverts all callbacks and hence possibly * the integrity of the list. */ public void setData(int size, Object[] data) { this.size = size; this.data = data; ++modCount; } /** * An IndexOutOfBoundsException that constructs a message from the argument * data. Having this avoids having the byte code that computes the message * repeated/inlined at the creation site. */ protected static class BasicIndexOutOfBoundsException extends IndexOutOfBoundsException { private static final long serialVersionUID = 1L; /** * Constructs an instance with a message based on the arguments. */ public BasicIndexOutOfBoundsException(int index, int size) { super("index=" + index + ", size=" + size); } } /** * Returns the object at the index. This implementation delegates to * {@link #resolve resolve} so that clients may transform the fetched * object. * * @param index * the position in question. * @return the object at the index. * @exception IndexOutOfBoundsException * if the index isn't within the size range. * @see #resolve * @see #basicGet */ @SuppressWarnings("unchecked") @Override public E get(int index) { if (index >= size) throw new BasicIndexOutOfBoundsException(index, size); return resolve(index, (E) data[index]); } /** * Returns the object at the index without {@link #resolve resolving} it. * * @param index * the position in question. * @return the object at the index. * @exception IndexOutOfBoundsException * if the index isn't within the size range. * @see #resolve * @see #get */ @SuppressWarnings("unchecked") public E basicGet(int index) { if (index >= size) throw new BasicIndexOutOfBoundsException(index, size); return (E) data[index]; } /** * Sets the object at the index and returns the old object at the index. * This implementation delegates to {@link #setUnique setUnique} after range * checking and after {@link #isUnique uniqueness} checking. * * @param index * the position in question. * @param object * the object to set. * @return the old object at the index. * @exception IndexOutOfBoundsException * if the index isn't within the size range. * @exception IllegalArgumentException * if there is a constraint violation, e.g., non-uniqueness. * @see #setUnique */ @Override public E set(int index, E object) { if (index >= size) throw new BasicIndexOutOfBoundsException(index, size); if (isUnique()) { int currentIndex = indexOf(object); if (currentIndex >= 0 && currentIndex != index) { throw new IllegalArgumentException( "The 'no duplicates' constraint is violated"); } } return setUnique(index, object); } /** * Sets the object at the index and returns the old object at the index; it * does no ranging checking or uniqueness checking. This implementation * delegates to {@link #assign assign}, {@link #didSet didSet}, and * {@link #didChange didChange}. * * @param index * the position in question. * @param object * the object to set. * @return the old object at the index. * @see #set */ public E setUnique(int index, E object) { @SuppressWarnings("unchecked") E oldObject = (E) data[index]; assign(index, validate(index, object)); didSet(index, object, oldObject); didChange(); return oldObject; } /** * Adds the object at the end of the list and returns whether the object was * added; if {@link #isUnique uniqueness} is required, duplicates will be * ignored and <code>false</code> will be returned. This implementation * delegates to {@link #addUnique(Object) addUnique(E)} after uniqueness * checking. * * @param object * the object to be added. * @return whether the object was added. * @see #addUnique(Object) */ @Override public boolean add(E object) { if (isUnique() && contains(object)) { return false; } else { addUnique(object); return true; } } /** * Adds the object at the end of the list; it does no uniqueness checking. * This implementation delegates to {@link #assign assign}, {@link #didAdd * didAdd}, and {@link #didChange didChange}. after uniqueness checking. * * @param object * the object to be added. * @see #add(Object) */ public void addUnique(E object) { // ++modCount // grow(size + 1); assign(size, validate(size, object)); didAdd(size++, object); didChange(); } /** * Adds the object at the given index in the list. If {@link #isUnique * uniqueness} is required, duplicates will be ignored. This implementation * delegates to {@link #addUnique(int, Object) addUnique(int, E)} after * uniqueness checking. * * @param object * the object to be added. * @exception IllegalArgumentException * if {@link #isUnique uniqueness} is required, and the * object is a duplicate. * @see #addUnique(int, Object) */ @Override public void add(int index, E object) { if (index > size) throw new BasicIndexOutOfBoundsException(index, size); if (isUnique() && contains(object)) { throw new IllegalArgumentException( "The 'no duplicates' constraint is violated"); } addUnique(index, object); } /** * Adds the object at the given index in the list; it does no ranging * checking or uniqueness checking. This implementation delegates to * {@link #assign assign}, {@link #didAdd didAdd}, and {@link #didChange * didChange}. * * @param object * the object to be added. * @see #add(int, Object) */ public void addUnique(int index, E object) { // ++modCount // grow(size + 1); E validatedObject = validate(index, object); if (index != size) { System.arraycopy(data, index, data, index + 1, size - index); } assign(index, validatedObject); ++size; didAdd(index, object); didChange(); } /** * Adds each object of the collection to the end of the list. If * {@link #isUnique uniqueness} is required, duplicates will be * {@link #getNonDuplicates removed} from the collection, which could even * result in an empty collection. This implementation delegates to * {@link #addAllUnique(Collection) addAllUnique(Collection)} after * uniqueness checking. * * @param collection * the collection of objects to be added. * @see #addAllUnique(Collection) */ @Override public boolean addAll(Collection<? extends E> collection) { if (isUnique()) { collection = getNonDuplicates(collection); } return addAllUnique(collection); } /** * Adds each object of the collection to the end of the list; it does no * uniqueness checking. This implementation delegates to {@link #assign * assign}, {@link #didAdd didAdd}, and {@link #didChange didChange}. * * @param collection * the collection of objects to be added. * @see #addAll(Collection) */ public boolean addAllUnique(Collection<? extends E> collection) { int growth = collection.size(); // ++modCount // grow(size + growth); Iterator<? extends E> objects = collection.iterator(); int oldSize = size; size += growth; for (int i = oldSize; i < size; ++i) { E object = objects.next(); assign(i, validate(i, object)); didAdd(i, object); didChange(); } return growth != 0; } /** * Adds each object of the collection at each successive index in the list * and returns whether any objects were added. If {@link #isUnique * uniqueness} is required, duplicates will be {@link #getNonDuplicates * removed} from the collection, which could even result in an empty * collection. This implementation delegates to * {@link #addAllUnique(int, Collection) addAllUnique(int, Collection)} * after uniqueness checking. * * @param index * the index at which to add. * @param collection * the collection of objects to be added. * @return whether any objects were added. * @see #addAllUnique(int, Collection) */ @Override public boolean addAll(int index, Collection<? extends E> collection) { if (index > size) throw new BasicIndexOutOfBoundsException(index, size); if (isUnique()) { collection = getNonDuplicates(collection); } return addAllUnique(index, collection); } /** * Adds each object of the collection at each successive index in the list * and returns whether any objects were added; it does no ranging checking * or uniqueness checking. This implementation delegates to {@link #assign * assign}, {@link #didAdd didAdd}, and {@link #didChange didChange}. * * @param index * the index at which to add. * @param collection * the collection of objects to be added. * @return whether any objects were added. * @see #addAll(int, Collection) */ public boolean addAllUnique(int index, Collection<? extends E> collection) { int growth = collection.size(); // ++modCount // grow(size + growth); int shifted = size - index; if (shifted > 0) { System.arraycopy(data, index, data, index + growth, shifted); } Iterator<? extends E> objects = collection.iterator(); size += growth; for (int i = 0; i < growth; ++i) { E object = objects.next(); assign(index, validate(index, object)); didAdd(index, object); didChange(); ++index; } return growth != 0; } /** * Adds each object from start to end of the array at the index of list and * returns whether any objects were added; it does no ranging checking or * uniqueness checking. This implementation delegates to {@link #assign * assign}, {@link #didAdd didAdd}, and {@link #didChange didChange}. * * @param objects * the objects to be added. * @param start * the index of first object to be added. * @param end * the index past the last object to be added. * @return whether any objects were added. * @see #addAllUnique(Object[], int, int) */ public boolean addAllUnique(Object[] objects, int start, int end) { int growth = end - start; // ++modCount // grow(size + growth); size += growth; int index = size; for (int i = start; i < end; ++i) { @SuppressWarnings("unchecked") E object = (E) objects[i]; assign(index, validate(index, object)); didAdd(index, object); didChange(); ++index; } return growth != 0; } /** * Adds each object from start to end of the array at each successive index * in the list and returns whether any objects were added; it does no * ranging checking or uniqueness checking. This implementation delegates to * {@link #assign assign}, {@link #didAdd didAdd}, and {@link #didChange * didChange}. * * @param index * the index at which to add. * @param objects * the objects to be added. * @param start * the index of first object to be added. * @param end * the index past the last object to be added. * @return whether any objects were added. * @see #addAllUnique(Object[], int, int) */ public boolean addAllUnique(int index, Object[] objects, int start, int end) { int growth = end - start; // ++modCount // grow(size + growth); int shifted = size - index; if (shifted > 0) { System.arraycopy(data, index, data, index + growth, shifted); } size += growth; for (int i = start; i < end; ++i) { @SuppressWarnings("unchecked") E object = (E) objects[i]; assign(index, validate(index, object)); didAdd(index, object); didChange(); ++index; } return growth != 0; } /** * Removes the object from the list and returns whether the object was * actually contained by the list. This implementation uses {@link #indexOf * indexOf} to find the object and delegates to {@link #remove(int) * remove(int)} in the case that it finds the object. * * @param object * the object to be removed. * @return whether the object was actually contained by the list. */ @Override public boolean remove(Object object) { int index = indexOf(object); if (index >= 0) { remove(index); return true; } else { return false; } } /** * Removes each object of the collection from the list and returns whether * any object was actually contained by the list. * * @param collection * the collection of objects to be removed. * @return whether any object was actually contained by the list. */ @Override public boolean removeAll(Collection<?> collection) { boolean modified = false; for (int i = size; --i >= 0;) { if (collection.contains(data[i])) { remove(i); modified = true; } } return modified; } /** * Removes the object at the index from the list and returns it. This * implementation delegates to {@link #didRemove didRemove} and * {@link #didChange didChange}. * * @param index * the position of the object to remove. * @return the removed object. * @exception IndexOutOfBoundsException * if the index isn't within the size range. */ @Override public E remove(int index) { if (index >= size) throw new BasicIndexOutOfBoundsException(index, size); ++modCount; @SuppressWarnings("unchecked") E oldObject = (E) data[index]; int shifted = size - index - 1; if (shifted > 0) { System.arraycopy(data, index + 1, data, index, shifted); } // Don't hold onto a duplicate reference to the last object. // data[--size] = null; didRemove(index, oldObject); didChange(); return oldObject; } /** * Removes from the list each object not contained by the collection and * returns whether any object was actually removed. This delegates to * {@link #remove(int) remove(int)} in the case that it finds an object that * isn't retained. * * @param collection * the collection of objects to be retained. * @return whether any object was actually removed. */ @Override public boolean retainAll(Collection<?> collection) { boolean modified = false; for (int i = size; --i >= 0;) { if (!collection.contains(data[i])) { remove(i); modified = true; } } return modified; } /** * Clears the list of all objects. This implementation discards the data * storage without modifying it and delegates to {@link #didClear didClear} * and {@link #didChange didChange}. */ @Override public void clear() { ++modCount; Object[] oldData = data; int oldSize = size; // Give it all back to the garbage collector. // data = null; size = 0; didClear(oldSize, oldData); didChange(); } /** * Moves the object to the index of the list. This implementation uses * {@link #indexOf} of find the object and delegates to * {@link #move(int, int) move(int, int)}. * * @param index * the new position for the object in the list. * @param object * the object to be moved. * @exception IndexOutOfBoundsException * if the index isn't within the size range or the object * isn't contained by the list. */ public void move(int index, E object) { move(index, indexOf(object)); } /** * Moves the object at the source index of the list to the target index of * the list and returns the moved object. This implementation delegates to * {@link #assign assign}, {@link #didMove didMove}, and {@link #didChange * didChange}. * * @param targetIndex * the new position for the object in the list. * @param sourceIndex * the old position of the object in the list. * @return the moved object. * @exception IndexOutOfBoundsException * if either index isn't within the size range. */ public E move(int targetIndex, int sourceIndex) { ++modCount; if (targetIndex >= size) throw new IndexOutOfBoundsException("targetIndex=" + targetIndex + ", size=" + size); if (sourceIndex >= size) throw new IndexOutOfBoundsException("sourceIndex=" + sourceIndex + ", size=" + size); @SuppressWarnings("unchecked") E object = (E) data[sourceIndex]; if (targetIndex != sourceIndex) { if (targetIndex < sourceIndex) { System.arraycopy(data, targetIndex, data, targetIndex + 1, sourceIndex - targetIndex); } else { System.arraycopy(data, sourceIndex + 1, data, sourceIndex, targetIndex - sourceIndex); } assign(targetIndex, object); didMove(targetIndex, object, sourceIndex); didChange(); } return object; } /** * Shrinks the capacity of the list to the minimal requirements. * * @see #grow */ public void shrink() { ++modCount; // Conditionally create the data. // if (size == 0) { // Give it all back to the garbage collector. // data = null; } else if (size < data.length) { Object[] oldData = data; data = newData(size); System.arraycopy(oldData, 0, data, 0, size); } } /** * Grows the capacity of the list to ensure that no additional growth is * needed until the size exceeds the specified minimum capacity. * * @see #shrink */ public void grow(int minimumCapacity) { ++modCount; int oldCapacity = data == null ? 0 : data.length; if (minimumCapacity > oldCapacity) { Object oldData[] = data; // This seems to be a pretty sweet formula that supports good // growth. // Adding an object to a list will create a list of capacity 4, // which is just about the average list size. // int newCapacity = oldCapacity + oldCapacity / 2 + 4; if (newCapacity < minimumCapacity) { newCapacity = minimumCapacity; } data = newData(newCapacity); if (oldData != null) { System.arraycopy(oldData, 0, data, 0, size); } } } private synchronized void writeObject(ObjectOutputStream objectOutputStream) throws IOException { objectOutputStream.defaultWriteObject(); if (data == null) { objectOutputStream.writeInt(0); } else { objectOutputStream.writeInt(data.length); for (int i = 0; i < size; ++i) { objectOutputStream.writeObject(data[i]); } } } private synchronized void readObject(ObjectInputStream objectInputStream) throws IOException, ClassNotFoundException { objectInputStream.defaultReadObject(); int arrayLength = objectInputStream.readInt(); if (arrayLength > 0) { try { data = newData(arrayLength); } catch (Throwable exception) { data = new Object[arrayLength]; } for (int i = 0; i < size; ++i) { @SuppressWarnings("unchecked") E object = (E) objectInputStream.readObject(); didAdd(i, assign(i, object)); } } } /** * Returns a shallow copy of this list. * * @return a shallow copy of this list. */ @Override public Object clone() { try { @SuppressWarnings("unchecked") ExtensibleList<E> clone = (ExtensibleList<E>) super.clone(); if (size > 0) { clone.size = size; clone.data = newData(size); System.arraycopy(data, 0, clone.data, 0, size); } return clone; } catch (CloneNotSupportedException exception) { throw new InternalError(); } } /** * Returns whether the object is a list with corresponding equal objects. * This implementation uses either <code>equals</code> or <code>"=="</code> * depending on {@link #useEquals useEquals}. * * @return whether the object is a list with corresponding equal objects. * @see #useEquals */ @Override public boolean equals(Object object) { if (object == this) { return true; } if (!(object instanceof List)) { return false; } List<?> list = (List<?>) object; if (list.size() != size) { return false; } Iterator<?> objects = list.iterator(); if (useEquals()) { for (int i = 0; i < size; ++i) { Object o1 = data[i]; Object o2 = objects.next(); if (o1 == null ? o2 != null : !o1.equals(o2)) { return false; } } } else { for (int i = 0; i < size; ++i) { Object o1 = data[i]; Object o2 = objects.next(); if (o1 != o2) { return false; } } } return true; } /** * Returns a hash code computed from each object's hash code. * * @return a hash code. */ @Override public int hashCode() { int hashCode = 1; for (int i = 0; i < size; ++i) { Object object = data[i]; hashCode = 31 * hashCode + (object == null ? 0 : object.hashCode()); } return hashCode; } /** * Returns a string of the form <code>"[object1, object2]"</code>. * * @return a string of the form <code>"[object1, object2]"</code>. */ @Override public String toString() { StringBuffer stringBuffer = new StringBuffer(); stringBuffer.append("["); for (int i = 0; i < size;) { stringBuffer.append(String.valueOf(data[i])); if (++i < size) { stringBuffer.append(", "); } } stringBuffer.append("]"); return stringBuffer.toString(); } /** * Returns an iterator. This implementation allocates a * {@link ExtensibleList.EIterator}. * * @return an iterator. * @see ExtensibleList.EIterator */ @Override public Iterator<E> iterator() { return new EIterator<E>(); } /** * An extensible iterator implementation. */ protected class EIterator<E1> implements Iterator<E1> { /** * The current position of the iterator. */ protected int cursor = 0; /** * The previous position of the iterator. */ protected int lastCursor = -1; /** * The modification count of the containing list. */ protected int expectedModCount = modCount; /** * Returns whether there are more objects. * * @return whether there are more objects. */ public boolean hasNext() { return cursor != size(); } /** * Returns the next object and advances the iterator. This * implementation delegates to {@link #doNext doNext}. * * @return the next object. * @exception NoSuchElementException * if the iterator is done. */ @SuppressWarnings("unchecked") public E1 next() { return (E1) doNext(); } /** * Returns the next object and advances the iterator. This * implementation delegates to {@link ExtensibleList#get get}. * * @return the next object. * @exception NoSuchElementException * if the iterator is done. */ protected E doNext() { try { E next = ExtensibleList.this.get(cursor); checkModCount(); lastCursor = cursor++; return next; } catch (IndexOutOfBoundsException exception) { checkModCount(); throw new NoSuchElementException(); } } /** * Removes the last object returned by {@link #next()} from the list, * it's an optional operation. This implementation can also function in * a list iterator to act upon on the object returned by calling * <code>previous</code>. * * @exception IllegalStateException * if <code>next</code> has not yet been called, or * <code>remove</code> has already been called after the * last call to <code>next</code>. */ public void remove() { if (lastCursor == -1) { throw new IllegalStateException(); } checkModCount(); try { ExtensibleList.this.remove(lastCursor); expectedModCount = modCount; if (lastCursor < cursor) { --cursor; } lastCursor = -1; } catch (IndexOutOfBoundsException exception) { throw new ConcurrentModificationException(); } } /** * Checks that the modification count is as expected. * * @exception ConcurrentModificationException * if the modification count is not as expected. */ protected void checkModCount() { if (modCount != expectedModCount) { throw new ConcurrentModificationException(); } } } /** * Returns a read-only iterator that does not {@link #resolve resolve} * objects. This implementation allocates a {@link NonResolvingEIterator}. * * @return a read-only iterator that does not resolve objects. */ protected Iterator<E> basicIterator() { return new NonResolvingEIterator<E>(); } /** * An extended read-only iterator that does not * {@link ExtensibleList#resolve resolve} objects. */ protected class NonResolvingEIterator<E1> extends EIterator<E1> { /** * Returns the next object and advances the iterator. This * implementation accesses the data storage directly. * * @return the next object. * @exception NoSuchElementException * if the iterator is done. */ @Override protected E doNext() { try { @SuppressWarnings("unchecked") E next = (E) ExtensibleList.this.data[cursor]; checkModCount(); lastCursor = cursor++; return next; } catch (IndexOutOfBoundsException exception) { checkModCount(); throw new NoSuchElementException(); } } /** * Throws and exception. * * @exception UnsupportedOperationException * always because it's not supported. */ @Override public void remove() { throw new UnsupportedOperationException(); } } /** * Returns a list iterator. This implementation allocates a * {@link ExtensibleList.EListIterator}. * * @return a list iterator. * @see ExtensibleList.EListIterator */ @Override public ListIterator<E> listIterator() { return new EListIterator<E>(); } /** * Returns a list iterator advanced to the given index. This implementation * allocates a {@link ExtensibleList.EListIterator}. * * @param index * the starting index. * @return a list iterator advanced to the index. * @see ExtensibleList.EListIterator * @exception IndexOutOfBoundsException * if the index isn't within the size range. */ @Override public ListIterator<E> listIterator(int index) { if (index < 0 || index > size()) throw new BasicIndexOutOfBoundsException(index, size); return new EListIterator<E>(index); } /** * An extensible list iterator implementation. */ protected class EListIterator<E1> extends EIterator<E1> implements ListIterator<E1> { /** * Creates an instance. */ public EListIterator() { super(); } /** * Creates an instance advanced to the index. * * @param index * the starting index. */ public EListIterator(int index) { cursor = index; } /** * Returns whether there are more objects for {@link #previous}. Returns * whether there are more objects. */ public boolean hasPrevious() { return cursor != 0; } /** * Returns the previous object and advances the iterator. This * implementation delegates to {@link #doPrevious doPrevious}. * * @return the previous object. * @exception NoSuchElementException * if the iterator is done. */ @SuppressWarnings("unchecked") public E1 previous() { return (E1) doPrevious(); } /** * Returns the previous object and advances the iterator. This * implementation delegates to {@link ExtensibleList#get get}. * * @return the previous object. * @exception NoSuchElementException * if the iterator is done. */ protected E doPrevious() { try { E previous = ExtensibleList.this.get(--cursor); checkModCount(); lastCursor = cursor; return previous; } catch (IndexOutOfBoundsException exception) { checkModCount(); throw new NoSuchElementException(); } } /** * Returns the index of the object that would be returned by calling * {@link #next() next}. * * @return the index of the object that would be returned by calling * <code>next</code>. */ public int nextIndex() { return cursor; } /** * Returns the index of the object that would be returned by calling * {@link #previous previous}. * * @return the index of the object that would be returned by calling * <code>previous</code>. */ public int previousIndex() { return cursor - 1; } /** * Sets the object at the index of the last call to {@link #next() next} * or {@link #previous previous}. This implementation delegates to * {@link ExtensibleList#set set}. * * @param object * the object to set. * @exception IllegalStateException * if <code>next</code> or <code>previous</code> have not * yet been called, or {@link #remove(Object) remove} or * {@link #add add} have already been called after the * last call to <code>next</code> or * <code>previous</code>. */ @SuppressWarnings("unchecked") public void set(E1 object) { doSet((E) object); } /** * Sets the object at the index of the last call to {@link #next() next} * or {@link #previous previous}. This implementation delegates to * {@link ExtensibleList#set set}. * * @param object * the object to set. * @exception IllegalStateException * if <code>next</code> or <code>previous</code> have not * yet been called, or {@link #remove(Object) remove} or * {@link #add add} have already been called after the * last call to <code>next</code> or * <code>previous</code>. */ protected void doSet(E object) { if (lastCursor == -1) { throw new IllegalStateException(); } checkModCount(); try { ExtensibleList.this.set(lastCursor, object); } catch (IndexOutOfBoundsException exception) { throw new ConcurrentModificationException(); } } /** * Adds the object at the {@link #next() next} index and advances the * iterator past it. This implementation delegates to * {@link #doAdd(Object) doAdd(E)}. * * @param object * the object to add. */ @SuppressWarnings("unchecked") public void add(E1 object) { doAdd((E) object); } /** * Adds the object at the {@link #next() next} index and advances the * iterator past it. This implementation delegates to * {@link ExtensibleList#add(int, Object) add(int, E)}. * * @param object * the object to add. */ protected void doAdd(E object) { checkModCount(); try { ExtensibleList.this.add(cursor++, object); expectedModCount = modCount; lastCursor = -1; } catch (IndexOutOfBoundsException exception) { throw new ConcurrentModificationException(); } } } /** * Returns a read-only list iterator that does not {@link #resolve resolve} * objects. This implementation allocates a * {@link NonResolvingEListIterator}. * * @return a read-only list iterator that does not resolve objects. */ protected ListIterator<E> basicListIterator() { return new NonResolvingEListIterator<E>(); } /** * Returns a read-only list iterator advanced to the given index that does * not {@link #resolve resolve} objects. This implementation allocates a * {@link NonResolvingEListIterator}. * * @param index * the starting index. * @return a read-only list iterator advanced to the index. * @exception IndexOutOfBoundsException * if the index isn't within the size range. */ protected ListIterator<E> basicListIterator(int index) { if (index < 0 || index > size()) throw new BasicIndexOutOfBoundsException(index, size); return new NonResolvingEListIterator<E>(index); } /** * An extended read-only list iterator that does not * {@link ExtensibleList#resolve resolve} objects. */ protected class NonResolvingEListIterator<E1> extends EListIterator<E1> { /** * Creates an instance. */ public NonResolvingEListIterator() { super(); } /** * Creates an instance advanced to the index. * * @param index * the starting index. */ public NonResolvingEListIterator(int index) { super(index); } /** * Returns the next object and advances the iterator. This * implementation accesses the data storage directly. * * @return the next object. * @exception NoSuchElementException * if the iterator is done. */ @Override protected E doNext() { try { @SuppressWarnings("unchecked") E next = (E) ExtensibleList.this.data[cursor]; checkModCount(); lastCursor = cursor++; return next; } catch (IndexOutOfBoundsException exception) { checkModCount(); throw new NoSuchElementException(); } } /** * Returns the previous object and advances the iterator. This * implementation accesses the data storage directly. * * @return the previous object. * @exception NoSuchElementException * if the iterator is done. */ @Override protected E doPrevious() { try { @SuppressWarnings("unchecked") E previous = (E) ExtensibleList.this.data[--cursor]; checkModCount(); lastCursor = cursor; return previous; } catch (IndexOutOfBoundsException exception) { checkModCount(); throw new NoSuchElementException(); } } /** * Throws an exception. * * @exception UnsupportedOperationException * always because it's not supported. */ @Override public void remove() { throw new UnsupportedOperationException(); } /** * Throws an exception. * * @exception UnsupportedOperationException * always because it's not supported. */ @Override public void set(E1 object) { throw new UnsupportedOperationException(); } /** * Throws an exception. * * @exception UnsupportedOperationException * always because it's not supported. */ @Override public void add(E1 object) { throw new UnsupportedOperationException(); } } /** * An unmodifiable version of {@link ExtensibleList}. */ public static class UnmodifiableEList<E> extends ExtensibleList<E> { private static final long serialVersionUID = 1L; /** * Creates an initialized instance. * * @param size * the size of the list. * @param data * the underlying storage of the list. */ public UnmodifiableEList(int size, Object[] data) { this.size = size; this.data = data; } /** * Throws an exception. * * @exception UnsupportedOperationException * always because it's not supported. */ @Override public E set(int index, E object) { throw new UnsupportedOperationException(); } /** * Throws an exception. * * @exception UnsupportedOperationException * always because it's not supported. */ @Override public boolean add(E object) { throw new UnsupportedOperationException(); } /** * Throws an exception. * * @exception UnsupportedOperationException * always because it's not supported. */ @Override public void add(int index, E object) { throw new UnsupportedOperationException(); } /** * Throws an exception. * * @exception UnsupportedOperationException * always because it's not supported. */ @Override public boolean addAll(Collection<? extends E> collection) { throw new UnsupportedOperationException(); } /** * Throws an exception. * * @exception UnsupportedOperationException * always because it's not supported. */ @Override public boolean addAll(int index, Collection<? extends E> collection) { throw new UnsupportedOperationException(); } /** * Throws an exception. * * @exception UnsupportedOperationException * always because it's not supported. */ @Override public boolean remove(Object object) { throw new UnsupportedOperationException(); } /** * Throws an exception. * * @exception UnsupportedOperationException * always because it's not supported. */ @Override public E remove(int index) { throw new UnsupportedOperationException(); } /** * Throws an exception. * * @exception UnsupportedOperationException * always because it's not supported. */ @Override public boolean removeAll(Collection<?> collection) { throw new UnsupportedOperationException(); } /** * Throws an exception. * * @exception UnsupportedOperationException * always because it's not supported. */ @Override public boolean retainAll(Collection<?> collection) { throw new UnsupportedOperationException(); } /** * Throws an exception. * * @exception UnsupportedOperationException * always because it's not supported. */ @Override public void clear() { throw new UnsupportedOperationException(); } /** * Throws an exception. * * @exception UnsupportedOperationException * always because it's not supported. */ @Override public void move(int index, E object) { throw new UnsupportedOperationException(); } /** * Throws an exception. * * @exception UnsupportedOperationException * always because it's not supported. */ @Override public E move(int targetIndex, int sourceIndex) { throw new UnsupportedOperationException(); } /** * Throws an exception. * * @exception UnsupportedOperationException * always because it's not supported. */ @Override public void shrink() { throw new UnsupportedOperationException(); } /** * Throws an exception. * * @exception UnsupportedOperationException * always because it's not supported. */ @Override public void grow(int minimumCapacity) { throw new UnsupportedOperationException(); } /** * Returns the {@link ExtensibleList#basicIterator basic iterator}. * * @return the basic iterator. */ @Override public Iterator<E> iterator() { return basicIterator(); } /** * Returns the {@link #basicListIterator() basic list iterator}. * * @return the basic list iterator. */ @Override public ListIterator<E> listIterator() { return basicListIterator(); } /** * Returns the {@link #basicListIterator(int) basic list iterator} * advanced to the index. * * @param index * the starting index. * @return the basic list iterator. */ @Override public ListIterator<E> listIterator(int index) { return basicListIterator(index); } } /** * Returns an <b>unsafe</b> list that provides a {@link #resolve * non-resolving} view of the underlying data storage. * * @return an <b>unsafe</b> list that provides a non-resolving view of the * underlying data storage. */ protected List<E> basicList() { if (size == 0) { return Collections.emptyList(); } else { return new UnmodifiableEList<E>(size, data); } } /** * A <code>BasicEList</code> that {@link #useEquals uses} <code>==</code> * instead of <code>equals</code> to compare members. */ public static class FastCompare<E> extends ExtensibleList<E> { private static final long serialVersionUID = 1L; /** * Creates an empty instance with no initial capacity. */ public FastCompare() { super(); } /** * Creates an empty instance with the given capacity. * * @param initialCapacity * the initial capacity of the list before it must grow. * @exception IllegalArgumentException * if the <code>initialCapacity</code> is negative. */ public FastCompare(int initialCapacity) { super(initialCapacity); } /** * Creates an instance that is a copy of the collection. * * @param collection * the initial contents of the list. */ public FastCompare(Collection<? extends E> collection) { super(collection.size()); addAll(collection); } /** * Returns <code>false</code> because this list uses <code>==</code>. * * @return <code>false</code>. */ @Override protected boolean useEquals() { return false; } } /** * Returns the collection of objects in the given collection that are also * contained by this list. * * @param collection * the other collection. * @return the collection of objects in the given collection that are also * contained by this list. */ protected Collection<E> getDuplicates(Collection<?> collection) { if (collection.isEmpty()) { return Collections.emptyList(); } else { Collection<E> filteredResult = useEquals() ? new ExtensibleList<E>( collection.size()) : new FastCompare<E>(collection.size()); for (E object : this) { if (collection.contains(object)) { filteredResult.add(object); } } return filteredResult; } } /** * Returns the collection of objects in the given collection that are not * also contained by this list. * * @param collection * the other collection. * @return the collection of objects in the given collection that are not * also contained by this list. */ protected Collection<E> getNonDuplicates(Collection<? extends E> collection) { Collection<E> result = useEquals() ? new UniqueExtensibleList<E>( collection.size()) : new UniqueExtensibleList.FastCompare<E>( collection.size()); for (E object : collection) { if (!contains(object)) { result.add(object); } } return result; } }