All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ca.odell.glazedlists.impl.event.Tree4Deltas Maven / Gradle / Ivy

The newest version!
/* Glazed Lists                                                 (c) 2003-2006 */
/* http://publicobject.com/glazedlists/                      publicobject.com,*/
/*                                                     O'Dell Engineering Ltd.*/
package ca.odell.glazedlists.impl.event;

import java.util.Arrays;

import ca.odell.glazedlists.event.ListEvent;
import ca.odell.glazedlists.impl.adt.barcode2.Element;
import ca.odell.glazedlists.impl.adt.barcode2.FourColorTree;
import ca.odell.glazedlists.impl.adt.barcode2.FourColorTreeIterator;
import ca.odell.glazedlists.impl.adt.barcode2.ListToByteCoder;

/**
 * Manage and describe the differences between two revisions of the
 * same List, assuming either one can change at any time.
 *
 * 

Initially, the source and target lists are equal. Over time, the * target list changes. It's also possible that the source list can change, * which is necessary for long-lived buffered changes. * * @author Jesse Wilson */ public class Tree4Deltas { /** all the names of the index sets are with respect to the target */ private static final ListToByteCoder BYTE_CODER = new ListToByteCoder(Arrays.asList("+", "U", "X", "_")); public static final byte INSERT = BYTE_CODER.colorToByte("+"); public static final byte UPDATE = BYTE_CODER.colorToByte("U"); public static final byte DELETE = BYTE_CODER.colorToByte("X"); public static final byte NO_CHANGE = BYTE_CODER.colorToByte("_"); private static final byte SOURCE_INDICES = BYTE_CODER.colorsToByte(Arrays.asList("U", "X", "_")); private static final byte TARGET_INDICES = BYTE_CODER.colorsToByte(Arrays.asList("U", "+", "_")); private static final byte ALL_INDICES = BYTE_CODER.colorsToByte(Arrays.asList("U", "X", "+", "_")); private static final byte CHANGE_INDICES = BYTE_CODER.colorsToByte(Arrays.asList("U", "X", "+")); /** the trees values include removed elements */ private FourColorTree tree = new FourColorTree(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 horribleHackPreferMostRecentValue = false; public boolean getAllowContradictingEvents() { return allowContradictingEvents; } public void setAllowContradictingEvents(boolean allowContradictingEvents) { this.allowContradictingEvents = allowContradictingEvents; } public int targetToSource(int targetIndex) { if(!initialCapacityKnown) ensureCapacity(targetIndex + 1); return tree.convertIndexColor(targetIndex, TARGET_INDICES, SOURCE_INDICES); } public int sourceToTarget(int sourceIndex) { if(!initialCapacityKnown) ensureCapacity(sourceIndex + 1); return tree.convertIndexColor(sourceIndex, SOURCE_INDICES, TARGET_INDICES); } /** *

We should consider removing the loop by only setting on removed elements. * * @param oldValue the previous value being replaced * @param newValue the new value * @param startIndex the first updated element, inclusive * @param endIndex the last index, exclusive */ public void targetUpdate(int startIndex, int endIndex, E oldValue, E newValue) { if(!initialCapacityKnown) ensureCapacity(endIndex); for(int i = startIndex; i < endIndex; i++) { int overallIndex = tree.convertIndexColor(i, TARGET_INDICES, ALL_INDICES); Element standingChangeToIndex = tree.get(overallIndex, ALL_INDICES); if(horribleHackPreferMostRecentValue) { byte newColor = standingChangeToIndex.getColor() == INSERT ? INSERT : UPDATE; tree.set(overallIndex, ALL_INDICES, newColor, oldValue, 1); continue; } // don't bother updating an inserted element if(standingChangeToIndex.getColor() == INSERT) { continue; } // if we're updating an update, the original replaced value stands. if(standingChangeToIndex.getColor() == UPDATE) { oldValue = standingChangeToIndex.get(); } // apply the update to our change description tree.set(overallIndex, ALL_INDICES, UPDATE, oldValue, 1); } } /** * Add a value to the target only. * *

Since this method takes a value parameter, is is only needed * when the target doesn't store its value, for example with buffered * changes. * * @param startIndex the first inserted element, inclusive * @param endIndex the last index, exclusive * @param newValue the inserted value */ public void targetInsert(int startIndex, int endIndex, E newValue) { if(!initialCapacityKnown) ensureCapacity(endIndex); tree.add(startIndex, TARGET_INDICES, INSERT, newValue, 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 * @param value the removed value */ public void targetDelete(int startIndex, int endIndex, E value) { if(!initialCapacityKnown) ensureCapacity(endIndex); for(int i = startIndex; i < endIndex; i++) { if(startIndex > 0 && startIndex > tree.size(TARGET_INDICES)) { throw new IllegalArgumentException(); } int overallIndex = tree.convertIndexColor(startIndex, TARGET_INDICES, ALL_INDICES); Element standingChangeToIndex = tree.get(overallIndex, ALL_INDICES); // if we're deleting an insert, remove that insert if(standingChangeToIndex.getColor() == INSERT) { if(!allowContradictingEvents) throw new IllegalStateException("Remove " + i + " undoes prior insert at the same index! Consider enabling contradicting events."); tree.remove(overallIndex, ALL_INDICES, 1); continue; } // if we're deleting an update, the original replaced value stands. if(standingChangeToIndex.getColor() == UPDATE) { value = standingChangeToIndex.get(); } tree.set(overallIndex, ALL_INDICES, DELETE, value, 1); } } public void sourceInsert(int sourceIndex) { tree.add(sourceIndex, SOURCE_INDICES, NO_CHANGE, ListEvent.unknownValue(), 1); } public void sourceDelete(int sourceIndex) { tree.remove(sourceIndex, SOURCE_INDICES, 1); } public void sourceRevert(int sourceIndex) { tree.set(sourceIndex, SOURCE_INDICES, NO_CHANGE, ListEvent.unknownValue(), 1); } public int targetSize() { return tree.size(TARGET_INDICES); } public int sourceSize() { return tree.size(SOURCE_INDICES); } public byte getChangeType(int sourceIndex) { return tree.get(sourceIndex, SOURCE_INDICES).getColor(); } /** * Get the value at the specified target index. * * @return the value, or {@link ListEvent#UNKNOWN_VALUE} if this index * holds a value that hasn't been buffered. In this case, the value * can be obtained from the source list. */ public E getTargetValue(int targetIndex) { return tree.get(targetIndex, TARGET_INDICES).get(); } public E getSourceValue(int sourceIndex) { return tree.get(sourceIndex, SOURCE_INDICES).get(); } public void reset(int size) { tree.clear(); initialCapacityKnown = true; ensureCapacity(size); } private void ensureCapacity(int size) { int currentSize = tree.size(TARGET_INDICES); int delta = size - currentSize; if(delta > 0) { int endOfTree = tree.size(ALL_INDICES); tree.add(endOfTree, ALL_INDICES, NO_CHANGE, ListEvent.unknownValue(), delta); } } /** * Add all the specified changes to this. */ public void addAll(BlockSequence blocks) { for(BlockSequence.Iterator i = blocks.iterator(); i.nextBlock(); ) { int blockStart = i.getBlockStart(); int blockEnd = i.getBlockEnd(); int type = i.getType(); E oldValue = i.getOldValue(); E newValue = i.getNewValue(); if(type == ListEvent.INSERT) { targetInsert(blockStart, blockEnd, newValue); } else if(type == ListEvent.UPDATE) { targetUpdate(blockStart, blockEnd, oldValue, newValue); } else if(type == ListEvent.DELETE) { targetDelete(blockStart, blockEnd, oldValue); } else { throw new IllegalStateException(); } } } /** * @return true if this event contains no changes. */ public boolean isEmpty() { return tree.size(CHANGE_INDICES) == 0; } public Iterator iterator() { return new Iterator(tree); } @Override 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 Iterator copy() { return new Iterator(tree, treeIterator.copy()); } public int getIndex() { return treeIterator.index(TARGET_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(TARGET_INDICES) + treeIterator.nodeSize(ALL_INDICES); } public int getType() { byte color = treeIterator.color(); if(color == INSERT) return ListEvent.INSERT; else if(color == UPDATE) return ListEvent.UPDATE; else if(color == DELETE) return ListEvent.DELETE; else throw new IllegalStateException(); } public boolean next() { if(!hasNext()) return false; treeIterator.next(CHANGE_INDICES); return true; } public boolean nextNode() { if(!hasNextNode()) return false; treeIterator.nextNode(CHANGE_INDICES); return true; } public boolean hasNext() { return treeIterator.hasNext(CHANGE_INDICES); } public boolean hasNextNode() { return treeIterator.hasNextNode(CHANGE_INDICES); } public E getOldValue() { return treeIterator.node().get(); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy