// c) Deleted events may be forwarded with a resultant DELETE or UPDATE
// event, depending on whether the deleted value was the last member of
// its group.
// a Barcode to track values that must be revisited to determine if they are UNIQUE or DUPLICATE
final Barcode toDoList = new Barcode();
toDoList.addWhite(0, barcode.size());
// first pass -> update the barcode and accumulate the type of values removed (UNIQUE or DUPLICATE or UNIQUE_WITH_DUPLICATE)
final LinkedList removedValues = new LinkedList();
while (listChanges.next()) {
final int changeIndex = listChanges.getIndex();
final int changeType = listChanges.getType();
if (changeType == ListEvent.INSERT) {
// assume the inserted element is unique until we determine otherwise
barcode.add(changeIndex, UNIQUE, 1);
// indicate we must revisit this index later to determine its uniqueness
toDoList.add(changeIndex, TODO, 1);
} else if (changeType == ListEvent.UPDATE) {
// case: AACCC -> AABCC
// if the updated index is a UNIQUE index, we MAY have just created a
// new group (by modifying an element in place). Consequently, we must
// mark the NEXT element as UNIQUE and revisit it later to determine
// if it really is
if (barcode.get(changeIndex) == UNIQUE) {
if (changeIndex+1 < barcode.size() && barcode.get(changeIndex+1) == DUPLICATE) {
barcode.set(changeIndex, UNIQUE, 2);
toDoList.set(changeIndex, TODO, 1);
}
}
} else if (changeType == ListEvent.DELETE) {
Object deleted = barcode.get(changeIndex);
barcode.remove(changeIndex, 1);
toDoList.remove(changeIndex, 1);
if (deleted == UNIQUE) {
// if the deleted UNIQUE value has a DUPLICATE, promote the DUPLICATE to be the new UNIQUE
if (changeIndex < barcode.size() && barcode.get(changeIndex) == DUPLICATE) {
// case: AABB -> AAB
barcode.set(changeIndex, UNIQUE, 1);
deleted = UNIQUE_WITH_DUPLICATE;
}
}
removedValues.addLast(deleted);
}
}
TryJoinResult<E> tryJoinResult = new TryJoinResult<E>();
// second pass, handle toDoList, update groupLists, and fire events
listChanges.reset();
while(listChanges.next()) {
final int changeIndex = listChanges.getIndex();
final int changeType = listChanges.getType();
E newValue = listChanges.getNewValue();
E oldValue = listChanges.getOldValue();
// inserts can result in UPDATE or INSERT events
if(changeType == ListEvent.INSERT) {
// if no group already exists to join, create a new group
tryJoinExistingGroup(changeIndex, toDoList, tryJoinResult);
if(tryJoinResult.group == NO_GROUP) {
client.groupChanged(changeIndex, tryJoinResult.groupIndex, ListEvent.INSERT, true, changeType, (E)ListEvent.UNKNOWN_VALUE, tryJoinResult.newFirstInGroup);
} else {
client.groupChanged(changeIndex, tryJoinResult.groupIndex, ListEvent.UPDATE, true, changeType, tryJoinResult.oldFirstInGroup, tryJoinResult.newFirstInGroup);
}
// updates can result in INSERT, UPDATE and DELETE events
} else if(changeType == ListEvent.UPDATE) {
// get the location of the group before the update occurred
int oldGroup = 0;
if(toDoList.get(changeIndex) == TODO) oldGroup = RIGHT_GROUP;
else if(barcode.get(changeIndex) == DUPLICATE) oldGroup = LEFT_GROUP;
else if(barcode.get(changeIndex) == UNIQUE) oldGroup = NO_GROUP;
// get the new group location
tryJoinExistingGroup(changeIndex, toDoList, tryJoinResult);