    001    /*
    002     * Licensed to the Apache Software Foundation (ASF) under one or more
    003     * contributor license agreements.  See the NOTICE file distributed with
    004     * this work for additional information regarding copyright ownership.
    005     * The ASF licenses this file to You under the Apache License, Version 2.0
    006     * (the "License"); you may not use this file except in compliance with
    007     * the License.  You may obtain a copy of the License at
    008     *
    009     *      http://www.apache.org/licenses/LICENSE-2.0
    010     *
    011     * Unless required by applicable law or agreed to in writing, software
    012     * distributed under the License is distributed on an "AS IS" BASIS,
    013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014     * See the License for the specific language governing permissions and
    015     * limitations under the License.
    016     */
    017    package org.apache.commons.lang.math;
    018
    019    import java.io.Serializable;
    020
    021    import org.apache.commons.lang.text.StrBuilder;
    022
    023    /**
    024     * <p><code>DoubleRange</code> represents an inclusive range of <code>double</code>s.</p>
    025     *
    026     * @author Apache Software Foundation
    027     * @since 2.0
    028     * @version $Id: DoubleRange.java 1057072 2011-01-10 01:55:57Z niallp $
    029     */
    030    public final class DoubleRange extends Range implements Serializable {
    031
    032        /**
    033         * Required for serialization support.
    034         *
    035         * @see java.io.Serializable
    036         */
    037        private static final long serialVersionUID = 71849363892740L;
    038
    039        /**
    040         * The minimum number in this range (inclusive).
    041         */
    042        private final double min;
    043        /**
    044         * The maximum number in this range (inclusive).
    045         */
    046        private final double max;
    047
    048        /**
    049         * Cached output minObject (class is immutable).
    050         */
    051        private transient Double minObject = null;
    052        /**
    053         * Cached output maxObject (class is immutable).
    054         */
    055        private transient Double maxObject = null;
    056        /**
    057         * Cached output hashCode (class is immutable).
    058         */
    059        private transient int hashCode = 0;
    060        /**
    061         * Cached output toString (class is immutable).
    062         */
    063        private transient String toString = null;
    064
    065        /**
    066         * <p>Constructs a new <code>DoubleRange</code> using the specified
    067         * number as both the minimum and maximum in this range.</p>
    068         *
    069         * @param number  the number to use for this range
    070         * @throws IllegalArgumentException if the number is <code>NaN</code>
    071         */
    072        public DoubleRange(double number) {
    073            super();
    074            if (Double.isNaN(number)) {
    075                throw new IllegalArgumentException("The number must not be NaN");
    076            }
    077            this.min = number;
    078            this.max = number;
    079        }
    080
    081        /**
    082         * <p>Constructs a new <code>DoubleRange</code> using the specified
    083         * number as both the minimum and maximum in this range.</p>
    084         *
    085         * @param number  the number to use for this range, must not
    086         *  be <code>null</code>
    087         * @throws IllegalArgumentException if the number is <code>null</code>
    088         * @throws IllegalArgumentException if the number is <code>NaN</code>
    089         */
    090        public DoubleRange(Number number) {
    091            super();
    092            if (number == null) {
    093                throw new IllegalArgumentException("The number must not be null");
    094            }
    095            this.min = number.doubleValue();
    096            this.max = number.doubleValue();
    097            if (Double.isNaN(min) || Double.isNaN(max)) {
    098                throw new IllegalArgumentException("The number must not be NaN");
    099            }
    100            if (number instanceof Double) {
    101                this.minObject = (Double) number;
    102                this.maxObject = (Double) number;
    103            }
    104        }
    105
    106        /**
    107         * <p>Constructs a new <code>DoubleRange</code> with the specified
    108         * minimum and maximum numbers (both inclusive).</p>
    109         *
    110         * <p>The arguments may be passed in the order (min,max) or (max,min). The
    111         * getMinimum and getMaximum methods will return the correct values.</p>
    112         *
    113         * @param number1  first number that defines the edge of the range, inclusive
    114         * @param number2  second number that defines the edge of the range, inclusive
    115         * @throws IllegalArgumentException if either number is <code>NaN</code>
    116         */
    117        public DoubleRange(double number1, double number2) {
    118            super();
    119            if (Double.isNaN(number1) || Double.isNaN(number2)) {
    120                throw new IllegalArgumentException("The numbers must not be NaN");
    121            }
    122            if (number2 < number1) {
    123                this.min = number2;
    124                this.max = number1;
    125            } else {
    126                this.min = number1;
    127                this.max = number2;
    128            }
    129        }
    130
    131        /**
    132         * <p>Constructs a new <code>DoubleRange</code> with the specified
    133         * minimum and maximum numbers (both inclusive).</p>
    134         *
    135         * <p>The arguments may be passed in the order (min,max) or (max,min). The
    136         * getMinimum and getMaximum methods will return the correct values.</p>
    137         *
    138         * @param number1  first number that defines the edge of the range, inclusive
    139         * @param number2  second number that defines the edge of the range, inclusive
    140         * @throws IllegalArgumentException if either number is <code>null</code>
    141         * @throws IllegalArgumentException if either number is <code>NaN</code>
    142         */
    143        public DoubleRange(Number number1, Number number2) {
    144            super();
    145            if (number1 == null || number2 == null) {
    146                throw new IllegalArgumentException("The numbers must not be null");
    147            }
    148            double number1val = number1.doubleValue();
    149            double number2val = number2.doubleValue();
    150            if (Double.isNaN(number1val) || Double.isNaN(number2val)) {
    151                throw new IllegalArgumentException("The numbers must not be NaN");
    152            }
    153            if (number2val < number1val) {
    154                this.min = number2val;
    155                this.max = number1val;
    156                if (number2 instanceof Double) {
    157                    this.minObject = (Double) number2;
    158                }
    159                if (number1 instanceof Double) {
    160                    this.maxObject = (Double) number1;
    161                }
    162            } else {
    163                this.min = number1val;
    164                this.max = number2val;
    165                if (number1 instanceof Double) {
    166                    this.minObject = (Double) number1;
    167                }
    168                if (number2 instanceof Double) {
    169                    this.maxObject = (Double) number2;
    170                }
    171            }
    172        }
    173
    174        // Accessors
    175        //--------------------------------------------------------------------
    176
    177        /**
    178         * <p>Returns the minimum number in this range.</p>
    179         *
    180         * @return the minimum number in this range
    181         */
    182        public Number getMinimumNumber() {
    183            if (minObject == null) {
    184                minObject = new Double(min);
    185            }
    186            return minObject;
    187        }
    188
    189        /**
    190         * <p>Gets the minimum number in this range as a <code>long</code>.</p>
    191         *
    192         * <p>This conversion can lose information for large values or decimals.</p>
    193         *
    194         * @return the minimum number in this range
    195         */
    196        public long getMinimumLong() {
    197            return (long) min;
    198        }
    199
    200        /**
    201         * <p>Gets the minimum number in this range as a <code>int</code>.</p>
    202         *
    203         * <p>This conversion can lose information for large values or decimals.</p>
    204         *
    205         * @return the minimum number in this range
    206         */
    207        public int getMinimumInteger() {
    208            return (int) min;
    209        }
    210
    211        /**
    212         * <p>Gets the minimum number in this range as a <code>double</code>.</p>
    213         *
    214         * @return the minimum number in this range
    215         */
    216        public double getMinimumDouble() {
    217            return min;
    218        }
    219
    220        /**
    221         * <p>Gets the minimum number in this range as a <code>float</code>.</p>
    222         *
    223         * <p>This conversion can lose information for large values.</p>
    224         *
    225         * @return the minimum number in this range
    226         */
    227        public float getMinimumFloat() {
    228            return (float) min;
    229        }
    230
    231        /**
    232         * <p>Returns the maximum number in this range.</p>
    233         *
    234         * @return the maximum number in this range
    235         */
    236        public Number getMaximumNumber() {
    237            if (maxObject == null) {
    238                maxObject = new Double(max);
    239            }
    240            return maxObject;
    241        }
    242
    243        /**
    244         * <p>Gets the maximum number in this range as a <code>long</code>.</p>
    245         *
    246         * <p>This conversion can lose information for large values or decimals.</p>
    247         *
    248         * @return the maximum number in this range
    249         */
    250        public long getMaximumLong() {
    251            return (long) max;
    252        }
    253
    254        /**
    255         * <p>Gets the maximum number in this range as a <code>int</code>.</p>
    256         *
    257         * <p>This conversion can lose information for large values or decimals.</p>
    258         *
    259         * @return the maximum number in this range
    260         */
    261        public int getMaximumInteger() {
    262            return (int) max;
    263        }
    264
    265        /**
    266         * <p>Gets the maximum number in this range as a <code>double</code>.</p>
    267         *
    268         * @return the maximum number in this range
    269         */
    270        public double getMaximumDouble() {
    271            return max;
    272        }
    273
    274        /**
    275         * <p>Gets the maximum number in this range as a <code>float</code>.</p>
    276         *
    277         * <p>This conversion can lose information for large values.</p>
    278         *
    279         * @return the maximum number in this range
    280         */
    281        public float getMaximumFloat() {
    282            return (float) max;
    283        }
    284
    285        // Tests
    286        //--------------------------------------------------------------------
    287
    288        /**
    289         * <p>Tests whether the specified <code>number</code> occurs within
    290         * this range using <code>double</code> comparison.</p>
    291         *
    292         * <p><code>null</code> is handled and returns <code>false</code>.</p>
    293         *
    294         * @param number  the number to test, may be <code>null</code>
    295         * @return <code>true</code> if the specified number occurs within this range
    296         */
    297        public boolean containsNumber(Number number) {
    298            if (number == null) {
    299                return false;
    300            }
    301            return containsDouble(number.doubleValue());
    302        }
    303
    304        /**
    305         * <p>Tests whether the specified <code>double</code> occurs within
    306         * this range using <code>double</code> comparison.</p>
    307         *
    308         * <p>This implementation overrides the superclass for performance as it is
    309         * the most common case.</p>
    310         *
    311         * @param value  the double to test
    312         * @return <code>true</code> if the specified number occurs within this
    313         *  range by <code>double</code> comparison
    314         */
    315        public boolean containsDouble(double value) {
    316            return value >= min && value <= max;
    317        }
    318
    319        // Range tests
    320        //--------------------------------------------------------------------
    321
    322        /**
    323         * <p>Tests whether the specified range occurs entirely within this range
    324         * using <code>double</code> comparison.</p>
    325         *
    326         * <p><code>null</code> is handled and returns <code>false</code>.</p>
    327         *
    328         * @param range  the range to test, may be <code>null</code>
    329         * @return <code>true</code> if the specified range occurs entirely within this range
    330         * @throws IllegalArgumentException if the range is not of this type
    331         */
    332        public boolean containsRange(Range range) {
    333            if (range == null) {
    334                return false;
    335            }
    336            return containsDouble(range.getMinimumDouble())
    337                && containsDouble(range.getMaximumDouble());
    338        }
    339
    340        /**
    341         * <p>Tests whether the specified range overlaps with this range
    342         * using <code>double</code> comparison.</p>
    343         *
    344         * <p><code>null</code> is handled and returns <code>false</code>.</p>
    345         *
    346         * @param range  the range to test, may be <code>null</code>
    347         * @return <code>true</code> if the specified range overlaps with this range
    348         */
    349        public boolean overlapsRange(Range range) {
    350            if (range == null) {
    351                return false;
    352            }
    353            return range.containsDouble(min)
    354                || range.containsDouble(max)
    355                || containsDouble(range.getMinimumDouble());
    356        }
    357
    358        // Basics
    359        //--------------------------------------------------------------------
    360
    361        /**
    362         * <p>Compares this range to another object to test if they are equal.</p>.
    363         *
    364         * <p>To be equal, the class, minimum and maximum must be equal.</p>
    365         *
    366         * @param obj the reference object with which to compare
    367         * @return <code>true</code> if this object is equal
    368         */
    369        public boolean equals(Object obj) {
    370            if (obj == this) {
    371                return true;
    372            }
    373            if (obj instanceof DoubleRange == false) {
    374                return false;
    375            }
    376            DoubleRange range = (DoubleRange) obj;
    377            return (Double.doubleToLongBits(min) == Double.doubleToLongBits(range.min) &&
    378                    Double.doubleToLongBits(max) == Double.doubleToLongBits(range.max));
    379        }
    380
    381        /**
    382         * <p>Gets a hashCode for the range.</p>
    383         *
    384         * @return a hash code value for this object
    385         */
    386        public int hashCode() {
    387            if (hashCode == 0) {
    388                hashCode = 17;
    389                hashCode = 37 * hashCode + getClass().hashCode();
    390                long lng = Double.doubleToLongBits(min);
    391                hashCode = 37 * hashCode + ((int) (lng ^ (lng >> 32)));
    392                lng = Double.doubleToLongBits(max);
    393                hashCode = 37 * hashCode + ((int) (lng ^ (lng >> 32)));
    394            }
    395            return hashCode;
    396        }
    397
    398        /**
    399         * <p>Gets the range as a <code>String</code>.</p>
    400         *
    401         * <p>The format of the String is 'Range[<i>min</i>,<i>max</i>]'.</p>
    402         *
    403         * @return the <code>String</code> representation of this range
    404         */
    405        public String toString() {
    406            if (toString == null) {
    407                StrBuilder buf = new StrBuilder(32);
    408                buf.append("Range[");
    409                buf.append(min);
    410                buf.append(',');
    411                buf.append(max);
    412                buf.append(']');
    413                toString = buf.toString();
    414            }
    415            return toString;
    416        }
    417
    418    }