/* * @(#)CombinedNumericRange.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.ArrayList; import java.util.List; /** * A little convenience class to compute the maximum and minimum values of multiple ranges. * * @author swhite@catalysoft.com */ public class CombinedNumericRange extends AbstractNumericRange<Double> { private final Object monitor = new Object(); private List<Range<Double>> _ranges = new ArrayList<Range<Double>>(); private Double _max = null; private Double _min = null; /** * Using this constructor relies on the user subsequently calling add() to add a range */ public CombinedNumericRange() { super(); } /** * Add a new range to this combined range. Notice the method returns this instance, so method calls can be chained * together. If you pass null to this method the CombinedNumericRange remains unchanged; an Exception is NOT * thrown. * * @param range the new range to add * @return this instance */ public CombinedNumericRange add(Range<Double> range) { if (range == null) { return this; } synchronized (monitor) { _ranges.add(range); _min = null; _max = null; } return this; } /** * The lower value in the range; here, the same as <code>minimum</code> */ public Double lower() { return minimum(); } /** * The upper value in the range; here, the same as <code>maximum()</code> */ public Double upper() { return maximum(); } /** * Returns the number of sub-ranges contained by this range * * @return the number of sub-ranges in this range */ public int rangeCount() { return _ranges == null ? 0 : _ranges.size(); } /** * Calls to this method throw an UnsupportedOprationException. The idea is that in the case of this class we don't * want to be able to mess with the lower and upper bounds as they are computed from the supplied range values. If * the class needed to recompute the lower and upper bounds any previous adjustment that had been made through this * method would have been lost. * * @throws UnsupportedOperationException */ public void adjust(Double lower, Double upper) { throw new UnsupportedOperationException(); } /** * Lazily calculates the maximum value in the range */ public double maximum() { synchronized (monitor) { if (_max != null) { return _max; } if (_ranges == null || _ranges.size() == 0) { return Double.MAX_VALUE; } _max = -Double.MAX_VALUE; for (Range<Double> range : _ranges) { if (range != null && range.maximum() > _max) { _max = range.maximum(); } } return _max; } } /** * Lazily calculates the minimum value in the range */ public double minimum() { synchronized (monitor) { if (_min != null) { return _min; } if (_ranges == null || _ranges.size() == 0) { return -Double.MAX_VALUE; } _min = Double.MAX_VALUE; for (Range<Double> range : _ranges) { if (range != null && range.minimum() < _min) { _min = range.minimum(); } } return _min; } } /** * This range contains some point iff one or more of its sub-ranges contain that point */ public boolean contains(Double x) { synchronized (monitor) { if (x == null || _ranges.size() == 0) { return false; } else { for (Range<Double> range : _ranges) { if (range.contains(x)) { return true; } } return false; } } } /** * The size of the range is computed as the maximum minus the minimum value. */ public double size() { synchronized (monitor) { return maximum() - minimum(); } } /** * Returns a new numeric range that is based on this range, but with a margin introduced at each end. The margin * proportion is a value between 0 and 1. For example to add a 20% margin to each end use parameters of 0.2 for both * the leading and trailing margin proportion. * * @param leadingMarginProportion how much margin to add at the low end of the range * @param trailingMarginProportion how much margin to add at the top end of the range * @return a new NumericRange object with margins added */ public NumericRange getRange(double leadingMarginProportion, double trailingMarginProportion) { double maximum = maximum(); double minimum = minimum(); double difference = Math.abs(maximum - minimum); double leadingMargin = leadingMarginProportion * difference; double trailingMargin = trailingMarginProportion * difference; return new NumericRange(minimum - leadingMargin, maximum + trailingMargin); } @Override public Range<Double> createIntermediate(Range<Double> targetRange, double position) { double sourceMin = this.minimum(); double sourceMax = this.maximum(); double targetMin = targetRange.minimum(); double targetMax = targetRange.maximum(); double min = sourceMin + position * (targetMin - sourceMin); double max= sourceMax + position * (targetMax - sourceMax); return new NumericRange(min, max); } public String toString() { return String.format("#<CombinedNumericRange min=%s max=%s>", minimum(), maximum()); } }