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

ca.odell.glazedlists.GroupingList Maven / Gradle / Ivy

There is a newer version: 1.9.1
Show newest version
/* Glazed Lists                                                 (c) 2003-2006 */
/* http://publicobject.com/glazedlists/                      publicobject.com,*/
/*                                                     O'Dell Engineering Ltd.*/
package ca.odell.glazedlists;

// the Glazed Lists' change objects
import ca.odell.glazedlists.event.*;
// volatile implementation support
import ca.odell.glazedlists.impl.adt.barcode2.SimpleTree;
import ca.odell.glazedlists.impl.adt.barcode2.Element;
import ca.odell.glazedlists.impl.Grouper;
// Java collections are used for underlying data storage
import java.util.*;

/**
 * A grouping list contains elements which are themselves Lists. Those Lists
 * are infact elements of the source list which have been grouped together into
 * a List. The logic of how to group the source elements into groups is specified
 * via a Comparator. Elements for which the Comparator returns 0 are guaranteed
 * to be contained within the same group within this GroupingList. This implies
 * that source elements may only participate in a single group within this
 * GroupingList.
 *
 * 

Further transformations may be layered on top of this GroupingList to * transform the group lists into any other desirable form. * *

* * * * * * * *
EventList Overview
Writable:yes
Concurrency:thread ready, not thread safe
Performance:reads: O(log N), writes O(log N)
Memory:
Unit Tests:GroupingListTest
Issues: * 281 *
* * @author James Lemieux */ public final class GroupingList extends TransformedList> { /** The GroupLists defined by the comparator. They are stored in an IndexedTree so their indices can be quickly updated. */ private SimpleTree groupLists = new SimpleTree(); /** the grouping service manages creating and deleting groups */ private final Grouper grouper; /** * Creates a {@link GroupingList} that determines groupings via the * {@link Comparable} interface which all elements of the source * are assumed to implement. */ public GroupingList(EventList source) { this(source, (Comparator) GlazedLists.comparableComparator()); } /** * Creates a {@link GroupingList} that determines groups using the specified * {@link Comparator}. * * @param source the {@link EventList} containing elements to be grouped * @param comparator the {@link Comparator} used to determine groupings */ public GroupingList(EventList source, Comparator comparator) { this(new SortedList(source, comparator), comparator, null); } /** * A private constructor which provides a convenient handle to the * {@link SortedList} which will serve as the source of this list. * * @param source the elements to be grouped arranged in sorted order * @param comparator the {@link Comparator} used to determine groupings * @param dummyParameter dummy parameter to differentiate between the different * {@link GroupingList} constructors. */ private GroupingList(SortedList source, Comparator comparator, Void dummyParameter) { super(source); // the grouper handles changes to the SortedList GrouperClient grouperClient = new GrouperClient(); this.grouper = new Grouper(source, grouperClient); // initialize the tree of GroupLists for(int i = 0; i < grouper.getBarcode().colourSize(Grouper.UNIQUE); i++) { grouperClient.insertGroupList(i); } source.addListEventListener(this); } /** * Handle changes to the grouping list groups. */ private class GrouperClient implements Grouper.Client { public void groupChanged(int index, int groupIndex, int groupChangeType, boolean primary, int elementChangeType) { if(groupChangeType == ListEvent.INSERT) { insertGroupList(groupIndex); updates.addInsert(groupIndex); } else if(groupChangeType == ListEvent.DELETE) { removeGroupList(groupIndex); updates.addDelete(groupIndex); } else if(groupChangeType == ListEvent.UPDATE) { updates.addUpdate(groupIndex); } else { throw new IllegalStateException(); } } /** * Creates and inserts a new GroupList at the specified * index. * * @param index the location at which to insert an empty GroupList */ private void insertGroupList(int index) { final GroupList groupList = new GroupList(); final Element indexedTreeNode = groupLists.add(index, groupList, 1); groupList.setTreeNode(indexedTreeNode); } /** * Removes the GroupList at the given index. * * @param index the location at which to remove a GroupList */ private void removeGroupList(int index) { final Element indexedTreeNode = groupLists.get(index); groupLists.remove(indexedTreeNode); // for safety, null out the GroupList's reference to its now defunct indexedTreeNode indexedTreeNode.get().setTreeNode(null); } } /** {@inheritDoc} */ public int size() { return grouper.getBarcode().colourSize(Grouper.UNIQUE); } /** {@inheritDoc} */ protected int getSourceIndex(int index) { return grouper.getBarcode().getIndex(index, Grouper.UNIQUE); } /** {@inheritDoc} */ protected boolean isWritable() { return true; } /** {@inheritDoc} */ public void listChanged(ListEvent listChanges) { updates.beginEvent(true); grouper.listChanged(listChanges); updates.commitEvent(); } public List get(int index) { return this.groupLists.get(index).get(); } /** {@inheritDoc} */ public List remove(int index) { if(index < 0 || index >= size()) throw new IndexOutOfBoundsException("Cannot remove at " + index + " on list of size " + size()); final List removed = (List)this.get(index); // make a copy of the list to return final List result = new ArrayList(removed); removed.clear(); return result; } /** {@inheritDoc} */ public List set(int index, List value) { if(index < 0 || index >= size()) throw new IndexOutOfBoundsException("Cannot set at " + index + " on list of size " + size()); updates.beginEvent(true); final List result = (List)this.remove(index); this.add(index, value); updates.commitEvent(); return result; } /** * This version of add will distribute all elements within the given * value List into groups. Existing groups will be reused and * new groups will be created as needed. As such, the index * argument is meaningless. * *

Warning: This method * breaks the contract required by {@link List#add(int, Object)}. */ public void add(int index, List value) { source.addAll(value); } /** {@inheritDoc} */ public void dispose() { ((SortedList) this.source).dispose(); super.dispose(); } /** * This is the List implementation used to store groups created by this * GroupingList. It defines all mutator methods by mapping them to mutations * on the source list of GroupList. Thus, writes to this GroupList effect * all Lists sitting under GroupList. */ private class GroupList extends AbstractList { /** The node within {@link groupLists} that records the index of this GroupList within the GroupingList. */ private Element treeNode; /** * Attach the IndexedTreeNode that tracks this GroupLists position to the * GroupList itself so it can look up its own position. */ private void setTreeNode(Element treeNode) { this.treeNode = treeNode; } /** * Returns the inclusive index of the start of this {@link GroupList} * within the source {@link SortedList}. */ private int getStartIndex() { if (treeNode == null) return -1; final int groupIndex = groupLists.indexOfNode(treeNode, (byte)1); return GroupingList.this.getSourceIndex(groupIndex); } /** * Returns the exclusive index of the end of this {@link GroupList} * within the source {@link SortedList}. */ private int getEndIndex() { if (treeNode == null) return -1; final int groupIndex = groupLists.indexOfNode(treeNode, (byte)1); // if this is before the end, its everything up to the first different element if(groupIndex < grouper.getBarcode().blackSize() - 1) { return grouper.getBarcode().getIndex(groupIndex + 1, Grouper.UNIQUE); // if this is at the end, its everything after } else { return grouper.getBarcode().size(); } } private int getSourceIndex(int index) { return this.getStartIndex() + index; } /** {@inheritDoc} */ public E set(int index, E element) { return source.set(this.getSourceIndex(index), element); } /** {@inheritDoc} */ public E get(int index) { return source.get(this.getSourceIndex(index)); } /** {@inheritDoc} */ public int size() { return this.getEndIndex() - this.getStartIndex(); } /** {@inheritDoc} */ public void clear() { source.subList(this.getStartIndex(), this.getEndIndex()).clear(); } /** {@inheritDoc} */ public E remove(int index) { return source.remove(this.getSourceIndex(index)); } /** {@inheritDoc} */ public void add(int index, E element) { source.add(this.getSourceIndex(index), element); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy