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

ca.odell.glazedlists.SequenceList 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;

import ca.odell.glazedlists.event.ListEvent;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.RandomAccess;

/**
 * A SequenceList contains values in adjacent indices which occur at predictable
 * intervals from each other. A simple SequenceList could be:
 * 
 {-10, -5, 0, 5, 10, 15} 
* * while a more sophisticated example could be: *
 {Jun 1, Jul 1, Aug 1, Sep 1, Oct 1} 
* * As long as the values can be ordered via a {@link Comparator} and a * {@link Sequencer} can be implemented to reliably produce the next or previous * value in a sequence using only some value from the source list. * *

SequenceList is a readonly list; calling any write method on this list * will produce an {@link UnsupportedOperationException}. * *

The start and end values of the sequence are the smallest sequence values * which maintain the invariant that: * sequence start <= each value in the source list <= sequence end * *

Warning: This class is * thread ready but not thread safe. See {@link EventList} for an example * of thread safe code. * *

* * * * * * * *
EventList Overview
Writable:no
Concurrency:thread ready, not thread safe
Performance:reads: O(1)
Memory:O(N)
Unit Tests:SequenceListTest
Issues:N/A
* * @author James Lemieux */ public final class SequenceList extends TransformedList implements RandomAccess { /** The values participating in the sequence. */ private final List sequence = new ArrayList(); /** The comparator that defines the order of the source and sequence values. */ private final Comparator comparator; /** * The object containing the logic which produces next and previous * sequence values by inspecting any source value. */ private final Sequencer sequencer; /** * Constructs a SequenceList containing a sequence of values produced by * the sequencer which cover the range of values contained * within the source. * * @param source the raw values to build a sequence around * @param sequencer the logic to produce sequence values relative to a value */ public SequenceList(EventList source, Sequencer sequencer) { this(source, sequencer, (Comparator) GlazedLists.comparableComparator()); } /** * Constructs a SequenceList containing a sequence of values produced by * the sequencer which cover the range of values contained * within the source. The given comparator * determines the order of the sequence values. * * @param source the raw values to build a sequence around * @param sequencer the logic to produce sequence values relative to a value * @param comparator determines the order of the sequence values */ public SequenceList(EventList source, Sequencer sequencer, Comparator comparator) { this(new SortedList(source, comparator), sequencer, comparator); } private SequenceList(SortedList source, Sequencer sequencer, Comparator comparator) { super(source); if (sequencer == null) throw new IllegalArgumentException("sequencer may not be null"); if (comparator == null) throw new IllegalArgumentException("comparator may not be null"); this.sequencer = sequencer; this.comparator = comparator; this.updateSequence(); source.addListEventListener(this); } /** * @return false; SequenceList is readonly */ @Override protected boolean isWritable() { return false; } /** {@inheritDoc} */ @Override public int size() { return this.sequence.size(); } /** {@inheritDoc} */ @Override public E get(int index) { return this.sequence.get(index); } /** * A Sequencer defines the logic required to calculate the previous and * next sequence values given any value. It is important to note that the * arguments passed to {@link #previous} and {@link #next} will not always * be sequence values themselves. For example if a Sequencer is contains * logic to produce a sequence of numbers evenly divisible by 2, it must * handle returning the next and previous even number relative to * any integer. So the Sequencer logic must produce: * *
    *
  • previous(5) returns 4 *
  • previous(6) returns 4 *
  • next(5) returns 6 *
  • next(4) returns 6 *
*/ public interface Sequencer { /** * Given a sequencable value, produce the previous value * in the sequence such that value is now included in the * sequence. * * @param value a sequencable value * @return the previous value in the sequence such that value * would be included within the bounds of the sequence */ public E previous(E value); /** * Given a sequencable value, produce the next value * in the sequence such that value is now included in the * sequence. * * @param value a sequencable value * @return the next value in the sequence such that value * would be included within the bounds of the sequence */ public E next(E value); } /** * Returns true if value is exactly a sequence value * (i.e. could be stored at some index within this {@link SequenceList}. */ private boolean isSequenceValue(E value) { final E sequencedValue = sequencer.previous(sequencer.next(value)); return comparator.compare(value, sequencedValue) == 0; } /** * Returns the previous value in the sequence defined by this list or * value itself if it is a sequence value. * * @param value the value relative to which the previous sequence value is returned * @return the previous sequence value relative to the given value */ public E getPreviousSequenceValue(E value) { // if value is already a sequence value, return it if (isSequenceValue(value)) return value; // ask the sequencer for the previous value return sequencer.previous(value); } /** * Returns the next value in the sequence defined by this list or * value itself if it is a sequence value. * * @param value the value relative to which the next sequence value is returned * @return the next sequence value relative to the given value */ public E getNextSequenceValue(E value) { // if value is already a sequence value, return it if (isSequenceValue(value)) return value; // ask the sequencer for the next value return sequencer.next(value); } /** {@inheritDoc} */ @Override public void listChanged(ListEvent listChanges) { this.updateSequence(); } /** * A convenience method to update the sequence to minimally cover the * underlying SortedList. */ private void updateSequence() { updates.beginEvent(); // check for the special case when the underlying list has been completely cleared if (source.isEmpty()) { while (!sequence.isEmpty()) { updates.elementDeleted(0, sequence.remove(0)); } } else { // seed this SequenceList with the initial two values if (this.isEmpty()) { final E value = source.get(0); final E previousSequenceValue = getPreviousSequenceValue(value); final E nextSequenceValue = getNextSequenceValue(value); sequence.add(0, previousSequenceValue); updates.elementInserted(0, previousSequenceValue); sequence.add(1, nextSequenceValue); updates.elementInserted(1, nextSequenceValue); } // add the necessary leading sequence values final E firstSourceValue = source.get(0); while (comparator.compare(firstSourceValue, get(0)) == -1) { E element = sequencer.previous(get(0)); sequence.add(0, element); updates.elementInserted(0, element); } // remove the unnecessary leading sequence values while (comparator.compare(get(1), firstSourceValue) == -1) { E oldValue = sequence.remove(0); updates.elementDeleted(0, oldValue); } // add the necessary trailing sequence values final E lastSourceValue = source.get(source.size()-1); while (comparator.compare(lastSourceValue, get(size()-1)) == 1) { E element = sequencer.next(get(size() - 1)); int index = size(); sequence.add(index, element); updates.elementInserted(index, element); } // remove the unnecessary trailing sequence values while (comparator.compare(get(size()-2), lastSourceValue) == 1) { final int lastIndex = size()-1; updates.elementDeleted(lastIndex, sequence.remove(lastIndex)); } } updates.commitEvent(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy