/* Glazed Lists (c) 2003-2006 */
/* http://publicobject.com/glazedlists/ publicobject.com,*/
/* O'Dell Engineering Ltd.*/
package ca.odell.glazedlists;
import ca.odell.glazedlists.event.ListEvent;
import ca.odell.glazedlists.impl.GlazedListsImpl;
import ca.odell.glazedlists.impl.adt.barcode2.Element;
import ca.odell.glazedlists.impl.adt.barcode2.SimpleTree;
import ca.odell.glazedlists.impl.adt.barcode2.SimpleTreeIterator;
import java.util.*;
/**
* An {@link EventList} that shows its source {@link EventList} in sorted order.
*
* <p>The sorting strategy is specified with a {@link Comparator}. If no
* {@link Comparator} is specified, all of the elements of the source {@link EventList}
* must implement {@link Comparable}.
*
* <p>This {@link EventList} supports all write operations.
*
* <p><strong><font color="#FF0000">Warning:</font></strong> This class
* breaks the contract required by {@link List}. See {@link EventList}
* for an example.
*
* <p><strong><font color="#FF0000">Warning:</font></strong> This class is
* thread ready but not thread safe. See {@link EventList} for an example
* of thread safe code.
*
* <p><table border="1" width="100%" cellpadding="3" cellspacing="0">
* <tr class="TableHeadingColor"><td colspan=2><font size="+2"><b>EventList Overview</b></font></td></tr>
* <tr><td class="TableSubHeadingColor"><b>Writable:</b></td><td>yes</td></tr>
* <tr><td class="TableSubHeadingColor"><b>Concurrency:</b></td><td>thread ready, not thread safe</td></tr>
* <tr><td class="TableSubHeadingColor"><b>Performance:</b></td><td>reads: O(log N), writes O(log N), change comparator O(N log N)</td></tr>
* <tr><td class="TableSubHeadingColor"><b>Memory:</b></td><td>72 bytes per element</td></tr>
* <tr><td class="TableSubHeadingColor"><b>Unit Tests:</b></td><td>N/A</td></tr>
* <tr><td class="TableSubHeadingColor"><b>Issues:</b></td><td>
* <a href="https://glazedlists.dev.java.net/issues/show_bug.cgi?id=39">39</a>
* <a href="https://glazedlists.dev.java.net/issues/show_bug.cgi?id=40">40</a>
* <a href="https://glazedlists.dev.java.net/issues/show_bug.cgi?id=58">58</a>
* <a href="https://glazedlists.dev.java.net/issues/show_bug.cgi?id=60">60</a>
* <a href="https://glazedlists.dev.java.net/issues/show_bug.cgi?id=62">62</a>
* <a href="https://glazedlists.dev.java.net/issues/show_bug.cgi?id=66">66</a>
* <a href="https://glazedlists.dev.java.net/issues/show_bug.cgi?id=161">161</a>
* <a href="https://glazedlists.dev.java.net/issues/show_bug.cgi?id=170">170</a>
* <a href="https://glazedlists.dev.java.net/issues/show_bug.cgi?id=206">206</a>
* <a href="https://glazedlists.dev.java.net/issues/show_bug.cgi?id=239">239</a>
* <a href="https://glazedlists.dev.java.net/issues/show_bug.cgi?id=255">255</a>
* <a href="https://glazedlists.dev.java.net/issues/show_bug.cgi?id=261">261</a>
* </td></tr>
* </table>
*
* @author <a href="mailto:jesse@swank.ca">Jesse Wilson</a>
*/
public final class SortedList<E> extends TransformedList<E,E> {
private static final byte ALL_COLORS = 1;
private static final Element EMPTY_ELEMENT = null;
/**
* Sorting mode where elements are always in sorted order, even if this
* requires that elements be moved from one index to another when their
* value is changed.
*/
public static final int STRICT_SORT_ORDER = 0;
/**
* Sorting mode where elements aren't moved when their value is changed,
* even if this means they are no longer in perfect sorted order. This mode
* is useful in editable lists and tables because it is annoying
* for the current element to move if its value changes.
*/
public static final int AVOID_MOVING_ELEMENTS = 1;
/** a map from the unsorted index to the sorted index */
private SimpleTree<Element> unsorted = null;
/** a map from the sorted index to the unsorted index */
private SimpleTree<Element> sorted = null;
/** the comparator that this list uses for sorting */
private Comparator<? super E> comparator = null;
/** one of {@link #STRICT_SORT_ORDER} or {@link #AVOID_MOVING_ELEMENTS}. */
private int mode = STRICT_SORT_ORDER;
/**
* Creates a {@link SortedList} that sorts the specified {@link EventList}.
* All elements in the specified {@link EventList} must implement {@link Comparable}.
*
* @param source the {@link EventList} to be sorted
*/
public static <E extends Comparable<? super E>> SortedList<E> create(EventList<E> source) {
return new SortedList<E>(source);
}
/**
* Creates a {@link SortedList} that sorts the specified {@link EventList}.
* Because this constructor takes no {@link Comparator} argument, all
* elements in the specified {@link EventList} must implement {@link Comparable}
* or a {@link ClassCastException} will be thrown.
* <p>Usage of factory method {@link #create(EventList)} is preferable.
*
* @param source the {@link EventList} to be sorted
*/
public SortedList(EventList<E> source) {
this(source, (Comparator<E>)GlazedLists.comparableComparator());
}
/**
* Creates a {@link SortedList} that sorts the specified {@link EventList}
* using the specified {@link Comparator} to determine sort order. If the
* specified {@link Comparator} is <code>null</code>, then this {@link List}
* will be unsorted.
*/
public SortedList(EventList<E> source, Comparator<? super E> comparator) {
super(source);
setComparator(comparator);
source.addListEventListener(this);
}
/**
* Modify the behaviour of this {@link SortedList} to one of the predefined modes.
*
* @param mode either {@link #STRICT_SORT_ORDER} or {@link #AVOID_MOVING_ELEMENTS}.
*/
public void setMode(int mode) {
if(mode != STRICT_SORT_ORDER && mode != AVOID_MOVING_ELEMENTS) throw new IllegalArgumentException("Mode must be either SortedList.STRICT_SORT_ORDER or SortedList.AVOID_MOVING_ELEMENTS");
if(mode == this.mode) return;
// apply the new mode
this.mode = mode;
// we need to re-sort the table on the off-chance that an element
// was out of order before
if(this.mode == STRICT_SORT_ORDER) {
setComparator(getComparator());
}
}
/**
* Get the behaviour mode for this {@link SortedList}.
*
* @return one of {@link #STRICT_SORT_ORDER} (default) or
* {@link #AVOID_MOVING_ELEMENTS}.
*/
public int getMode() {
return this.mode;
}
/** {@inheritDoc} */
public void listChanged(ListEvent<E> listChanges) {
// handle reordering events
if(listChanges.isReordering()) {
int[] sourceReorder = listChanges.getReorderMap();
// remember what the mapping was before
int[] previousIndexToSortedIndex = new int[sorted.size()];
int index = 0;
for(SimpleTreeIterator<Element> i = new SimpleTreeIterator<Element>(sorted); i.hasNext(); index++) {
i.next();
Element<Element> unsortedNode = i.value();
int unsortedIndex = unsorted.indexOfNode(unsortedNode, ALL_COLORS);
previousIndexToSortedIndex[unsortedIndex] = index;
}
// adjust the from index for the source reorder
int[] newIndexToSortedIndex = new int[sorted.size()];
for(int i = 0; i < previousIndexToSortedIndex.length; i++) {
newIndexToSortedIndex[i] = previousIndexToSortedIndex[sourceReorder[i]];
}
// reorder the unsorted nodes to get the new sorted order
Element<Element>[] unsortedNodes = new Element[unsorted.size()];
index = 0;
for(SimpleTreeIterator<Element> i = new SimpleTreeIterator<Element>(unsorted); i.hasNext(); index++) {
i.next();
Element<Element> unsortedNode = i.node();
unsortedNodes[index] = unsortedNode;
}
Arrays.sort(unsortedNodes, sorted.getComparator());
// create a new reorder map to send the changes forward
int[] reorderMap = new int[sorted.size()];
boolean indexChanged = false;
index = 0;
for(SimpleTreeIterator<Element> i = new SimpleTreeIterator<Element>(sorted); i.hasNext(); index++) {
i.next();
Element<Element> sortedNode = i.node();
Element<Element> unsortedNode = unsortedNodes[index];
sortedNode.set(unsortedNode);
unsortedNode.set(sortedNode);
int unsortedIndex = unsorted.indexOfNode(unsortedNode, ALL_COLORS);
reorderMap[index] = newIndexToSortedIndex[unsortedIndex];
indexChanged = indexChanged || (index != reorderMap[index]);
}
// notify the world of the reordering
if(indexChanged) {
updates.beginEvent();
updates.reorder(reorderMap);
updates.commitEvent();
}
return;
}
// This is implemented in three phases. These phases are:
// 1. Update the unsorted tree for all event types. Update the sorted tree
// for delete events by deleting nodes. Fire delete events. Queue unsorted
// nodes for inserts and deletes in a list.
// 2. Fire update events by going through the updated nodes and testing
// whether they're still in sort order or if they need to be moved
// 3. Process queue of unsorted nodes for inserts. Fire insert events.
// This cycle is rather complex but necessarily so. The reason is that for
// the two-tree SortedList to function properly, there is a very strict order
// for how trees can be modified. The unsorted tree must be brought completely
// up-to-date before any access is made to the sorted tree. This ensures that
// the unsorted nodes can discover their indices properly. The sorted tree must
// have all deleted notes removed and updated nodes marked as unsorted
// before any nodes are inserted. This is because a deleted node may
// have a changed value that violates the sorted order in the tree. An
// insert in this case may compare against a violating node and result
// in inconsistency, even if the other node is eventually deleted.
// Therefore the order of operations above is essentially update
// the unsorted tree, delete from the sorted tree and finally insert into the
// sorted tree.
// all of these changes to this list happen "atomically"
updates.beginEvent();
// first update the offset tree for all changes, and keep the changed nodes in a list
LinkedList<Element> insertNodes = new LinkedList<Element>();
List<Element<Element>> updateNodes = new ArrayList<Element<Element>>();
List<E> previousValues = new ArrayList<E>();
// Update the indexed tree so it matches the source.
// Save the nodes to be inserted and updated as well
while(listChanges.next()) {
// get the current change info
int unsortedIndex = listChanges.getIndex();
int changeType = listChanges.getType();
// on insert, insert the index node
if(changeType == ListEvent.INSERT) {
Element<Element> unsortedNode = unsorted.add(unsortedIndex, EMPTY_ELEMENT, 1);
insertNodes.addLast(unsortedNode);
// on update, mark the updated node as unsorted and save it so it can be moved
} else if(changeType == ListEvent.UPDATE) {
Element<Element> unsortedNode = unsorted.get(unsortedIndex);
Element sortedNode = unsortedNode.get();
sortedNode.setSorted(Element.PENDING);
updateNodes.add(sortedNode);
previousValues.add(listChanges.getOldValue());
// on delete, delete the index and sorted node
} else if(changeType == ListEvent.DELETE) {
Element<Element> unsortedNode = unsorted.get(unsortedIndex);
E deleted = listChanges.getOldValue();
unsorted.remove(unsortedNode);
int deleteSortedIndex = deleteByUnsortedNode(unsortedNode);
updates.elementDeleted(deleteSortedIndex, deleted);
}
}
// decide which updated elements need to be shifted. We walk through the
// tree, marking updated elements as sorted or unsorted depending on their
// value relative to their neighbours
for(int i = 0, size = updateNodes.size(); i < size; i++) {
Element<Element> sortedNode = updateNodes.get(i);
// we may have already handled this via a neighbour
if(sortedNode.getSorted() != Element.PENDING) continue;
// find the bounds (by value) on this element. this is the last element
// preceeding current that's sorted and the first element after current
// that's sorted. If there's no such element (ie. the end of the list),
// then the bound element is null
Element lowerBound = null;
Element upperBound = null;
Element firstUnsortedNode = sortedNode;
for(Element leftNeighbour = sortedNode.previous(); leftNeighbour != null; leftNeighbour = leftNeighbour.previous()) {
if(leftNeighbour.getSorted() != Element.SORTED) {
firstUnsortedNode = leftNeighbour;
continue;
}
lowerBound = leftNeighbour;
break;
}
for(Element rightNeighbour = sortedNode.next(); rightNeighbour != null; rightNeighbour = rightNeighbour.next()) {
if(rightNeighbour.getSorted() != Element.SORTED) continue;
upperBound = rightNeighbour;
break;
}
// walk from the leader to the follower, marking elements as in sorted
// order or not. We simply compare them to our 2 potentially distant neighbours
// on either side - the lower and upper bounds
Comparator nodeComparator = sorted.getComparator();
for(Element current = firstUnsortedNode; current != upperBound; current = current.next()) {
// ensure we're less than the upper bound
if(upperBound != null && nodeComparator.compare(current.get(), upperBound.get()) > 0) {
current.setSorted(Element.UNSORTED);
continue;
}
// and greater than the lower bound
if(lowerBound != null && nodeComparator.compare(current.get(), lowerBound.get()) < 0) {
current.setSorted(Element.UNSORTED);
continue;
}
// so the node is sorted, and it's our new lower bound
current.setSorted(Element.SORTED);
lowerBound = current;
}
}
// fire update events
for(int i = 0, size = updateNodes.size(); i < size; i++) {
E previous = previousValues.get(i);
Element<Element> sortedNode = updateNodes.get(i);
assert(sortedNode.getSorted() != Element.PENDING);
int originalIndex = sorted.indexOfNode(sortedNode, ALL_COLORS);
// the element is still in sorted order, forward the update event
if(sortedNode.getSorted() == Element.SORTED) {
updates.elementUpdated(originalIndex, previous);
// sort order is not enforced so we lose perfect sorting order
// but we don't need to move elements around
} else if(mode == AVOID_MOVING_ELEMENTS) {
updates.elementUpdated(originalIndex, previous);
// sort order is enforced so move the element to its new location
} else {
sorted.remove(sortedNode);
updates.elementDeleted(originalIndex, previous);
int insertedIndex = insertByUnsortedNode(sortedNode.get());
updates.addInsert(insertedIndex);
}
}
// fire insert events
while(!insertNodes.isEmpty()) {
Element insertNode = insertNodes.removeFirst();
int insertedIndex = insertByUnsortedNode(insertNode);
updates.addInsert(insertedIndex);
}
// commit the changes and notify listeners
updates.commitEvent();
}
/**
* Inserts the specified unsorted node as the value in the sorted tree
* and returns the sorted order.
*
* @return the sortIndex of the inserted object.
*/
private int insertByUnsortedNode(Element unsortedNode) {
// add the object to the sorted set
Element<Element> sortedNode = sorted.addInSortedOrder(ALL_COLORS, unsortedNode, 1);
// assign the unsorted node the value of the sorted node
unsortedNode.set(sortedNode);
// return the sorted index
return sorted.indexOfNode(sortedNode, ALL_COLORS);
}
/**
* Deletes the node in the sorted tree based on the value of the specified
* unsorted tree node.
*
* @return the sortIndex of the deleted object.
*/
private int deleteByUnsortedNode(Element unsortedNode) {
// get the sorted node
Element sortedNode = (Element)unsortedNode.get();
// look up the sorted index before removing the nodes
int sortedIndex = sorted.indexOfNode(sortedNode, ALL_COLORS);
// delete the sorted node from its tree
sorted.remove(sortedIndex, 1);
// return the sorted index
return sortedIndex;
}
/** {@inheritDoc} */
protected int getSourceIndex(int mutationIndex) {
Element sortedNode = sorted.get(mutationIndex);
Element unsortedNode = (Element)sortedNode.get();
return unsorted.indexOfNode(unsortedNode, ALL_COLORS);
}
/** {@inheritDoc} */
protected boolean isWritable() {
return true;
}
/**
* Gets the {@link Comparator} that is being used to sort this list.
*
* @return the {@link Comparator} in use, or <tt>null</tt> if this list is
* currently unsorted. If this is an {@link EventList} of {@link Comparable}
* elements in natural order, then a ComparableComparator} will
* be returned.
*/
public Comparator<? super E> getComparator() {
return comparator;
}
/**
* Set the {@link Comparator} in use in this {@link EventList}. This will
* sort the source {@link EventList} into a new order.
*
* <p>Performance Note: sorting will take <code>O(N * Log N)</code> time.
*
* <p><strong><font color="#FF0000">Warning:</font></strong> This method is
* thread ready but not thread safe. See {@link EventList} for an example
* of thread safe code.
*
* @param comparator the {@link Comparator} to specify how to sort the list. If
* the source {@link EventList} elements implement {@link Comparable},
* you may use a {@link GlazedLists#comparableComparator()} to sort them
* in their natural order. You may also specify <code>null</code> to put
* this {@link SortedList} in unsorted order.
*/
public void setComparator(Comparator<? super E> comparator) {
// save this comparator
this.comparator = comparator;
// keep the old trees to construct the reordering
SimpleTree previousSorted = sorted;
// create the sorted list with a simple comparator
final Comparator treeComparator;
if(comparator != null) treeComparator = new ElementComparator(comparator);
else treeComparator = new ElementRawOrderComparator();
sorted = new SimpleTree<Element>(treeComparator);
// create a list which knows the offsets of the indexes to initialize this list
if(previousSorted == null && unsorted == null) {
unsorted = new SimpleTree<Element>();
// add all elements in the source list, in order
for(int i = 0, n = source.size(); i < n; i++) {
Element unsortedNode = unsorted.add(i, EMPTY_ELEMENT, 1);
insertByUnsortedNode(unsortedNode);
}
// this is the first sort so we're done
return;
}
// if the lists are empty, we're done
if(source.size() == 0) return;
// rebuild the sorted tree to reflect the new Comparator
for(SimpleTreeIterator<Element> i = new SimpleTreeIterator<Element>(unsorted); i.hasNext(); ) {
i.next();
Element unsortedNode = i.node();
insertByUnsortedNode(unsortedNode);
}
// construct the reorder map
int[] reorderMap = new int[size()];
int oldSortedIndex = 0;
for(SimpleTreeIterator<Element> i = new SimpleTreeIterator<Element>(previousSorted); i.hasNext(); oldSortedIndex++) {
i.next();
Element oldSortedNode = i.node();
Element unsortedNode = (Element)oldSortedNode.get();
Element newSortedNode = (Element)unsortedNode.get();
int newSortedIndex = sorted.indexOfNode(newSortedNode, ALL_COLORS);
reorderMap[newSortedIndex] = oldSortedIndex;
}
// notification about the big change
updates.beginEvent();
updates.reorder(reorderMap);
updates.commitEvent();
}
/** {@inheritDoc} */
public int indexOf(Object object) {
if(mode != STRICT_SORT_ORDER || comparator == null) return source.indexOf(object);
// use the fact that we have sorted data to quickly locate a position
// at which we can begin a linear search for an object that .equals(object)
int index = ((SimpleTree)sorted).indexOfValue(object, true, false, ALL_COLORS);
// if we couldn't use the comparator to find the index, return -1
if (index == -1) return -1;
// otherwise, we must now begin a linear search for the index of an element
// that .equals() the given object
for (; index < size(); index++) {
E objectAtIndex = get(index);
// if the objectAtIndex no longer compares equally with the given object, stop the linear search
if (comparator.compare((E)object, objectAtIndex) != 0) return -1;
// if the objectAtIndex and object are equal, return the index
if (GlazedListsImpl.equal(object, objectAtIndex))
return index;
}
// if we fall out of the loop we could not locate the object
return -1;
}
/** {@inheritDoc} */
public int lastIndexOf(Object object) {
if(mode != STRICT_SORT_ORDER || comparator == null) return source.lastIndexOf(object);
// use the fact that we have sorted data to quickly locate a position
// at which we can begin a linear search for an object that .equals(object)
int index = ((SimpleTree)sorted).indexOfValue(object, false, false, ALL_COLORS);
// if we couldn't use the comparator to find the index, return -1
if (index == -1) return -1;
// otherwise, we must now begin a linear search for the index of an element
// that .equals() the given object
for(; index > -1; index--) {
E objectAtIndex = get(index);
// if the objectAtIndex no longer compares equally with the given object, stop the linear search
if(comparator.compare((E)object, objectAtIndex) != 0) return -1;
// if the objectAtIndex and object are equal, return the index
if(GlazedListsImpl.equal(object, objectAtIndex))
return index;
}
// if we fall out of the loop we could not locate the object
return -1;
}
/**
* Returns the first index of the <code>object</code>'s sort location or
* the first index at which the <code>object</code> could be positioned if
* inserted.
*
* <p>Unlike {@link #indexOf} this method does not guarantee the given
* <code>object</code> {@link Object#equals(Object) equals} the element at
* the returned index. Instead, they are indistinguishable according to the
* sorting {@link Comparator}.
*
* @return a value in <tt>[0, size()]</tt> inclusive
*/
public int sortIndex(Object object) {
if (comparator == null)
throw new IllegalStateException("No Comparator exists to perform this operation");
return ((SimpleTree)sorted).indexOfValue(object, true, true, ALL_COLORS);
}
/**
* Returns the last index of the <code>object</code>'s sort location or
* the last index at which the <code>object</code> could be positioned if
* inserted.
*
* <p>Unlike {@link #lastIndexOf} this method does not guarantee the given
* <code>object</code> {@link Object#equals(Object) equals} the element at
* the returned index. Instead, they are indistinguishable according to the
* sorting {@link Comparator}.
*
* @return a value in <tt>[0, size()]</tt> inclusive
*/
public int lastSortIndex(Object object) {
if (comparator == null)
throw new IllegalStateException("No Comparator exists to perform this operation");
return ((SimpleTree)sorted).indexOfValue(object, false, true, ALL_COLORS);
}
/**
* Returns the index in this list of the first occurrence of the specified
* element, or the index where that element would be in the list if it were
* inserted.
*
* @return the index in this list of the first occurrence of the specified
* element, or the index where that element would be in the list if it
* were inserted. This will return a value in <tt>[0, size()]</tt>,
* inclusive.
*
* @deprecated Deprecated as of 12/11/2005. Replaced with {@link #sortIndex(Object)}
* which has cleaner semantics.
*/
public int indexOfSimulated(Object object) {
return comparator != null ? ((SimpleTree)sorted).indexOfValue(object, true, true, ALL_COLORS) : size();
}
/** {@inheritDoc} */
public boolean contains(Object object) {
return indexOf(object) != -1;
}
/**
* A comparator that takes an indexed node, and compares the value
* of an object in a list that has the index of that node.
*
* <p>If one of the objects passed to {@link #compare} is not an
* {@link Element}, it will compare the object directly to the object
* in the source {@link EventList} referenced by the {@link Element}.
* This functionality is necessary to allow use of the underlying
* {@link Comparator} within {@link SimpleTree} to support {@link List#indexOf},
* {@link List#lastIndexOf}, and {@link List#contains}.
*/
private class ElementComparator implements Comparator {
/** the actual comparator used on the values found */
private Comparator comparator;
/**
* Creates an {@link ElementComparator} that compares the
* objects in the source list based on the indexes of the tree
* nodes being compared.
*/
public ElementComparator(Comparator comparator) {
this.comparator = comparator;
}
/**
* Compares object alpha to object beta by using the source comparator.
*/
public int compare(Object alpha, Object beta) {
Object alphaObject = alpha;
Object betaObject = beta;
int alphaIndex = -1;
int betaIndex = -1;
if(alpha instanceof Element) {
Element alphaTreeNode = (Element)alpha;
alphaIndex = unsorted.indexOfNode(alphaTreeNode, ALL_COLORS);
alphaObject = source.get(alphaIndex);
}
if(beta instanceof Element) {
Element betaTreeNode = (Element)beta;
betaIndex = unsorted.indexOfNode(betaTreeNode, ALL_COLORS);
betaObject = source.get(betaIndex);
}
int result = comparator.compare(alphaObject, betaObject);
if(result != 0) return result;
if(alphaIndex != -1 && betaIndex != -1) return alphaIndex - betaIndex;
return 0;
}
}
/**
* A comparator that takes an indexed node, and compares the index of that node.
*/
private class ElementRawOrderComparator implements Comparator {
/**
* Compares the alpha object to the beta object by their indices.
*/
public int compare(Object alpha, Object beta) {
Element alphaTreeNode = (Element)alpha;
Element betaTreeNode = (Element)beta;
int alphaIndex = unsorted.indexOfNode(alphaTreeNode, ALL_COLORS);
int betaIndex = unsorted.indexOfNode(betaTreeNode, ALL_COLORS);
return alphaIndex - betaIndex;
}
}
/** {@inheritDoc} */
public Iterator<E> iterator() {
return new SortedListIterator();
}
/**
* The fast iterator for SortedList
*/
private class SortedListIterator implements Iterator<E> {
/** the SimpleTreeIterator to use to move across the tree */
private SimpleTreeIterator<Element> treeIterator = new SimpleTreeIterator<Element>(sorted);
/**
* Returns true iff there are more value to iterate on by caling next()
*/
public boolean hasNext() {
return treeIterator.hasNext();
}
/**
* Returns the next value in the iteration.
*/
public E next() {
treeIterator.next();
Element unsortedNode = treeIterator.value();
return source.get(unsorted.indexOfNode(unsortedNode, ALL_COLORS));
}
/**
* Removes the last value returned by this iterator.
*/
public void remove() {
int indexToRemove = treeIterator.index();
SortedList.this.source.remove(getSourceIndex(indexToRemove));
treeIterator = new SimpleTreeIterator(sorted, indexToRemove, ALL_COLORS);
}
}
}