Mega Code Archive

 
Categories / Java / Collections Data Structure
 

NumberRange represents an inclusive range of java lang Number objects of the same type

/*  * Licensed to the Apache Software Foundation (ASF) under one or more  * contributor license agreements.  See the NOTICE file distributed with  * this work for additional information regarding copyright ownership.  * The ASF licenses this file to You under the Apache License, Version 2.0  * (the "License"); you may not use this file except in compliance with  * the License.  You may obtain a copy of the License at  *   *      http://www.apache.org/licenses/LICENSE-2.0  *   * Unless required by applicable law or agreed to in writing, software  * distributed under the License is distributed on an "AS IS" BASIS,  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  * See the License for the specific language governing permissions and  * limitations under the License.  */ import java.io.Serializable; /**  * <p><code>NumberRange</code> represents an inclusive range of   * {@link java.lang.Number} objects of the same type.</p>  *  * @author <a href="mailto:chrise@esha.com">Christopher Elkins</a>  * @author Stephen Colebourne  * @since 2.0 (previously in org.apache.commons.lang)  * @version $Id: NumberRange.java 437554 2006-08-28 06:21:41Z bayard $  */ public final class NumberRange extends Range implements Serializable {          /**      * Required for serialization support.      *       * @see java.io.Serializable      */     private static final long serialVersionUID = 71849363892710L;     /**      * The minimum number in this range.      */     private final Number min;     /**      * The maximum number in this range.      */     private final Number max;          /**      * Cached output hashCode (class is immutable).      */     private transient int hashCode = 0;     /**      * Cached output toString (class is immutable).      */     private transient String toString = null;     /**      * <p>Constructs a new <code>NumberRange</code> using the specified      * number as both the minimum and maximum in this range.</p>      *      * @param num the number to use for this range      * @throws IllegalArgumentException if the number is <code>null</code>      * @throws IllegalArgumentException if the number doesn't implement <code>Comparable</code>      * @throws IllegalArgumentException if the number is <code>Double.NaN</code> or <code>Float.NaN</code>      */     public NumberRange(Number num) {         if (num == null) {             throw new IllegalArgumentException("The number must not be null");         }         if (num instanceof Comparable == false) {             throw new IllegalArgumentException("The number must implement Comparable");         }         if (num instanceof Double && ((Double) num).isNaN()) {             throw new IllegalArgumentException("The number must not be NaN");         }         if (num instanceof Float && ((Float) num).isNaN()) {             throw new IllegalArgumentException("The number must not be NaN");         }         this.min = num;         this.max = num;     }     /**      * <p>Constructs a new <code>NumberRange</code> with the specified      * minimum and maximum numbers (both inclusive).</p>      *       * <p>The arguments may be passed in the order (min,max) or (max,min). The      * {@link #getMinimumNumber()} and {@link #getMaximumNumber()} methods will return the      * correct value.</p>      *       * <p>This constructor is designed to be used with two <code>Number</code>      * objects of the same type. If two objects of different types are passed in,      * an exception is thrown.</p>      *      * @param num1  first number that defines the edge of the range, inclusive      * @param num2  second number that defines the edge of the range, inclusive      * @throws IllegalArgumentException if either number is <code>null</code>      * @throws IllegalArgumentException if the numbers are of different types      * @throws IllegalArgumentException if the numbers don't implement <code>Comparable</code>      */     public NumberRange(Number num1, Number num2) {         if (num1 == null || num2 == null) {             throw new IllegalArgumentException("The numbers must not be null");         }         if (num1.getClass() != num2.getClass()) {             throw new IllegalArgumentException("The numbers must be of the same type");         }         if (num1 instanceof Comparable == false) {             throw new IllegalArgumentException("The numbers must implement Comparable");         }         if (num1 instanceof Double) {             if (((Double) num1).isNaN() || ((Double) num2).isNaN()) {                 throw new IllegalArgumentException("The number must not be NaN");             }         } else if (num1 instanceof Float) {             if (((Float) num1).isNaN() || ((Float) num2).isNaN()) {                 throw new IllegalArgumentException("The number must not be NaN");             }         }                  int compare = ((Comparable) num1).compareTo(num2);         if (compare == 0) {             this.min = num1;             this.max = num1;         } else if (compare > 0) {             this.min = num2;             this.max = num1;         } else {             this.min = num1;             this.max = num2;         }     }          // Accessors     //--------------------------------------------------------------------     /**      * <p>Returns the minimum number in this range.</p>      *      * @return the minimum number in this range      */     public Number getMinimumNumber() {         return min;     }     /**      * <p>Returns the maximum number in this range.</p>      *      * @return the maximum number in this range      */     public Number getMaximumNumber() {         return max;     }     // Tests     //--------------------------------------------------------------------          /**      * <p>Tests whether the specified <code>number</code> occurs within      * this range.</p>      *       * <p><code>null</code> is handled and returns <code>false</code>.</p>      *      * @param number  the number to test, may be <code>null</code>      * @return <code>true</code> if the specified number occurs within this range      * @throws IllegalArgumentException if the number is of a different type to the range      */     public boolean containsNumber(Number number) {         if (number == null) {             return false;         }         if (number.getClass() != min.getClass()) {             throw new IllegalArgumentException("The number must be of the same type as the range numbers");         }         int compareMin = ((Comparable) min).compareTo(number);         int compareMax = ((Comparable) max).compareTo(number);         return compareMin <= 0 && compareMax >= 0;     }     // Range tests     //--------------------------------------------------------------------     // use Range implementations     // Basics     //--------------------------------------------------------------------     /**      * <p>Compares this range to another object to test if they are equal.</p>.      *       * <p>To be equal, the class, minimum and maximum must be equal.</p>      *      * @param obj the reference object with which to compare      * @return <code>true</code> if this object is equal      */     public boolean equals(Object obj) {         if (obj == this) {             return true;         }         if (obj instanceof NumberRange == false) {             return false;         }         NumberRange range = (NumberRange) obj;         return min.equals(range.min) && max.equals(range.max);     }     /**      * <p>Gets a hashCode for the range.</p>      *      * @return a hash code value for this object      */     public int hashCode() {         if (hashCode == 0) {             hashCode = 17;             hashCode = 37 * hashCode + getClass().hashCode();             hashCode = 37 * hashCode + min.hashCode();             hashCode = 37 * hashCode + max.hashCode();         }         return hashCode;     }     /**      * <p>Gets the range as a <code>String</code>.</p>      *      * <p>The format of the String is 'Range[<i>min</i>,<i>max</i>]'.</p>      *      * @return the <code>String</code> representation of this range      */     public String toString() {         if (toString == null) {             StringBuffer buf = new StringBuffer(32);             buf.append("Range[");             buf.append(min);             buf.append(',');             buf.append(max);             buf.append(']');             toString = buf.toString();         }         return toString;     } } /*  * Licensed to the Apache Software Foundation (ASF) under one or more  * contributor license agreements.  See the NOTICE file distributed with  * this work for additional information regarding copyright ownership.  * The ASF licenses this file to You under the Apache License, Version 2.0  * (the "License"); you may not use this file except in compliance with  * the License.  You may obtain a copy of the License at  *   *      http://www.apache.org/licenses/LICENSE-2.0  *   * Unless required by applicable law or agreed to in writing, software  * distributed under the License is distributed on an "AS IS" BASIS,  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  * See the License for the specific language governing permissions and  * limitations under the License.  */ /**  * <p><code>Range</code> represents a range of numbers of the same type.</p>  *   * <p>Specific subclasses hold the range values as different types. Each  * subclass should be immutable and {@link java.io.Serializable Serializable}  * if possible.</p>  *  * @author Stephen Colebourne  * @since 2.0  * @version $Id: Range.java 437554 2006-08-28 06:21:41Z bayard $  */ abstract class Range {     /**      * <p>Constructs a new range.</p>      */     public Range() {         super();     }     // Accessors     //--------------------------------------------------------------------     /**      * <p>Gets the minimum number in this range.</p>      *      * @return the minimum number in this range      */     public abstract Number getMinimumNumber();     /**      * <p>Gets the minimum number in this range as a <code>long</code>.</p>      *       * <p>This implementation uses the {@link #getMinimumNumber()} method.       * Subclasses may be able to optimise this.</p>      *      * @return the minimum number in this range      */     public long getMinimumLong() {         return getMinimumNumber().longValue();     }     /**      * <p>Gets the minimum number in this range as a <code>int</code>.</p>      *       * <p>This implementation uses the {@link #getMinimumNumber()} method.       * Subclasses may be able to optimise this.</p>      *      * @return the minimum number in this range      */     public int getMinimumInteger() {         return getMinimumNumber().intValue();     }     /**      * <p>Gets the minimum number in this range as a <code>double</code>.</p>      *       * <p>This implementation uses the {@link #getMinimumNumber()} method.       * Subclasses may be able to optimise this.</p>      *      * @return the minimum number in this range      */     public double getMinimumDouble() {         return getMinimumNumber().doubleValue();     }     /**      * <p>Gets the minimum number in this range as a <code>float</code>.</p>      *       * <p>This implementation uses the {@link #getMinimumNumber()} method.       * Subclasses may be able to optimise this.</p>      *      * @return the minimum number in this range      */     public float getMinimumFloat() {         return getMinimumNumber().floatValue();     }     /**      * <p>Gets the maximum number in this range.</p>      *      * @return the maximum number in this range      */     public abstract Number getMaximumNumber();     /**      * <p>Gets the maximum number in this range as a <code>long</code>.</p>      *       * <p>This implementation uses the {@link #getMaximumNumber()} method.       * Subclasses may be able to optimise this.</p>      *      * @return the maximum number in this range      */     public long getMaximumLong() {         return getMaximumNumber().longValue();     }     /**      * <p>Gets the maximum number in this range as a <code>int</code>.</p>      *       * <p>This implementation uses the {@link #getMaximumNumber()} method.       * Subclasses may be able to optimise this.</p>      *      * @return the maximum number in this range      */     public int getMaximumInteger() {         return getMaximumNumber().intValue();     }     /**      * <p>Gets the maximum number in this range as a <code>double</code>.</p>      *       * <p>This implementation uses the {@link #getMaximumNumber()} method.       * Subclasses may be able to optimise this.</p>      *      * @return the maximum number in this range      */     public double getMaximumDouble() {         return getMaximumNumber().doubleValue();     }     /**      * <p>Gets the maximum number in this range as a <code>float</code>.</p>      *       * <p>This implementation uses the {@link #getMaximumNumber()} method.       * Subclasses may be able to optimise this.</p>      *      * @return the maximum number in this range      */     public float getMaximumFloat() {         return getMaximumNumber().floatValue();     }     // Include tests     //--------------------------------------------------------------------          /**      * <p>Tests whether the specified <code>Number</code> occurs within      * this range.</p>      *       * <p>The exact comparison implementation varies by subclass. It is      * intended that an <code>int</code> specific subclass will compare using      * <code>int</code> comparison.</p>      *       * <p><code>null</code> is handled and returns <code>false</code>.</p>      *      * @param number  the number to test, may be <code>null</code>      * @return <code>true</code> if the specified number occurs within this range      * @throws IllegalArgumentException if the <code>Number</code> cannot be compared      */     public abstract boolean containsNumber(Number number);     /**      * <p>Tests whether the specified <code>Number</code> occurs within      * this range using <code>long</code> comparison..</p>      *       * <p><code>null</code> is handled and returns <code>false</code>.</p>      *       * <p>This implementation forwards to the {@link #containsLong(long)} method.</p>      *      * @param value  the long to test, may be <code>null</code>      * @return <code>true</code> if the specified number occurs within this      *  range by <code>long</code> comparison      */     public boolean containsLong(Number value) {         if (value == null) {             return false;         }         return containsLong(value.longValue());     }     /**      * <p>Tests whether the specified <code>long</code> occurs within      * this range using <code>long</code> comparison.</p>      *       * <p>This implementation uses the {@link #getMinimumLong()} and       * {@link #getMaximumLong()} methods and should be good for most uses.</p>      *       * @param value  the long to test      * @return <code>true</code> if the specified number occurs within this      *  range by <code>long</code> comparison      */     public boolean containsLong(long value) {         return value >= getMinimumLong() && value <= getMaximumLong();     }     /**      * <p>Tests whether the specified <code>Number</code> occurs within      * this range using <code>int</code> comparison..</p>      *       * <p><code>null</code> is handled and returns <code>false</code>.</p>      *       * <p>This implementation forwards to the {@link #containsInteger(int)} method.</p>      *      * @param value  the integer to test, may be <code>null</code>      * @return <code>true</code> if the specified number occurs within this      *  range by <code>int</code> comparison      */     public boolean containsInteger(Number value) {         if (value == null) {             return false;         }         return containsInteger(value.intValue());     }     /**      * <p>Tests whether the specified <code>int</code> occurs within      * this range using <code>int</code> comparison.</p>      *       * <p>This implementation uses the {@link #getMinimumInteger()} and       * {@link #getMaximumInteger()} methods and should be good for most uses.</p>      *       * @param value  the int to test      * @return <code>true</code> if the specified number occurs within this      *  range by <code>int</code> comparison      */     public boolean containsInteger(int value) {         return value >= getMinimumInteger() && value <= getMaximumInteger();     }     /**      * <p>Tests whether the specified <code>Number</code> occurs within      * this range using <code>double</code> comparison..</p>      *       * <p><code>null</code> is handled and returns <code>false</code>.</p>      *       * <p>This implementation forwards to the {@link #containsDouble(double)} method.</p>      *      * @param value  the double to test, may be <code>null</code>      * @return <code>true</code> if the specified number occurs within this      *  range by <code>double</code> comparison      */     public boolean containsDouble(Number value) {         if (value == null) {             return false;         }         return containsDouble(value.doubleValue());     }     /**      * <p>Tests whether the specified <code>double</code> occurs within      * this range using <code>double</code> comparison.</p>      *       * <p>This implementation uses the {@link #getMinimumDouble()} and       * {@link #getMaximumDouble()} methods and should be good for most uses.</p>      *       * @param value  the double to test      * @return <code>true</code> if the specified number occurs within this      *  range by <code>double</code> comparison      */     public boolean containsDouble(double value) {         int compareMin = compare(getMinimumDouble(), value);         int compareMax = compare(getMaximumDouble(), value);         return compareMin <= 0 && compareMax >= 0;     }     /**      * <p>Tests whether the specified <code>Number</code> occurs within      * this range using <code>float</code> comparison.</p>      *       * <p><code>null</code> is handled and returns <code>false</code>.</p>      *       * <p>This implementation forwards to the {@link #containsFloat(float)} method.</p>      *      * @param value  the float to test, may be <code>null</code>      * @return <code>true</code> if the specified number occurs within this      *  range by <code>float</code> comparison      */     public boolean containsFloat(Number value) {         if (value == null) {             return false;         }         return containsFloat(value.floatValue());     }     /**      * <p>Tests whether the specified <code>float</code> occurs within      * this range using <code>float</code> comparison.</p>      *       * <p>This implementation uses the {@link #getMinimumFloat()} and       * {@link #getMaximumFloat()} methods and should be good for most uses.</p>      *       * @param value  the float to test      * @return <code>true</code> if the specified number occurs within this      *  range by <code>float</code> comparison      */     public boolean containsFloat(float value) {         int compareMin = compare(getMinimumFloat(), value);         int compareMax = compare(getMaximumFloat(), value);         return compareMin <= 0 && compareMax >= 0;     }     // Range tests     //--------------------------------------------------------------------     /**      * <p>Tests whether the specified range occurs entirely within this range.</p>      *       * <p>The exact comparison implementation varies by subclass. It is      * intended that an <code>int</code> specific subclass will compare using      * <code>int</code> comparison.</p>      *       * <p><code>null</code> is handled and returns <code>false</code>.</p>      *       * <p>This implementation uses the {@link #containsNumber(Number)} method.      * Subclasses may be able to optimise this.</p>      *      * @param range  the range to test, may be <code>null</code>      * @return <code>true</code> if the specified range occurs entirely within      *  this range; otherwise, <code>false</code>      * @throws IllegalArgumentException if the <code>Range</code> cannot be compared      */     public boolean containsRange(Range range) {         if (range == null) {             return false;         }         return containsNumber(range.getMinimumNumber())              && containsNumber(range.getMaximumNumber());     }     /**      * <p>Tests whether the specified range overlaps with this range.</p>      *       * <p>The exact comparison implementation varies by subclass. It is      * intended that an <code>int</code> specific subclass will compare using      * <code>int</code> comparison.</p>      *       * <p><code>null</code> is handled and returns <code>false</code>.</p>      *       * <p>This implementation uses the {@link #containsNumber(Number)} and      * {@link #containsRange(Range)} methods.      * Subclasses may be able to optimise this.</p>      *      * @param range  the range to test, may be <code>null</code>      * @return <code>true</code> if the specified range overlaps with this      *  range; otherwise, <code>false</code>      * @throws IllegalArgumentException if the <code>Range</code> cannot be compared      */     public boolean overlapsRange(Range range) {         if (range == null) {             return false;         }         return range.containsNumber(getMinimumNumber())             || range.containsNumber(getMaximumNumber())             || containsNumber(range.getMinimumNumber());     }     // Basics     //--------------------------------------------------------------------     /**      * <p>Compares this range to another object to test if they are equal.</p>.      *       * <p>To be equal, the class, minimum and maximum must be equal.</p>      *       * <p>This implementation uses the {@link #getMinimumNumber()} and       * {@link #getMaximumNumber()} methods.       * Subclasses may be able to optimise this.</p>      *      * @param obj the reference object with which to compare      * @return <code>true</code> if this object is equal      */     public boolean equals(Object obj) {         if (obj == this) {             return true;         } else if (obj == null || obj.getClass() != getClass()) {             return false;         } else {             Range range = (Range) obj;             return getMinimumNumber().equals(range.getMinimumNumber()) &&                    getMaximumNumber().equals(range.getMaximumNumber());         }     }     /**      * <p>Gets a hashCode for the range.</p>      *       * <p>This implementation uses the {@link #getMinimumNumber()} and       * {@link #getMaximumNumber()} methods.       * Subclasses may be able to optimise this.</p>      *      * @return a hash code value for this object      */     public int hashCode() {         int result = 17;         result = 37 * result + getClass().hashCode();         result = 37 * result + getMinimumNumber().hashCode();         result = 37 * result + getMaximumNumber().hashCode();         return result;     }     /**      * <p>Gets the range as a <code>String</code>.</p>      *      * <p>The format of the String is 'Range[<i>min</i>,<i>max</i>]'.</p>      *       * <p>This implementation uses the {@link #getMinimumNumber()} and       * {@link #getMaximumNumber()} methods.       * Subclasses may be able to optimise this.</p>      *      * @return the <code>String</code> representation of this range      */     public String toString() {         StringBuffer buf = new StringBuffer(32);         buf.append("Range[");         buf.append(getMinimumNumber());         buf.append(',');         buf.append(getMaximumNumber());         buf.append(']');         return buf.toString();     }     /**      * <p>Compares two <code>doubles</code> for order.</p>      *      * <p>This method is more comprehensive than the standard Java greater      * than, less than and equals operators.</p>      * <ul>      *  <li>It returns <code>-1</code> if the first value is less than the second.</li>      *  <li>It returns <code>+1</code> if the first value is greater than the second.</li>      *  <li>It returns <code>0</code> if the values are equal.</li>      * </ul>      *      * <p>      * The ordering is as follows, largest to smallest:      * <ul>      *  <li>NaN      *  <li>Positive infinity      *  <li>Maximum double      *  <li>Normal positive numbers      *  <li>+0.0      *  <li>-0.0      *  <li>Normal negative numbers      *  <li>Minimum double (<code>-Double.MAX_VALUE</code>)      *  <li>Negative infinity      * </ul>      * </p>      *      * <p>Comparing <code>NaN</code> with <code>NaN</code> will      * return <code>0</code>.</p>      *       * @param lhs  the first <code>double</code>      * @param rhs  the second <code>double</code>      * @return <code>-1</code> if lhs is less, <code>+1</code> if greater,      *  <code>0</code> if equal to rhs      */     public static int compare(double lhs, double rhs) {         if (lhs < rhs) {             return -1;         }         if (lhs > rhs) {             return +1;         }         // Need to compare bits to handle 0.0 == -0.0 being true         // compare should put -0.0 < +0.0         // Two NaNs are also == for compare purposes         // where NaN == NaN is false         long lhsBits = Double.doubleToLongBits(lhs);         long rhsBits = Double.doubleToLongBits(rhs);         if (lhsBits == rhsBits) {             return 0;         }         // Something exotic! A comparison to NaN or 0.0 vs -0.0         // Fortunately NaN's long is > than everything else         // Also negzeros bits < poszero         // NAN: 9221120237041090560         // MAX: 9218868437227405311         // NEGZERO: -9223372036854775808         if (lhsBits < rhsBits) {             return -1;         } else {             return +1;         }     }          /**      * <p>Compares two floats for order.</p>      *      * <p>This method is more comprehensive than the standard Java greater than,      * less than and equals operators.</p>      * <ul>      *  <li>It returns <code>-1</code> if the first value is less than the second.      *  <li>It returns <code>+1</code> if the first value is greater than the second.      *  <li>It returns <code>0</code> if the values are equal.      * </ul>      *      * <p> The ordering is as follows, largest to smallest:      * <ul>      * <li>NaN      * <li>Positive infinity      * <li>Maximum float      * <li>Normal positive numbers      * <li>+0.0      * <li>-0.0      * <li>Normal negative numbers      * <li>Minimum float (<code>-Float.MAX_VALUE</code>)      * <li>Negative infinity      * </ul>      *      * <p>Comparing <code>NaN</code> with <code>NaN</code> will return      * <code>0</code>.</p>      *       * @param lhs  the first <code>float</code>      * @param rhs  the second <code>float</code>      * @return <code>-1</code> if lhs is less, <code>+1</code> if greater,      *  <code>0</code> if equal to rhs      */     public static int compare(float lhs, float rhs) {         if (lhs < rhs) {             return -1;         }         if (lhs > rhs) {             return +1;         }         //Need to compare bits to handle 0.0 == -0.0 being true         // compare should put -0.0 < +0.0         // Two NaNs are also == for compare purposes         // where NaN == NaN is false         int lhsBits = Float.floatToIntBits(lhs);         int rhsBits = Float.floatToIntBits(rhs);         if (lhsBits == rhsBits) {             return 0;         }         //Something exotic! A comparison to NaN or 0.0 vs -0.0         //Fortunately NaN's int is > than everything else         //Also negzeros bits < poszero         //NAN: 2143289344         //MAX: 2139095039         //NEGZERO: -2147483648         if (lhsBits < rhsBits) {             return -1;         } else {             return +1;         }     } }