ca.odell.glazedlists.event.Tree4Deltas Maven / Gradle / Ivy
Show all versions of glazedlists_java15 Show documentation
/* Glazed Lists (c) 2003-2006 */
/* http://publicobject.com/glazedlists/ publicobject.com,*/
/* O'Dell Engineering Ltd.*/
package ca.odell.glazedlists.event;
import ca.odell.glazedlists.impl.adt.barcode2.*;
import java.util.Arrays;
/**
* Working copy of a class to eventually become a proper replacement for
* {@link ca.odell.glazedlists.event.Block}s.
*
* Test cases fail due to no copy constructor in {@link ca.odell.glazedlists.event.ListEvent}
* Logic to find appropriate index doing an extra layer of mapping isn't nice
* Clarify tree's rules regarding combining of nodes
* Provide special-case support for increasing event indices, such as
* those from FilterList.matcherChanged
*
* @author Jesse Wilson
*/
class Tree4Deltas {
private static final String LIST_CHANGE = "*";
private static final ListToByteCoder BYTE_CODER = new ListToByteCoder(Arrays.asList(new String[] { "+", "U", "X", "_" }));
private static final byte INSERT = Tree4Deltas.BYTE_CODER.colorToByte("+");
private static final byte UPDATE = Tree4Deltas.BYTE_CODER.colorToByte("U");
private static final byte DELETE = Tree4Deltas.BYTE_CODER.colorToByte("X");
private static final byte NO_CHANGE = Tree4Deltas.BYTE_CODER.colorToByte("_");
private static final byte SNAPSHOT_INDICES = Tree4Deltas.BYTE_CODER.colorsToByte(Arrays.asList(new String[] { "U", "X", "_" }));
private static final byte CURRENT_INDICES = Tree4Deltas.BYTE_CODER.colorsToByte(Arrays.asList(new String[] { "U", "+", "_" }));
private static final byte ALL_INDICES = Tree4Deltas.BYTE_CODER.colorsToByte(Arrays.asList(new String[] { "U", "X", "+", "_" }));
private static final byte CHANGE_INDICES = Tree4Deltas.BYTE_CODER.colorsToByte(Arrays.asList(new String[] { "U", "X", "+" }));
private FourColorTree tree = new FourColorTree(Tree4Deltas.BYTE_CODER);
private boolean allowContradictingEvents = false;
/**
* When the first change to a list happens, we need to guess what the list's
* capacity is. After that change, we reliably know the list's capacity, so
* we don't need to keep testing the capacity one index at a time.
*/
private boolean initialCapacityKnown = false;
public boolean getAllowContradictingEvents() {
return allowContradictingEvents;
}
public void setAllowContradictingEvents(boolean allowContradictingEvents) {
this.allowContradictingEvents = allowContradictingEvents;
}
public int currentToSnapshot(int currentIndex) {
if(!initialCapacityKnown) ensureCapacity(currentIndex + 1);
return tree.convertIndexColor(currentIndex, Tree4Deltas.CURRENT_INDICES, Tree4Deltas.SNAPSHOT_INDICES);
}
public int snapshotToCurrent(int snapshotIndex) {
if(!initialCapacityKnown) ensureCapacity(snapshotIndex + 1);
return tree.convertIndexColor(snapshotIndex, Tree4Deltas.SNAPSHOT_INDICES, Tree4Deltas.CURRENT_INDICES);
}
/**
* We should consider removing the loop by only setting on removed elements.
*
* @param startIndex the first updated element, inclusive
* @param endIndex the last index, exclusive
*/
public void update(int startIndex, int endIndex) {
if(!initialCapacityKnown) ensureCapacity(endIndex);
for(int i = startIndex; i < endIndex; i++) {
int overallIndex = tree.convertIndexColor(i, Tree4Deltas.CURRENT_INDICES, Tree4Deltas.ALL_INDICES);
// don't bother updating an inserted element
if(tree.get(overallIndex, Tree4Deltas.ALL_INDICES).getColor() == Tree4Deltas.INSERT) return;
tree.set(overallIndex, Tree4Deltas.ALL_INDICES, Tree4Deltas.UPDATE, Tree4Deltas.LIST_CHANGE, 1);
}
}
/**
* @param startIndex the first inserted element, inclusive
* @param endIndex the last index, exclusive
*/
public void insert(int startIndex, int endIndex) {
if(!initialCapacityKnown) ensureCapacity(endIndex);
tree.add(startIndex, Tree4Deltas.CURRENT_INDICES, Tree4Deltas.INSERT, Tree4Deltas.LIST_CHANGE, endIndex - startIndex);
}
/**
*
We should consider removing the loop from this method by counting
* the inserted elements between startIndex and endIndex, removing those,
* then removing everything else...
*
* @param startIndex the index of the first element to remove
* @param endIndex the last index, exclusive
*/
public void delete(int startIndex, int endIndex) {
if(!initialCapacityKnown) ensureCapacity(endIndex);
for(int i = startIndex; i < endIndex; i++) {
int overallIndex = tree.convertIndexColor(startIndex, Tree4Deltas.CURRENT_INDICES, Tree4Deltas.ALL_INDICES);
// if its an insert, simply remove that insert
if(tree.get(overallIndex, Tree4Deltas.ALL_INDICES).getColor() == Tree4Deltas.INSERT) {
if(!allowContradictingEvents) throw new IllegalStateException("Remove " + i + " undoes prior insert at the same index! Consider enabling contradicting events.");
tree.remove(overallIndex, Tree4Deltas.ALL_INDICES, 1);
// otherwise apply the delete
} else {
tree.set(overallIndex, Tree4Deltas.ALL_INDICES, Tree4Deltas.DELETE, Tree4Deltas.LIST_CHANGE, 1);
}
}
}
public int currentSize() {
return tree.size(Tree4Deltas.CURRENT_INDICES);
}
public int snapshotSize() {
return tree.size(Tree4Deltas.SNAPSHOT_INDICES);
}
public void reset(int size) {
tree.clear();
initialCapacityKnown = true;
ensureCapacity(size);
}
private void ensureCapacity(int size) {
int currentSize = tree.size(Tree4Deltas.CURRENT_INDICES);
int delta = size - currentSize;
if(delta > 0) {
int endOfTree = tree.size(Tree4Deltas.ALL_INDICES);
tree.add(endOfTree, Tree4Deltas.ALL_INDICES, Tree4Deltas.NO_CHANGE, Tree4Deltas.LIST_CHANGE, delta);
}
}
/**
* Add all the specified changes to this.
*/
void addAll(BlockSequence blocks) {
for(BlockSequence.Iterator i = blocks.iterator(); i.nextBlock(); ) {
int blockStart = i.getBlockStart();
int blockEnd = i.getBlockEnd();
int type = i.getType();
if(type == ListEvent.INSERT) {
insert(blockStart, blockEnd);
} else if(type == ListEvent.UPDATE) {
update(blockStart, blockEnd);
} else if(type == ListEvent.DELETE) {
delete(blockStart, blockEnd);
} else {
throw new IllegalStateException();
}
}
}
/**
* @return true
if this event contains no changes.
*/
public boolean isEmpty() {
return tree.size(Tree4Deltas.CHANGE_INDICES) == 0;
}
public Tree4Deltas.Iterator iterator() {
return new Tree4Deltas.Iterator(tree);
}
public String toString() {
return tree.asSequenceOfColors();
}
/**
* Iterate through the list of changes in this tree.
*/
public static class Iterator {
private final FourColorTree tree;
private final FourColorTreeIterator treeIterator;
private Iterator(FourColorTree tree) {
this.tree = tree;
this.treeIterator = new FourColorTreeIterator(tree);
}
private Iterator(FourColorTree tree, FourColorTreeIterator treeIterator) {
this.tree = tree;
this.treeIterator = treeIterator;
}
public Tree4Deltas.Iterator copy() {
return new Tree4Deltas.Iterator(tree, treeIterator.copy());
}
public int getIndex() {
return treeIterator.index(Tree4Deltas.CURRENT_INDICES);
}
public int getEndIndex() {
// this is peculiar. We add mixed types - an index of current indices
// plus the size of "all indices". . . this is because we describe the
// range of deleted indices from its start to finish, although it's
// finish will ultimately go to zero once the change is applied.
return treeIterator.nodeStartIndex(Tree4Deltas.CURRENT_INDICES) + treeIterator.nodeSize(Tree4Deltas.ALL_INDICES);
}
public int getType() {
byte color = treeIterator.color();
if(color == Tree4Deltas.INSERT) return ListEvent.INSERT;
else if(color == Tree4Deltas.UPDATE) return ListEvent.UPDATE;
else if(color == Tree4Deltas.DELETE) return ListEvent.DELETE;
else throw new IllegalStateException();
}
public boolean next() {
if(!hasNext()) return false;
treeIterator.next(Tree4Deltas.CHANGE_INDICES);
return true;
}
public boolean nextNode() {
if(!hasNextNode()) return false;
treeIterator.nextNode(Tree4Deltas.CHANGE_INDICES);
return true;
}
public boolean hasNext() {
return treeIterator.hasNext(Tree4Deltas.CHANGE_INDICES);
}
public boolean hasNextNode() {
return treeIterator.hasNextNode(Tree4Deltas.CHANGE_INDICES);
}
}
}