| /** |
| * 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.util.AbstractList; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.ConcurrentModificationException; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.LinkedHashSet; |
| import java.util.List; |
| import java.util.ListIterator; |
| import java.util.NoSuchElementException; |
| import java.util.Set; |
| |
| |
| /** |
| * A highly extensible abstract list implementation. |
| */ |
| public abstract class AbstractEList<E> extends AbstractList<E> implements EList<E> |
| { |
| /** |
| * Creates an empty instance with no initial capacity. |
| * The data storage will be null. |
| */ |
| public AbstractEList() |
| { |
| super(); |
| } |
| |
| /** |
| * 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; |
| } |
| |
| /** |
| * 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. |
| } |
| |
| /** |
| * 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 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 |
| */ |
| protected E basicGet(int index) |
| { |
| int size = size(); |
| if (index >= size) |
| throw new BasicIndexOutOfBoundsException(index, size); |
| |
| return primitiveGet(index); |
| } |
| |
| /** |
| * Returns the object at the index without {@link #resolve resolving} it and without range checking the index. |
| * @param index the position in question. |
| * @return the object at the index. |
| * @see #resolve |
| * @see #get |
| * @see #basicGet(int) |
| */ |
| protected abstract E primitiveGet(int 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) |
| { |
| int size = size(); |
| 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 abstract E setUnique(int index, E object); |
| |
| /** |
| * 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 abstract void addUnique(E object); |
| |
| /** |
| * 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) |
| { |
| int size = size(); |
| 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 abstract void addUnique(int index, E object); |
| |
| /** |
| * 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 abstract boolean addAllUnique(Collection<? extends E> collection); |
| |
| /** |
| * 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) |
| { |
| int size = size(); |
| 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 abstract boolean addAllUnique(int index, Collection<? extends E> collection); |
| |
| /** |
| * 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 abstract boolean addAllUnique(Object [] objects, int start, int end); |
| |
| /** |
| * 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 abstract boolean addAllUnique(int index, Object [] objects, int start, int end); |
| |
| /** |
| * 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(primitiveGet(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 abstract E remove(int index); |
| |
| /** |
| * 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(primitiveGet(i))) |
| { |
| remove(i); |
| modified = true; |
| } |
| } |
| return modified; |
| } |
| |
| /** |
| * 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 abstract E move(int targetIndex, int sourceIndex); |
| |
| |
| /** |
| * 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; |
| int size = size(); |
| if (list.size() != size) |
| { |
| return false; |
| } |
| |
| Iterator<?> objects = list.iterator(); |
| if (useEquals()) |
| { |
| for (int i = 0; i < size; ++i) |
| { |
| Object o1 = primitiveGet(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 = primitiveGet(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, size = size(); i < size; ++i) |
| { |
| Object object = primitiveGet(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, size = size(); i < size; ) |
| { |
| stringBuffer.append(String.valueOf(primitiveGet(i))); |
| if (++i < size) |
| { |
| stringBuffer.append(", "); |
| } |
| } |
| stringBuffer.append("]"); |
| return stringBuffer.toString(); |
| } |
| |
| /** |
| * Returns an iterator. |
| * This implementation allocates a {@link AbstractEList.EIterator}. |
| * @return an iterator. |
| * @see AbstractEList.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 AbstractEList#get get}. |
| * @return the next object. |
| * @exception NoSuchElementException if the iterator is done. |
| */ |
| protected E doNext() |
| { |
| try |
| { |
| E next = 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 |
| { |
| AbstractEList.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 AbstractEList#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 |
| { |
| E next = primitiveGet(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 AbstractEList.EListIterator}. |
| * @return a list iterator. |
| * @see AbstractEList.EListIterator |
| */ |
| @Override |
| public ListIterator<E> listIterator() |
| { |
| return new EListIterator<E>(); |
| } |
| |
| /** |
| * Returns a list iterator advanced to the given index. |
| * This implementation allocates a {@link AbstractEList.EListIterator}. |
| * @param index the starting index. |
| * @return a list iterator advanced to the index. |
| * @see AbstractEList.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 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 AbstractEList#get get}. |
| * @return the previous object. |
| * @exception NoSuchElementException if the iterator is done. |
| */ |
| protected E doPrevious() |
| { |
| try |
| { |
| E previous = 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 AbstractEList#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 AbstractEList#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 |
| { |
| AbstractEList.this.set(lastCursor, object); |
| expectedModCount = modCount; |
| } |
| 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 AbstractEList#add(int, Object) add(int, E)}. |
| * @param object the object to add. |
| */ |
| protected void doAdd(E object) |
| { |
| checkModCount(); |
| |
| try |
| { |
| AbstractEList.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) |
| { |
| 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 AbstractEList#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 |
| { |
| E next = primitiveGet(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 |
| { |
| E previous = primitiveGet(--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(); |
| } |
| } |
| |
| /** |
| * 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 abstract List<E> basicList(); |
| |
| /** |
| * 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 ECollections.emptyEList(); |
| } |
| else |
| { |
| Collection<E> filteredResult = |
| new BasicEList<E>(collection.size()) |
| { |
| private static final long serialVersionUID = 1L; |
| |
| private int expectedModCount; |
| |
| private Set<E> set; |
| |
| @Override |
| public boolean contains(Object object) |
| { |
| if (size > 10) |
| { |
| if (set == null || AbstractEList.this.modCount != expectedModCount) |
| { |
| set = new HashSet<E>(this); |
| expectedModCount = modCount; |
| } |
| return set.contains(object); |
| } |
| else |
| { |
| return super.contains(object); |
| } |
| } |
| |
| @Override |
| protected boolean useEquals() |
| { |
| return AbstractEList.this.useEquals(); |
| } |
| }; |
| |
| 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 = new LinkedHashSet<E>(collection); |
| result.removeAll(this); |
| return new ArrayList<E>(result); |
| } |
| } |