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

ca.odell.glazedlists.impl.adt.Barcode Maven / Gradle / Ivy

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

import java.util.Iterator;

/**
 * A Barcode is an ADT to replace the more general CompressableList
 * ADT.  CompressableList provides list compression capabilites that allow
 * a list to be accessed by both the real index and a compressed index.
 * The compressed index corresponds to the index of the current value as
 * though no nulls existed in the list.
 *
 * 

This provides a huge performance boost over ArrayList on partially empty * lists. However, the CompressableList is one of the volatile implementation * classes for internal development and isn't the best structure for the current * usage. The GlazedLists use CompressableList to store only three values: * Boolean.TRUE, Boolean.FALSE, and null. As such, it was slower and more * memory intensive than it could be due to its general purpose design. * *

The Barcode is designed such that a list of n elements of the same * colour will contain at most one node for BLACK and no nodes for WHITE. * This will improve the performance and scalability of the GlazedLists * which currently make use of CompressableList. * *

Barcode does not support more than two values stored in the list. * Three different values are used by one of the GlazedLists at this time. * Until UniqueList is refactored to make use of only two values, Barcode * cannot completely replace CompressableList and they will exist in parallel. * *

In an effort to maximize performance this ADT does NOT validate that arguments * passed to methods are valid in any way. While this adds inherent risk to * the use of this code, this is a volatile implementation class. As such, it * should only be used for internal GlazedList development. It is up to the * calling code to do any argument validation which may be necessary. * *

Every effort has been made to squeeze the highest performance and smallest * footprint out of this data structure. These benefits hopefully don't come at * the cost of code clarity or maintainability. The memory usage of this ADT * is bound to the number of sequences of BLACK elements. WHITE elements * have no memory impact on the data structure. * * * @author Kevin Maltby * */ public final class Barcode { /** barcode colour constants */ public static final Object WHITE = Boolean.FALSE; public static final Object BLACK = Boolean.TRUE; /** the root of the underlying tree */ private BarcodeNode root = null; /** the size of the trailing whitespace */ private int whiteSpace = 0; /** the size of tree */ private int treeSize = 0; /** * Prints internal debug information for this barcode */ public void printDebug() { System.out.println("\nTotal Size: " + size()); System.out.println("Trailing Whitespace : " + whiteSpace); System.out.println("Tree Size: " + treeSize); System.out.println("Tree Structure:\n" + root); } /** * Validates the barcode's internal structure */ public void validate() { if(root != null) root.validate(); } /** * Gets the size of this barcode */ public int size() { return treeSize + whiteSpace; } /** * Whether or not this barcode is empty */ public boolean isEmpty() { return size() == 0; } /** * Gets the size of the white portion of this barcode */ public int whiteSize() { return root == null ? whiteSpace : root.whiteSize() + whiteSpace; } /** * Gets the size of the black portion of this barcode */ public int blackSize() { return root == null ? 0 : root.blackSize(); } /** * Gets the size of the given colour portion of this barcode */ public int colourSize(Object colour) { if(colour == WHITE) return whiteSize(); else return blackSize(); } /** * Inserts a sequence of the specified colour into the barcode */ public void add(int index, Object colour, int length) { if(colour == WHITE) addWhite(index, length); else addBlack(index, length); } /** * Inserts a sequence of white into the list */ public void addWhite(int index, int length) { if(length < 0) throw new IllegalStateException(); if(length == 0) return; // Adding to the trailing whitespace if(root == null || index >= treeSize) { whiteSpace += length; // Adding whitespace to the actual list } else { root.insertWhite(index, length); treeSizeChanged(); } } /** * Inserts a sequence of black into the list */ public void addBlack(int index, int length) { if(length < 0) throw new IllegalArgumentException(); if(length == 0) return; // Make a new root if(root == null) { root = new BarcodeNode(this, null, length, index); treeSize = index + length; whiteSpace -= index; // Add in the trailing whitespace } else if(index >= treeSize) { int movingWhitespace = index - treeSize; whiteSpace -= movingWhitespace; root.insertBlackAtEnd(length, movingWhitespace); treeSizeChanged(); // Add values to the actual list } else { root.insertBlack(index, length); treeSizeChanged(); } } /** * Gets the value in this list at the given index */ public Object get(int index) { if(getBlackIndex(index) == -1) return WHITE; return BLACK; } /** * Sets all of the values between index and index + length to either * WHITE or BLACK depending on the value of colour * * @param colour Determines which colour to set the values in the range * to. Valid values for colour are Barcode.WHITE and * Barcode.BLACK. */ public void set(int index, Object colour, int length) { if(length < 1) throw new IllegalArgumentException(); // The set affects the trailing whitespace int trailingChange = index > treeSize - 1 ? length : index + length - treeSize; if(trailingChange > 0) { if(colour == BLACK) { whiteSpace -= trailingChange; addBlack(index, trailingChange); } length -= trailingChange; if(length == 0) return; } // The set affects the list if(root != null) { root.set(index, colour, length); if(root != null) treeSizeChanged(); } } /** * Sets all of the values between index and index + length to WHITE */ public void setWhite(int index, int length) { set(index, WHITE, length); } /** * Sets all of the values between index and index + length to WHITE */ public void setBlack(int index, int length) { set(index, BLACK, length); } /** * Removes the values from the given index to index + length */ public void remove(int index, int length) { if(length < 1) throw new IllegalArgumentException(); // The remove affects the trailing whitespace int trailingChange = index > treeSize ? length : index + length - treeSize; if(trailingChange > 0) { whiteSpace -= trailingChange; length -= trailingChange; } // The remove occurs in the actual list if(root != null && index < treeSize) { int oldTreeSize = -1; while(length > 0) { oldTreeSize = treeSize; root.remove(index, length); if(root != null) treeSizeChanged(); length -= (oldTreeSize - treeSize); } if(root != null) treeSizeChanged(); } } /** * Clears the list */ public void clear() { treeSize = 0; whiteSpace = 0; root = null; } /** * Gets the root for this Barcode. This method is exposed for * Iterators on Barcode whose set() operations may create a * root node on a Barcode where none existed. */ BarcodeNode getRootNode() { return root; } /** * Sets the root for this list. This method is exposed for the * BarcodeNode in the event that the list's root is involved in * an AVL rotation. */ void setRootNode(BarcodeNode root) { this.root = root; if(root == null) treeSize = 0; } /** * Gets the size of the underlying tree structure for this Barcode. This * method is exposed for Iterators on Barcode who would otherwise have to * maintain the state of treeSize themselves. */ int treeSize() { return treeSize; } /** * Notifies the list that the underlying list size has changed. This method * is exposed for BarcodeNode to propagate size adjustments. */ void treeSizeChanged() { treeSize = root.size(); } /** * Gets the real index of an element given the black index or white index. */ public int getIndex(int colourIndex, Object colour) { // Get the real index of a WHITE element if(colour == WHITE) { // There are no black elements if(root == null) { return colourIndex; // Retrieving from the trailing whitespace with a tree } else if(colourIndex >= root.whiteSize()) { return colourIndex - root.whiteSize() + treeSize; // The index maps to an element in the tree } else { return root.getIndexByWhiteIndex(colourIndex); } // Get the real index of a BLACK element } else { return root.getIndexByBlackIndex(colourIndex); } } /** * Gets the colour-based index of the element with the given real * index. * * @param index the real index. * @param colour the colour to retrieve the colour-based index for. * * @return The colour-based index of the element at index or -1 if that * element does not match the given colour. */ public int getColourIndex(int index, Object colour) { if(colour == WHITE) return getWhiteIndex(index); else return getBlackIndex(index); } /** * Gets the white index of the node with the given real * index. * * @param index specifies the real index. * * @return The white index of the element at index or -1 if that element is BLACK. */ public int getWhiteIndex(int index) { // Get a white index from the list if(root != null && index < treeSize) return root.getWhiteIndex(index); // There are only white indexes in the trailing whitespace else { if(root != null) return index - treeSize + root.whiteSize(); else return index; } } /** * Gets the black index of the node with the given real * index. * * @param index specifies the real index. * * @return The black index of the element at index or -1 if that element is WHITE. */ public int getBlackIndex(int index) { if(root != null && index < treeSize) return root.getBlackIndex(index); else return -1; } /** * Gets the colour-based index of the element with the given real index or * the colour-based index of the previous or next element matching the given * colour if that element is of the opposite colour. * * @param left true for opposite colour elements to return the colour-based * index of the first matching element before it in the list. Such * values will range from -1 through size()-1. * False for opposite colour elements to return the colour-based index * of the first matching element after it in the list. Such values will * range from 0 through size(). */ public int getColourIndex(int index, boolean left, Object colour) { if(colour == WHITE) return getWhiteIndex(index, left); else return getBlackIndex(index, left); } /** * Gets the white index of the element with the given real index or * the white index of the previous or next WHITE element if that element * is BLACK. * * @param left true for BLACK elements to return the white index of the * first WHITE element before it in the list. Such values will range * from -1 through size()-1. False for BLACK * elements to return the white index of the first WHITE element after * it in the list. Such values will range from 0 through * size(). */ public int getWhiteIndex(int index, boolean left) { if(root == null) return index; else if(index >= treeSize) return index - treeSize + root.whiteSize(); else return root.getWhiteIndex(index, left); } /** * Gets the black index of the element with the given real index or * the black index of the previous or next BLACK element if that element * is WHITE. * * @param left true for WHITE elements to return the black index of the * first BLACK element before it in the list. Such values will range * from -1 through size()-1. False for WHITE * elements to return the black index of the first BLACK element after * it in the list. Such values will range from 0 through * size(). */ public int getBlackIndex(int index, boolean left) { // there is no tree if(root == null) { if(left) return -1; else return 0; // if it is beyond the tree } else if(index >= treeSize) { if(left) return root.blackSize() - 1; return root.blackSize(); // get from the tree } else { return root.getBlackIndex(index, left); } } /** * Gets the index of the WHITE element at whiteIndex relative to the WHITE * element after the previous BLACK element or the start of the list if no * BLACK element exists before this node. */ public int getWhiteSequenceIndex(int whiteIndex) { // There is no tree sequence is beyond the tree if(root == null) { return whiteIndex; // The sequence is beyond the tree } else if(whiteIndex >= root.whiteSize()) { return whiteIndex - root.whiteSize(); // lookup the sequence index within the tree } else { return root.getWhiteSequenceIndex(whiteIndex); } } /** * This method exists for CollectionList which needs a way to call * getBlackIndex(index, true) with a white-centric index. */ public int getBlackBeforeWhite(int whiteIndex) { // there is no tree if(root == null) { return -1; // starting from beyond the tree } else if(whiteIndex >= root.whiteSize()) { return root.blackSize() - 1; // the index is from within the tree } else { return root.getBlackBeforeWhite(whiteIndex); } } /** * Finds a sequence of the given colour that is at least size elements * in length. * * @param size the minimum size of a matching sequence. * * @return The natural index of the first element in the sequence or -1 if * no sequences of that length exist. */ public int findSequenceOfMinimumSize(int size, Object colour) { // there is no tree if(root == null) { // There are no black sequences if(colour == BLACK) return -1; // The trailing whitespace matches else if(whiteSpace >= size) return 0; // nothing matches else return -1; // focus only within the tree } else if(colour == BLACK) { return root.findSequenceOfMinimumSize(size, colour); // check the tree first, if it fails check the trailing whitespace } else { int result = root.findSequenceOfMinimumSize(size, colour); if(result == -1 && whiteSpace >= size) result = treeSize; return result; } } /** * Provides a specialized {@link Iterator} that iterates over a * {@link Barcode} to provide high performance access to {@link Barcode} * functionality. */ public BarcodeIterator iterator() { return new BarcodeIterator(this); } @Override public String toString() { StringBuffer result = new StringBuffer(); for(BarcodeIterator bi = iterator(); bi.hasNext(); ) { result.append(bi.next() == Barcode.BLACK ? "X" : "_"); } return result.toString(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy