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