com.jidesoft.range.CategoryRange Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jide-oss Show documentation
Show all versions of jide-oss Show documentation
JIDE Common Layer (Professional Swing Components)
/*
* @(#)CategoryRange.java
*
* 2002 - 2012 JIDE Software Incorporated. All rights reserved.
* Copyright (c) 2005 - 2012 Catalysoft Limited. All rights reserved.
*/
package com.jidesoft.range;
import java.util.*;
/**
* Note that this class is iterable so you can use it in an advanced for.. loop
*
* @author Simon White ([email protected])
*/
public class CategoryRange extends AbstractRange implements Iterable> {
public static final String PROPERTY_VALUES = "values";
public static final String PROPERTY_COMPARATOR = "comparator";
public static final String PROPERTY_SORTED = "sorted";
private List _possibleValues = null;
private List> _categoryValues = null;
private Double minimum;
private Double maximum;
private Comparator comparator = null;
private boolean sorted = false;
// Private member variable to flag whether the possible values have been sorted or are in need of a sort
private boolean alreadySorted = false;
public CategoryRange() {
_possibleValues = new ArrayList();
_categoryValues = new ArrayList>();
alreadySorted = false;
}
/**
* Create a CategoryRange from the supplied values
*
* @param values the values.
*/
public CategoryRange(T... values) {
_possibleValues = new ArrayList();
_possibleValues.addAll(Arrays.asList(values));
alreadySorted = false;
}
/**
* Create a CategoryRange from a set of values. Note that internally, a list is created out of the set so that the
* class can reliably determine an upper()
and a lower()
value.
*
* @param values - the set of possible values
*/
public CategoryRange(Set values) {
_possibleValues = new ArrayList(values);
alreadySorted = false;
}
/**
* Create a new CategoryRange by copying an existing one. This would allow you subsequently to tweak the values in
* the copy without affecting the original.
*
* @param categoryRange the category range instance to copy
*/
public CategoryRange(CategoryRange categoryRange) {
_categoryValues = new ArrayList>(categoryRange.getCategoryValues());
_possibleValues = new ArrayList(categoryRange.getPossibleValues());
comparator = categoryRange.getComparator();
setMinimum(categoryRange.minimum());
setMaximum(categoryRange.maximum());
alreadySorted = false;
}
// When you call getCategoryValues() it will call this method and retrieve a list of possible values
// which is sorted if necessary
public List getPossibleValues() {
if (sorted && !alreadySorted) {
if (comparator == null) {
Comparator defaultComparator = new Comparator() {
public int compare(T o1, T o2) {
if (o1 == null && o2 == null) {
return 0;
} else if (o1 == null) {
return -1;
} else if (o2 == null) {
return 1;
} else {
// Use natural sort order if available
if (o1 instanceof Comparable) {
Comparable t1 = (Comparable) o1;
return t1.compareTo(o2);
} else {
// otherwise use the toString method to derive a string comparator
String s1 = o1.toString();
String s2 = o2.toString();
return s1.compareTo(s2);
}
}
}
};
Collections.sort(_possibleValues, defaultComparator);
} else {
Collections.sort(_possibleValues, comparator);
}
}
return _possibleValues;
}
/**
* Returns the category with the supplied position value. (Note that the first position is 1, not 0.)
*
* @param position the position of a category along an axis
* @return the category with the supplied position value.
*/
public Category getCategory(int position) {
if (position < 1) {
throw new IllegalArgumentException("Supplied category index was " + position + " but it should be >= 1");
}
return getCategoryValues().get(position - 1);
}
/**
* Adds a category to the range. Note that after adding categories, you will need to call reset() if you want the
* minimum and maximum numeric values of the range to be recomputed.
This method fires a property change
* event, but to avoid cloning a list for efficiency, the old value is always null
*
* @param c the category to add
* @return this range
*/
public CategoryRange add(Category c) {
if (!contains(c)) {
if (comparator == null) {
_possibleValues.add(c.getValue());
_categoryValues.add(c);
} else {
_possibleValues.add(c.getValue());
alreadySorted = false;
// Force the category values to be recomputed
_categoryValues = null;
}
c.setRange(this);
firePropertyChange(PROPERTY_VALUES, null, _possibleValues);
}
return this;
}
/**
* Specify whether the categories of the range should be sorted.
* If you call this method with true
but do not explicitly
* set a comparator for the sort ordering, then the natural ordering of the
* objects (using java.util.Comparable) will be used. If the objects do not
* implement Comparable, then a string comparator is constructed based on the
* toString() method of the object.
* @param sorted whether the categories of the range should be sorted
*/
public void setSorted(boolean sorted) {
boolean oldValue = this.sorted;
this.sorted = sorted;
// Force the category values to be recomputed
if (sorted) {
_categoryValues = null;
}
firePropertyChange(PROPERTY_SORTED, oldValue, sorted);
}
/**
* Returns a value to indicate whether the categories of the range are sorted
* @return a value to indicate whether the categories of the range are sorted
*/
public boolean isSorted() {
return this.sorted;
}
/**
* Returns the comparator that, if set, will be used to sort the values in the range
* @return the comparator that, if set, will be used to sort the values in the range
*/
public Comparator getComparator() {
return comparator;
}
/**
* Specify the comparator that will be used to sort the values in the range.
* Calling this method implicitly calls setSorted(): the sorted property will be set
* to true if the comparator is non-null and will be set to false if the comparator is null
* @param comparator the comparator to be used to sort the values in the range
*/
public void setComparator(Comparator comparator) {
Comparator oldValue = this.comparator;
this.comparator = comparator;
// This call will also force the category values to be recomputed
setSorted(comparator != null);
firePropertyChange(PROPERTY_COMPARATOR, oldValue, comparator);
}
@Override
public Range copy() {
return new CategoryRange(this);
}
// TODO: This assumes the possible values are sorted
public T lower() {
if (_possibleValues == null || _possibleValues.size() == 0) {
return null;
}
return _possibleValues.get(0);
}
// TODO: This assumes the possible values are sorted
public T upper() {
if (_possibleValues == null || _possibleValues.size() == 0) {
return null;
}
int numElements = _possibleValues.size();
return _possibleValues.get(numElements - 1); // get the last element
}
/**
* Not supported for Category Ranges
*/
public void adjust(T lower, T upper) {
throw new UnsupportedOperationException();
}
/**
* @return the maximum value for the axis in the range
*
* @see com.jidesoft.range.Range#maximum()
*/
public double maximum() {
if (maximum == null) {
T upper = upper();
if (upper == null) {
return 1.0;
}
else {
maximum = position(upper) + 1.0;
}
}
return maximum;
}
/**
* @return the minimum value for the axis in the range
*
* @see com.jidesoft.range.Range#minimum()
*/
public double minimum() {
if (minimum == null) {
T lower = lower();
if (lower == null) {
return 0.0;
}
else {
minimum = position(lower) - 1.0;
}
}
return minimum;
}
/**
* Reset the maximum and minimum. They will be recomputed on the next call to minimum() or maximum() respectively
*/
public void reset() {
maximum = null;
minimum = null;
}
public void setMinimum(double value) {
Double oldValue = this.minimum;
this.minimum = value;
firePropertyChange(PROPERTY_MIN, oldValue, value);
}
public void setMaximum(double value) {
Double oldValue = this.maximum;
this.maximum = value;
firePropertyChange(PROPERTY_MAX, oldValue, value);
}
/**
* Returns the size of the range, as given by the maximum minus the minimum. To compute the size of the range in
* terms of the number of members in the category, use getPossibleValue().size()
*
* @return the size of the range
*
* @see com.jidesoft.range.Range#size()
*/
public double size() {
if (_possibleValues == null) {
return 0;
}
int numElements = _possibleValues.size();
if (numElements == 0) {
return 0;
}
// The previous definition of size() prevented us from being able to zoom
// in on a categorical bar chart
return maximum() - minimum();
}
public int position(T value) {
int index = _possibleValues.indexOf(value);
if (index < 0) {
throw new IllegalArgumentException("Value " + value + " not known");
}
return 1 + index;
}
/**
* Determines whether the category range contains the supplied possible value
*/
public boolean contains(T x) {
if (x == null) {
return false;
}
else {
for (T category : _possibleValues) {
if (x.equals(category)) {
return true;
}
}
return false;
}
}
/**
* Determines whether the category range contains the specified category value
*
* @param value the category value.
* @return true if the range contains the specified value. Otherwise false.
*/
public boolean contains(Category value) {
if (value == null) {
return false;
}
else {
for (Category category : getCategoryValues()) {
if (value.equals(category)) {
return true;
}
}
return false;
}
}
/**
* Returns an iterator for the category values
* @return an iterator for the category values
*/
public Iterator> iterator() {
return getCategoryValues().iterator();
}
/**
* Returns a list of the category values in this range
* @return a list of category values
*/
public List> getCategoryValues() {
if (_categoryValues == null) {
_categoryValues = new ArrayList>();
for (T value : getPossibleValues()) {
_categoryValues.add(new Category(value, this));
}
}
return _categoryValues;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((_categoryValues == null) ? 0 : _categoryValues.hashCode());
result = prime * result + ((_possibleValues == null) ? 0 : _possibleValues.hashCode());
result = prime * result + ((maximum == null) ? 0 : maximum.hashCode());
result = prime * result + ((minimum == null) ? 0 : minimum.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
CategoryRange other = (CategoryRange) obj;
if (_categoryValues == null) {
if (other._categoryValues != null)
return false;
}
else if (!_categoryValues.equals(other._categoryValues))
return false;
if (_possibleValues == null) {
if (other._possibleValues != null)
return false;
}
else if (!_possibleValues.equals(other._possibleValues))
return false;
if (maximum == null) {
if (other.maximum != null)
return false;
}
else if (!maximum.equals(other.maximum))
return false;
if (minimum == null) {
if (other.minimum != null)
return false;
}
else if (!minimum.equals(other.minimum))
return false;
return true;
}
/**
* Creates an intermediate range between this range and a target range. Used for range morphing.
* @param target the target range of the morph
* @param position a value between 0 and 1 indicating the position of the morph
* @return a CategoryRange
*/
public Range createIntermediate(Range target, double position) {
double sourceMin = this.minimum();
double sourceMax = this.maximum();
double targetMin = target.minimum();
double targetMax = target.maximum();
double min = sourceMin + position * (targetMin - sourceMin);
double max= sourceMax + position * (targetMax - sourceMax);
CategoryRange r;
if (position < 0.5) {
r = new CategoryRange(this);
} else {
if (target instanceof CategoryRange) {
r = new CategoryRange((CategoryRange) target);
} else {
throw new IllegalArgumentException("Cannot create intermediate range from "+target.getClass());
}
}
r.setMinimum(min);
r.setMaximum(max);
return r;
}
public String toString() {
StringBuilder builder = new StringBuilder("#");
return builder.toString();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy