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

com.jidesoft.range.CategoryRange Maven / Gradle / Ivy

There is a newer version: 3.6.18
Show newest version
/*
 * @(#)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