ca.odell.glazedlists.ThresholdList 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;
// to use standard collections
import java.util.Comparator;
/**
* An {@link EventList} that shows a range of the elements of the source
* {@link EventList}. Each element in the source {@link EventList} is assigned
* an integer value via an {@link Evaluator}. This integer is used
* to determine whether the element fits in the {@link ThresholdList}s range.
*
* By modifying the upper and lower thresholds in the range, the list can
* be filtered in a simple and powerful way.
*
*
The {@link ThresholdList} lends itself to use with a slider widget for
* manipulating one of the range's endpoints.
*
*
One use case for {@link ThresholdList} is in a media player application.
* By creating a {@link Evaluator} for a song's bitrate, the user could
* limit results to MP3 files between 192 and 320kbps.
*
*
Note that the elements in the {@link ThresholdList} will be presented in
* order sorted by their {@link Evaluator} value.
*
*
This {@link EventList} supports all write operations.
*
*
Warning: This class
* breaks the contract required by {@link java.util.List}. See {@link EventList}
* for an example.
*
*
Warning: This class is
* thread ready but not thread safe. See {@link EventList} for an example
* of thread safe code.
*
*
* EventList Overview
* Writable: yes
* Concurrency: thread ready, not thread safe
* Performance: reads: O(log N), writes O(log N), change threshold O(log N)
* Memory: 72 bytes per element
* Unit Tests: N/A
* Issues:
* 47
* 137
* 217
* 218
* 246
* 277
*
*
*
* @author Kevin Maltby
*/
public final class ThresholdList extends RangeList {
/** the lower bound to use to define list containment */
private int lowerThreshold = Integer.MIN_VALUE;
/** the upper bound to use to define list containment */
private int upperThreshold = Integer.MAX_VALUE;
/** the evaluator to use to compare Objects against the threshold */
private Evaluator evaluator = null;
/** a sorted view of the source makes threshold operations really fast */
private final SortedList sortedSource;
/**
* Creates a {@link ThresholdList} that provides range-filtering based on the
* specified {@link EventList} based on the specified integer JavaBean property.
*/
public ThresholdList(EventList source, String propertyName) {
this(source, (Evaluator) GlazedLists.thresholdEvaluator(propertyName));
}
/**
* Creates a {@link ThresholdList} that provides range-filtering on the
* specified {@link EventList} using the specified {@link Evaluator}.
*/
public ThresholdList(EventList source, Evaluator evaluator) {
this(new SortedList(source, new ThresholdComparator(evaluator)), evaluator);
}
private ThresholdList(SortedList sortedSource, Evaluator evaluator) {
super(sortedSource);
this.sortedSource = sortedSource;
this.evaluator = evaluator;
}
/**
* Sets the lower threshold for this list to be the result of calling
* {@link Evaluator#evaluate(Object) evaluate()} on the given object.
*
* This list can be used programmatically rather than hooking it up to
* a UI component. Calling this method directly while this list
* is connected to a particular widget could result in errors.
*
*
Warning: This method is
* thread ready but not thread safe. See {@link EventList} for an example
* of thread safe code.
*/
public void setLowerThreshold(E object) {
setLowerThreshold(evaluator.evaluate(object));
}
/**
* Sets the lower threshold for this list.
*
*
This list can be used programmatically rather than hooking it up to
* a UI component. Calling this method directly while this list
* is connected to a particular widget could result in errors.
*
*
Warning: This method is
* thread ready but not thread safe. See {@link EventList} for an example
* of thread safe code.
*/
public void setLowerThreshold(int lowerThreshold) {
this.lowerThreshold = lowerThreshold;
adjustRange();
}
/**
* Gets the lower threshold for this list
*/
public int getLowerThreshold() {
return lowerThreshold;
}
/**
* Sets the upper threshold for this list to be the result of calling
* {@link Evaluator#evaluate(Object) evaluate()} on the given object.
*
*
This list can be used programmatically rather than hooking it up to
* a UI component. Calling this method directly while this list
* is connected to a particular widget could result in errors.
*
*
Warning: This method is
* thread ready but not thread safe. See {@link EventList} for an example
* of thread safe code.
*/
public void setUpperThreshold(E object) {
setUpperThreshold(evaluator.evaluate(object));
}
/**
* Sets the upper threshold for this list.
*
*
Warning: This method is
* thread ready but not thread safe. See {@link EventList} for an example
* of thread safe code.
*/
public void setUpperThreshold(int upperThreshold) {
this.upperThreshold = upperThreshold;
adjustRange();
}
/**
* Gets the upper threshold for this list
*/
public int getUpperThreshold() {
return upperThreshold;
}
/**
* A convenience method to allow access to the {@link Evaluator}
* that was provided on construction.
*/
public Evaluator getEvaluator() {
return evaluator;
}
/** {@inheritDoc} */
@Override
public boolean contains(Object object) {
// Fast fail if the object isn't within the thresholds
// Note: this technically breaks the contract for contains.
// evaluator.evaluate(object) may throw a ClassCastException
if(!withinRange((E)object)) return false;
return source.contains(object);
}
/** {@inheritDoc} */
@Override
public int indexOf(Object object) {
// Fast fail if the object isn't within the thresholds
// Note: this technically breaks the contract for indexOf.
// evaluator.evaluate(object) may throw a ClassCastException
if(!withinRange((E)object)) return -1;
return source.indexOf(object);
}
/** {@inheritDoc} */
@Override
public int lastIndexOf(Object object) {
// Fast fail if the object isn't within the thresholds
// Note: this technically breaks the contract for lastIndexOf.
// evaluator.evaluate(object) may throw a ClassCastException
if(!withinRange((E)object)) return -1;
return source.lastIndexOf(object);
}
/**
* Test if the specified object is within the range of this {@link ThresholdList}.
*/
private boolean withinRange(E object) {
int objectEvaluation = evaluator.evaluate(object);
return objectEvaluation >= lowerThreshold && objectEvaluation <= upperThreshold;
}
/** {@inheritDoc} */
@Override
public void setRange(int startIndex, int endIndex) {
// this implementation is slightly inconsistent with the superclass
// because the super treats endIndex as exclusive wheras we treat
// endIndex as inclusive
this.lowerThreshold = sourceIndexToThreshold(startIndex);
this.upperThreshold = sourceIndexToThreshold(endIndex);
adjustRange();
}
/** {@inheritDoc} */
@Override
public void setTailRange(int startIndex, int endIndex) {
// this implementation is slightly inconsistent with the superclass
// because the super treats endIndex as exclusive wheras we treat
// endIndex as inclusive
this.lowerThreshold = sourceIndexToThreshold(source.size() - startIndex);
this.upperThreshold = sourceIndexToThreshold(source.size() - endIndex);
adjustRange();
}
/**
* Given an index into the source {@link EventList}, get the
* threshold value for that index.
*/
private int sourceIndexToThreshold(int sourceIndex) {
if(sourceIndex < 0) {
return Integer.MIN_VALUE;
} else if(sourceIndex < source.size()) {
return evaluator.evaluate(source.get(sourceIndex));
} else {
return Integer.MIN_VALUE;
}
}
/** {@inheritDoc} */
@Override
public int getStartIndex() {
return sortedSource.sortIndex(new Integer(lowerThreshold));
}
/** {@inheritDoc} */
@Override
public int getEndIndex() {
// search for the upperThreshold value
int index = sortedSource.lastSortIndex(new Integer(upperThreshold));
// if the upperThreshold exists in the sortedSource, convert the exclusive index to an inclusive index
if (index < sortedSource.size() && evaluator.evaluate(sortedSource.get(index)) == upperThreshold)
index++;
return index;
}
/** {@inheritDoc} */
@Override
public void dispose() {
sortedSource.dispose();
super.dispose();
}
/**
* Provide an integer value for a given {@link Object} in a
* {@link ThresholdList}.
*/
public interface Evaluator {
/**
* Returns an integer value for an {@link Object} to be used to
* compare that object against a threshold. This value is
* not relative to any other object unlike a {@link Comparator}.
*/
public int evaluate(E object);
}
/**
* A ThresholdComparator is a simple helper class that wraps
* an {@link Evaluator} with a Comparator
to
* be used for sorting of the ThresholdList
.
*/
static final class ThresholdComparator implements Comparator {
/** the underlying evaluator */
private Evaluator evaluator = null;
/**
* Creates a new ThresholdComparator
*/
ThresholdComparator(Evaluator evaluator) {
this.evaluator = evaluator;
}
/**
* Compares two Object
s, and compares them using the result
* given when each Object
is evaluated using the underlying
* {@link Evaluator}.
*
* This method is dual-mode as in the case of the Objects passed being
* Integer
s, it returns the value of
* ((Integer)alpha).intValue() - ((Integer)beta).intValue()
.
* This is necessary so that a threshold value can be compared against an
* Object
, and vice versa. This can cause problems however
* if the underlying {@link Evaluator} were to return the negation
* of an Integer
.
*/
public int compare(E alpha, E beta) {
int alphaValue;
if(alpha instanceof Integer) alphaValue = ((Integer)alpha).intValue();
else alphaValue = evaluator.evaluate(alpha);
int betaValue;
if(beta instanceof Integer) betaValue = ((Integer)beta).intValue();
else betaValue = evaluator.evaluate(beta);
if(alphaValue > betaValue) return 1;
else if(alphaValue < betaValue) return -1;
else return 0;
}
/** {@inheritDoc} */
@Override
public boolean equals(Object o) {
if(this == o) return true;
if(o == null || getClass() != o.getClass()) return false;
final ThresholdComparator that = (ThresholdComparator) o;
if(!evaluator.equals(that.evaluator)) return false;
return true;
}
/** {@inheritDoc} */
@Override
public int hashCode() {
return evaluator.hashCode();
}
}
}