| /** |
| * Copyright (c) 2009 IBM Corporation 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: |
| * IBM - Initial API and implementation |
| */ |
| package org.eclipse.emf.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.Collection; |
| import java.util.ConcurrentModificationException; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.ListIterator; |
| import java.util.RandomAccess; |
| |
| |
| /** |
| * A highly extensible abstract list implementation {@link #data() logically backed by an array} that is <b>never<b> modified. |
| * |
| */ |
| public abstract class ArrayDelegatingEList<E> extends AbstractEList<E> implements RandomAccess, Cloneable, Serializable |
| { |
| private static final long serialVersionUID = 1L; |
| |
| /** |
| * Creates an empty instance with no initial capacity. |
| * The data storage will be null. |
| */ |
| public ArrayDelegatingEList() |
| { |
| super(); |
| } |
| |
| /** |
| * Creates an instance that is a copy of the collection. |
| * @param collection the initial contents of the list. |
| */ |
| public ArrayDelegatingEList(Collection<? extends E> collection) |
| { |
| // Conditionally create the data. |
| // |
| int size = collection.size(); |
| if (size > 0) |
| { |
| Object [] data = newData(size); |
| collection.toArray(data); |
| setData(data); |
| } |
| } |
| |
| /** |
| * Creates an initialized instance that directly uses the given arguments. |
| * @param data the underlying storage of the list. |
| */ |
| protected ArrayDelegatingEList(Object [] data) |
| { |
| setData(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]; |
| } |
| |
| /** |
| * 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(Object [] data, int index, E object) |
| { |
| data[index] = object; |
| return object; |
| } |
| |
| /** |
| * Returns the number of objects in the list. |
| * @return the number of objects in the list. |
| */ |
| @Override |
| public final int size() |
| { |
| Object[] data = data(); |
| return data == null ? 0 : data.length; |
| } |
| |
| /** |
| * Returns whether the list has zero size. |
| * @return whether the list has zero size. |
| */ |
| @Override |
| public boolean isEmpty() |
| { |
| return data() == null; |
| } |
| |
| /** |
| * 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) |
| { |
| Object[] data = data(); |
| if (data != null) |
| { |
| if (useEquals() && object != null) |
| { |
| for (Object datum : data) |
| { |
| if (object.equals(datum)) |
| { |
| return true; |
| } |
| } |
| } |
| else |
| { |
| for (Object datum : data) |
| { |
| if (datum == object) |
| { |
| return true; |
| } |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| /** |
| * 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) |
| { |
| Object[] data = data(); |
| if (data != null) |
| { |
| if (useEquals() && object != null) |
| { |
| for (int i = 0, size = data.length; i < size; ++i) |
| { |
| if (object.equals(data[i])) |
| { |
| return i; |
| } |
| } |
| } |
| else |
| { |
| for (int i = 0, size = data.length; 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) |
| { |
| Object[] data = data(); |
| if (data != null) |
| { |
| if (useEquals() && object != null) |
| { |
| for (int i = data.length - 1; i >= 0; --i) |
| { |
| if (object.equals(data[i])) |
| { |
| return i; |
| } |
| } |
| } |
| else |
| { |
| for (int i = data.length - 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[] data = data(); |
| int size = data == null ? 0 : data.length; |
| 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. |
| // |
| Object[] data = data(); |
| int size = data == null ? 0 : data.length; |
| 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 array's elements |
| * but <b>may</b> assume that the array remains valid even as the list is modified. |
| * The array's length is exactly the same as the list's {@link #size() size}; |
| * <code>null</code> is used to represent the empty list. |
| * @return direct <b>unsafe</b> access to the underlying data storage. |
| */ |
| public abstract Object [] 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. |
| * The list implementation itself calls this method whenever a modification of the list requires a new backing array. |
| * @param data the new underlying data storage. |
| */ |
| public void setData(Object [] data) |
| { |
| ++modCount; |
| } |
| |
| /** |
| * 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", "null"}) |
| @Override |
| public E get(int index) |
| { |
| Object[] data = data(); |
| int size = data == null ? 0 : data.length; |
| 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", "null"}) |
| @Override |
| public E basicGet(int index) |
| { |
| Object[] data = data(); |
| int size = data == null ? 0 : data.length; |
| if (index >= size) |
| throw new BasicIndexOutOfBoundsException(index, size); |
| |
| return (E)data[index]; |
| } |
| |
| @Override |
| @SuppressWarnings({"unchecked"}) |
| protected E primitiveGet(int index) |
| { |
| return (E)data()[index]; |
| } |
| |
| /** |
| * 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 |
| */ |
| @Override |
| public E setUnique(int index, E object) |
| { |
| Object[] data = copy(); |
| @SuppressWarnings("unchecked") E oldObject = (E)data[index]; |
| assign(data, index, validate(index, object)); |
| setData(data); |
| didSet(index, object, oldObject); |
| didChange(); |
| return oldObject; |
| } |
| |
| /** |
| * 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) |
| */ |
| @Override |
| public void addUnique(E object) |
| { |
| int size = size(); |
| Object [] data = grow(size + 1); |
| assign(data, size, validate(size, object)); |
| setData(data); |
| didAdd(size, object); |
| didChange(); |
| } |
| |
| /** |
| * 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) |
| */ |
| @Override |
| public void addUnique(int index, E object) |
| { |
| Object[] oldData = data(); |
| int size = oldData == null ? 0 : oldData.length; |
| Object[] data = grow(size + 1); |
| E validatedObject = validate(index, object); |
| if (index != size) |
| { |
| System.arraycopy(oldData, index, data, index + 1, size - index); |
| } |
| assign(data, index, validatedObject); |
| setData(data); |
| didAdd(index, object); |
| didChange(); |
| } |
| |
| /** |
| * 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) |
| */ |
| @Override |
| public boolean addAllUnique(Collection<? extends E> collection) |
| { |
| int growth = collection.size(); |
| if (growth != 0) |
| { |
| int oldSize = size(); |
| int size = oldSize + growth; |
| Object[] data = grow(size); |
| |
| Iterator<? extends E> objects = collection.iterator(); |
| for (int i = oldSize; i < size; ++i) |
| { |
| E object = objects.next(); |
| assign(data, i, validate(i, object)); |
| } |
| setData(data); |
| for (int i = oldSize; i < size; ++i) |
| { |
| @SuppressWarnings("unchecked") E object = (E)data[i]; |
| didAdd(i, object); |
| didChange(); |
| } |
| return true; |
| } |
| else |
| { |
| ++modCount; |
| return false; |
| } |
| } |
| |
| /** |
| * 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) |
| */ |
| @Override |
| public boolean addAllUnique(int index, Collection<? extends E> collection) |
| { |
| int growth = collection.size(); |
| if (growth != 0) |
| { |
| Object[] oldData = data(); |
| int oldSize = oldData == null ? 0 : oldData.length; |
| int size = oldSize + growth; |
| Object[] data = grow(size); |
| |
| int shifted = oldSize - index; |
| if (shifted > 0) |
| { |
| System.arraycopy(oldData, index, data, index + growth, shifted); |
| } |
| |
| Iterator<? extends E> objects = collection.iterator(); |
| for (int i = 0; i < growth; ++i) |
| { |
| E object = objects.next(); |
| int currentIndex = index + i; |
| assign(data, currentIndex, validate(currentIndex, object)); |
| } |
| setData(data); |
| for (int i = 0; i < growth; ++i) |
| { |
| @SuppressWarnings("unchecked") E object = (E)data[index]; |
| didAdd(index, object); |
| didChange(); |
| index++; |
| } |
| return true; |
| } |
| else |
| { |
| ++modCount; |
| return false; |
| } |
| } |
| |
| /** |
| * 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) |
| */ |
| @Override |
| public boolean addAllUnique(Object [] objects, int start, int end) |
| { |
| int growth = end - start; |
| if (growth > 0) |
| { |
| int oldSize = size(); |
| int size = oldSize + growth; |
| Object[] data = grow(size); |
| |
| for (int i = start, index = size; i < end; ++i, ++index) |
| { |
| @SuppressWarnings("unchecked") E object = (E)objects[i]; |
| assign(data, index, validate(index, object)); |
| } |
| setData(data); |
| for (int i = start, index = size; i < end; ++i, ++index) |
| { |
| @SuppressWarnings("unchecked") E object = (E)objects[i]; |
| didAdd(index, object); |
| didChange(); |
| } |
| return true; |
| } |
| else |
| { |
| ++modCount; |
| return false; |
| } |
| } |
| |
| /** |
| * 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) |
| */ |
| @Override |
| public boolean addAllUnique(int index, Object [] objects, int start, int end) |
| { |
| int growth = end - start; |
| if (growth > 0) |
| { |
| Object[] oldData = data(); |
| int oldSize = oldData == null ? 0 : oldData.length; |
| int size = oldSize + growth; |
| Object[] data = grow(size); |
| |
| int shifted = oldSize - index; |
| if (shifted > 0) |
| { |
| System.arraycopy(oldData, index, data, index + growth, shifted); |
| } |
| |
| for (int i = start, currentIndex = index; i < end; ++i, ++currentIndex) |
| { |
| @SuppressWarnings("unchecked") E object = (E)objects[i]; |
| assign(data, currentIndex, validate(currentIndex, object)); |
| } |
| setData(data); |
| for (int i = start, currentIndex = index; i < end; ++i, ++currentIndex) |
| { |
| @SuppressWarnings("unchecked") E object = (E)objects[i]; |
| didAdd(currentIndex, object); |
| didChange(); |
| } |
| return true; |
| } |
| else |
| { |
| ++modCount; |
| 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. |
| */ |
| @SuppressWarnings("null") |
| @Override |
| public boolean removeAll(Collection<?> collection) |
| { |
| Object[] data = data(); |
| int size = data == null ? 0 : data.length; |
| 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. |
| */ |
| @SuppressWarnings("null") |
| @Override |
| public E remove(int index) |
| { |
| Object[] data = data(); |
| int size = data == null ? 0 : data.length; |
| if (index >= size) |
| throw new BasicIndexOutOfBoundsException(index, size); |
| |
| @SuppressWarnings("unchecked") E oldObject = (E)data[index]; |
| |
| Object[] newData; |
| if (size == 1) |
| { |
| newData = null; |
| } |
| else |
| { |
| newData = newData(size - 1); |
| System.arraycopy(data, 0, newData, 0, index); |
| int shifted = size - index - 1; |
| if (shifted > 0) |
| { |
| System.arraycopy(data, index+1, newData, index, shifted); |
| } |
| } |
| |
| setData(newData); |
| 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. |
| */ |
| @SuppressWarnings("null") |
| @Override |
| public boolean retainAll(Collection<?> collection) |
| { |
| boolean modified = false; |
| Object[] data = data(); |
| int size = data == null ? 0 : data.length; |
| 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 = oldData == null ? 0 : oldData.length; |
| |
| // Give it all back to the garbage collector. |
| // |
| setData(null); |
| |
| didClear(oldSize, oldData); |
| didChange(); |
| } |
| |
| /** |
| * 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. |
| */ |
| @SuppressWarnings("null") |
| @Override |
| public E move(int targetIndex, int sourceIndex) |
| { |
| Object[] data = copy(); |
| int size = data == null ? 0 : data.length; |
| 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(data, targetIndex, object); |
| setData(data); |
| didMove(targetIndex, object, sourceIndex); |
| didChange(); |
| } |
| return object; |
| } |
| |
| /** |
| * Grows the capacity of the list to exactly the new size. |
| */ |
| protected Object[] grow(int size) |
| { |
| Object[] oldData = data(); |
| Object[] data = newData(size); |
| if (oldData != null) |
| { |
| System.arraycopy(oldData, 0, data, 0, oldData.length); |
| } |
| return data; |
| } |
| |
| private static Object [] EMPTY_ARRAY = new Object [0]; |
| |
| /** |
| * Returns a copy of the existing data array. |
| */ |
| protected Object[] copy() |
| { |
| Object[] data = data(); |
| if (data != null) |
| { |
| Object[] newData = newData(data.length); |
| System.arraycopy(data, 0, newData, 0, data.length); |
| return newData; |
| } |
| else |
| { |
| return EMPTY_ARRAY; |
| } |
| } |
| |
| private synchronized void writeObject(ObjectOutputStream objectOutputStream) throws IOException |
| { |
| objectOutputStream.defaultWriteObject(); |
| Object[] data = data(); |
| int size = data == null ? 0 : data.length; |
| if (data == null) |
| { |
| objectOutputStream.writeInt(0); |
| } |
| else |
| { |
| objectOutputStream.writeInt(size); |
| for (int i = 0; i < size; ++i) |
| { |
| objectOutputStream.writeObject(data[i]); |
| } |
| } |
| } |
| |
| private synchronized void readObject(ObjectInputStream objectInputStream) throws IOException, ClassNotFoundException |
| { |
| objectInputStream.defaultReadObject(); |
| int size = objectInputStream.readInt(); |
| Object[] data; |
| if (size > 0) |
| { |
| try |
| { |
| data = newData(size); |
| } |
| catch (Throwable exception) |
| { |
| data = new Object[size]; |
| } |
| |
| setData(data); |
| |
| for (int i = 0; i < size; ++i) |
| { |
| @SuppressWarnings("unchecked") E object = (E)objectInputStream.readObject(); |
| didAdd(i, assign(data, i, object)); |
| } |
| } |
| } |
| |
| /** |
| * Returns a shallow copy of this list. |
| * @return a shallow copy of this list. |
| */ |
| @Override |
| public Object clone() |
| { |
| try |
| { |
| @SuppressWarnings("unchecked") ArrayDelegatingEList<E> clone = (ArrayDelegatingEList<E>)super.clone(); |
| Object[] data = data(); |
| int size = data == null ? 0 : data.length; |
| if (size > 0) |
| { |
| Object[] newData = newData(size); |
| clone.setData(newData); |
| System.arraycopy(data, 0, newData, 0, size); |
| } |
| return clone; |
| } |
| catch (CloneNotSupportedException exception) |
| { |
| throw new InternalError(); |
| } |
| } |
| |
| /** |
| * Returns an iterator. |
| * This implementation allocates a {@link ArrayDelegatingEList.EIterator}. |
| * @return an iterator. |
| * @see ArrayDelegatingEList.EIterator |
| */ |
| @Override |
| public Iterator<E> iterator() |
| { |
| return new EIterator<E>(); |
| } |
| |
| /** |
| * An extensible iterator implementation. |
| */ |
| protected class EIterator<E1> extends AbstractEList<E>.EIterator<E1> |
| { |
| /** |
| * The expected data array of the containing list. |
| */ |
| protected Object [] expectedData = data(); |
| |
| /** |
| * Checks that the modification count and data array are as expected. |
| * @exception ConcurrentModificationException if the modification count is not as expected. |
| */ |
| @Override |
| protected void checkModCount() |
| { |
| if (modCount != expectedModCount || data() != expectedData) |
| { |
| throw new ConcurrentModificationException(); |
| } |
| } |
| |
| @Override |
| public void remove() |
| { |
| super.remove(); |
| expectedData = data(); |
| } |
| } |
| |
| /** |
| * 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. |
| */ |
| @Override |
| protected Iterator<E> basicIterator() |
| { |
| return new NonResolvingEIterator<E>(); |
| } |
| |
| /** |
| * An extended read-only iterator that does not {@link ArrayDelegatingEList#resolve resolve} objects. |
| */ |
| protected class NonResolvingEIterator<E1> extends AbstractEList<E>.NonResolvingEIterator<E1> |
| { |
| /** |
| * The expected data array of the containing list. |
| */ |
| protected Object [] expectedData = data(); |
| |
| /** |
| * Checks that the modification count and data array are as expected. |
| * @exception ConcurrentModificationException if the modification count is not as expected. |
| */ |
| @Override |
| protected void checkModCount() |
| { |
| if (modCount != expectedModCount || data() != expectedData) |
| { |
| throw new ConcurrentModificationException(); |
| } |
| } |
| } |
| |
| /** |
| * Returns a list iterator. |
| * This implementation allocates a {@link ArrayDelegatingEList.EListIterator}. |
| * @return a list iterator. |
| * @see ArrayDelegatingEList.EListIterator |
| */ |
| @Override |
| public ListIterator<E> listIterator() |
| { |
| return new EListIterator<E>(); |
| } |
| |
| /** |
| * Returns a list iterator advanced to the given index. |
| * This implementation allocates a {@link ArrayDelegatingEList.EListIterator}. |
| * @param index the starting index. |
| * @return a list iterator advanced to the index. |
| * @see ArrayDelegatingEList.EListIterator |
| * @exception IndexOutOfBoundsException if the index isn't within the size range. |
| */ |
| @Override |
| public ListIterator<E> listIterator(int index) |
| { |
| int size = size(); |
| 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 AbstractEList<E>.EListIterator<E1> |
| { |
| /** |
| * The expected data array of the containing list. |
| */ |
| protected Object [] expectedData = data(); |
| |
| /** |
| * Creates an instance. |
| */ |
| public EListIterator() |
| { |
| super(); |
| } |
| |
| /** |
| * Creates an instance advanced to the index. |
| * @param index the starting index. |
| */ |
| public EListIterator(int index) |
| { |
| super(index); |
| } |
| |
| /** |
| * Checks that the modification count and data array are as expected. |
| * @exception ConcurrentModificationException if the modification count is not as expected. |
| */ |
| @Override |
| protected void checkModCount() |
| { |
| if (modCount != expectedModCount || data() != expectedData) |
| { |
| throw new ConcurrentModificationException(); |
| } |
| } |
| |
| @Override |
| public void remove() |
| { |
| super.remove(); |
| expectedData = data(); |
| } |
| |
| @Override |
| protected void doSet(E object) |
| { |
| super.doSet(object); |
| expectedData = data(); |
| } |
| } |
| |
| /** |
| * 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. |
| */ |
| @Override |
| 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. |
| */ |
| @Override |
| protected ListIterator<E> basicListIterator(int index) |
| { |
| int size = size(); |
| 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 ArrayDelegatingEList#resolve resolve} objects. |
| */ |
| protected class NonResolvingEListIterator<E1> extends AbstractEList<E>.NonResolvingEListIterator<E1> |
| { |
| /** |
| * The expected data array of the containing list. |
| */ |
| protected Object [] expectedData = data(); |
| |
| /** |
| * Creates an instance. |
| */ |
| public NonResolvingEListIterator() |
| { |
| super(); |
| } |
| |
| /** |
| * Creates an instance advanced to the index. |
| * @param index the starting index. |
| */ |
| public NonResolvingEListIterator(int index) |
| { |
| super(index); |
| } |
| |
| /** |
| * Checks that the modification count and data array are as expected. |
| * @exception ConcurrentModificationException if the modification count is not as expected. |
| */ |
| @Override |
| protected void checkModCount() |
| { |
| if (modCount != expectedModCount || data() != expectedData) |
| { |
| throw new ConcurrentModificationException(); |
| } |
| } |
| } |
| |
| /** |
| * 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. |
| */ |
| @Override |
| protected List<E> basicList() |
| { |
| Object[] data = data(); |
| int size = data == null ? 0 : data.length; |
| if (size == 0) |
| { |
| return ECollections.emptyEList(); |
| } |
| else |
| { |
| return new BasicEList.UnmodifiableEList<E>(size, data); |
| } |
| } |
| } |