    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    /**
    020     * <p><code>Range</code> represents a range of numbers of the same type.</p>
    021     *
    022     * <p>Specific subclasses hold the range values as different types. Each
    023     * subclass should be immutable and {@link java.io.Serializable Serializable}
    024     * if possible.</p>
    025     *
    026     * @author Apache Software Foundation
    027     * @since 2.0
    028     * @version $Id: Range.java 905636 2010-02-02 14:03:32Z niallp $
    029     */
    030    public abstract class Range {
    031
    032        /**
    033         * <p>Constructs a new range.</p>
    034         */
    035        public Range() {
    036            super();
    037        }
    038
    039        // Accessors
    040        //--------------------------------------------------------------------
    041
    042        /**
    043         * <p>Gets the minimum number in this range.</p>
    044         *
    045         * @return the minimum number in this range
    046         */
    047        public abstract Number getMinimumNumber();
    048
    049        /**
    050         * <p>Gets the minimum number in this range as a <code>long</code>.</p>
    051         *
    052         * <p>This implementation uses the {@link #getMinimumNumber()} method.
    053         * Subclasses may be able to optimise this.</p>
    054         *
    055         * @return the minimum number in this range
    056         */
    057        public long getMinimumLong() {
    058            return getMinimumNumber().longValue();
    059        }
    060
    061        /**
    062         * <p>Gets the minimum number in this range as a <code>int</code>.</p>
    063         *
    064         * <p>This implementation uses the {@link #getMinimumNumber()} method.
    065         * Subclasses may be able to optimise this.</p>
    066         *
    067         * @return the minimum number in this range
    068         */
    069        public int getMinimumInteger() {
    070            return getMinimumNumber().intValue();
    071        }
    072
    073        /**
    074         * <p>Gets the minimum number in this range as a <code>double</code>.</p>
    075         *
    076         * <p>This implementation uses the {@link #getMinimumNumber()} method.
    077         * Subclasses may be able to optimise this.</p>
    078         *
    079         * @return the minimum number in this range
    080         */
    081        public double getMinimumDouble() {
    082            return getMinimumNumber().doubleValue();
    083        }
    084
    085        /**
    086         * <p>Gets the minimum number in this range as a <code>float</code>.</p>
    087         *
    088         * <p>This implementation uses the {@link #getMinimumNumber()} method.
    089         * Subclasses may be able to optimise this.</p>
    090         *
    091         * @return the minimum number in this range
    092         */
    093        public float getMinimumFloat() {
    094            return getMinimumNumber().floatValue();
    095        }
    096
    097        /**
    098         * <p>Gets the maximum number in this range.</p>
    099         *
    100         * @return the maximum number in this range
    101         */
    102        public abstract Number getMaximumNumber();
    103
    104        /**
    105         * <p>Gets the maximum number in this range as a <code>long</code>.</p>
    106         *
    107         * <p>This implementation uses the {@link #getMaximumNumber()} method.
    108         * Subclasses may be able to optimise this.</p>
    109         *
    110         * @return the maximum number in this range
    111         */
    112        public long getMaximumLong() {
    113            return getMaximumNumber().longValue();
    114        }
    115
    116        /**
    117         * <p>Gets the maximum number in this range as a <code>int</code>.</p>
    118         *
    119         * <p>This implementation uses the {@link #getMaximumNumber()} method.
    120         * Subclasses may be able to optimise this.</p>
    121         *
    122         * @return the maximum number in this range
    123         */
    124        public int getMaximumInteger() {
    125            return getMaximumNumber().intValue();
    126        }
    127
    128        /**
    129         * <p>Gets the maximum number in this range as a <code>double</code>.</p>
    130         *
    131         * <p>This implementation uses the {@link #getMaximumNumber()} method.
    132         * Subclasses may be able to optimise this.</p>
    133         *
    134         * @return the maximum number in this range
    135         */
    136        public double getMaximumDouble() {
    137            return getMaximumNumber().doubleValue();
    138        }
    139
    140        /**
    141         * <p>Gets the maximum number in this range as a <code>float</code>.</p>
    142         *
    143         * <p>This implementation uses the {@link #getMaximumNumber()} method.
    144         * Subclasses may be able to optimise this.</p>
    145         *
    146         * @return the maximum number in this range
    147         */
    148        public float getMaximumFloat() {
    149            return getMaximumNumber().floatValue();
    150        }
    151
    152        // Include tests
    153        //--------------------------------------------------------------------
    154
    155        /**
    156         * <p>Tests whether the specified <code>Number</code> occurs within
    157         * this range.</p>
    158         *
    159         * <p>The exact comparison implementation varies by subclass. It is
    160         * intended that an <code>int</code> specific subclass will compare using
    161         * <code>int</code> comparison.</p>
    162         *
    163         * <p><code>null</code> is handled and returns <code>false</code>.</p>
    164         *
    165         * @param number  the number to test, may be <code>null</code>
    166         * @return <code>true</code> if the specified number occurs within this range
    167         * @throws IllegalArgumentException if the <code>Number</code> cannot be compared
    168         */
    169        public abstract boolean containsNumber(Number number);
    170
    171        /**
    172         * <p>Tests whether the specified <code>Number</code> occurs within
    173         * this range using <code>long</code> comparison..</p>
    174         *
    175         * <p><code>null</code> is handled and returns <code>false</code>.</p>
    176         *
    177         * <p>This implementation forwards to the {@link #containsLong(long)} method.</p>
    178         *
    179         * @param value  the long to test, may be <code>null</code>
    180         * @return <code>true</code> if the specified number occurs within this
    181         *  range by <code>long</code> comparison
    182         */
    183        public boolean containsLong(Number value) {
    184            if (value == null) {
    185                return false;
    186            }
    187            return containsLong(value.longValue());
    188        }
    189
    190        /**
    191         * <p>Tests whether the specified <code>long</code> occurs within
    192         * this range using <code>long</code> comparison.</p>
    193         *
    194         * <p>This implementation uses the {@link #getMinimumLong()} and
    195         * {@link #getMaximumLong()} methods and should be good for most uses.</p>
    196         *
    197         * @param value  the long to test
    198         * @return <code>true</code> if the specified number occurs within this
    199         *  range by <code>long</code> comparison
    200         */
    201        public boolean containsLong(long value) {
    202            return value >= getMinimumLong() && value <= getMaximumLong();
    203        }
    204
    205        /**
    206         * <p>Tests whether the specified <code>Number</code> occurs within
    207         * this range using <code>int</code> comparison..</p>
    208         *
    209         * <p><code>null</code> is handled and returns <code>false</code>.</p>
    210         *
    211         * <p>This implementation forwards to the {@link #containsInteger(int)} method.</p>
    212         *
    213         * @param value  the integer to test, may be <code>null</code>
    214         * @return <code>true</code> if the specified number occurs within this
    215         *  range by <code>int</code> comparison
    216         */
    217        public boolean containsInteger(Number value) {
    218            if (value == null) {
    219                return false;
    220            }
    221            return containsInteger(value.intValue());
    222        }
    223
    224        /**
    225         * <p>Tests whether the specified <code>int</code> occurs within
    226         * this range using <code>int</code> comparison.</p>
    227         *
    228         * <p>This implementation uses the {@link #getMinimumInteger()} and
    229         * {@link #getMaximumInteger()} methods and should be good for most uses.</p>
    230         *
    231         * @param value  the int to test
    232         * @return <code>true</code> if the specified number occurs within this
    233         *  range by <code>int</code> comparison
    234         */
    235        public boolean containsInteger(int value) {
    236            return value >= getMinimumInteger() && value <= getMaximumInteger();
    237        }
    238
    239        /**
    240         * <p>Tests whether the specified <code>Number</code> occurs within
    241         * this range using <code>double</code> comparison..</p>
    242         *
    243         * <p><code>null</code> is handled and returns <code>false</code>.</p>
    244         *
    245         * <p>This implementation forwards to the {@link #containsDouble(double)} method.</p>
    246         *
    247         * @param value  the double to test, may be <code>null</code>
    248         * @return <code>true</code> if the specified number occurs within this
    249         *  range by <code>double</code> comparison
    250         */
    251        public boolean containsDouble(Number value) {
    252            if (value == null) {
    253                return false;
    254            }
    255            return containsDouble(value.doubleValue());
    256        }
    257
    258        /**
    259         * <p>Tests whether the specified <code>double</code> occurs within
    260         * this range using <code>double</code> comparison.</p>
    261         *
    262         * <p>This implementation uses the {@link #getMinimumDouble()} and
    263         * {@link #getMaximumDouble()} methods and should be good for most uses.</p>
    264         *
    265         * @param value  the double to test
    266         * @return <code>true</code> if the specified number occurs within this
    267         *  range by <code>double</code> comparison
    268         */
    269        public boolean containsDouble(double value) {
    270            int compareMin = NumberUtils.compare(getMinimumDouble(), value);
    271            int compareMax = NumberUtils.compare(getMaximumDouble(), value);
    272            return compareMin <= 0 && compareMax >= 0;
    273        }
    274
    275        /**
    276         * <p>Tests whether the specified <code>Number</code> occurs within
    277         * this range using <code>float</code> comparison.</p>
    278         *
    279         * <p><code>null</code> is handled and returns <code>false</code>.</p>
    280         *
    281         * <p>This implementation forwards to the {@link #containsFloat(float)} method.</p>
    282         *
    283         * @param value  the float to test, may be <code>null</code>
    284         * @return <code>true</code> if the specified number occurs within this
    285         *  range by <code>float</code> comparison
    286         */
    287        public boolean containsFloat(Number value) {
    288            if (value == null) {
    289                return false;
    290            }
    291            return containsFloat(value.floatValue());
    292        }
    293
    294        /**
    295         * <p>Tests whether the specified <code>float</code> occurs within
    296         * this range using <code>float</code> comparison.</p>
    297         *
    298         * <p>This implementation uses the {@link #getMinimumFloat()} and
    299         * {@link #getMaximumFloat()} methods and should be good for most uses.</p>
    300         *
    301         * @param value  the float to test
    302         * @return <code>true</code> if the specified number occurs within this
    303         *  range by <code>float</code> comparison
    304         */
    305        public boolean containsFloat(float value) {
    306            int compareMin = NumberUtils.compare(getMinimumFloat(), value);
    307            int compareMax = NumberUtils.compare(getMaximumFloat(), value);
    308            return compareMin <= 0 && compareMax >= 0;
    309        }
    310
    311        // Range tests
    312        //--------------------------------------------------------------------
    313
    314        /**
    315         * <p>Tests whether the specified range occurs entirely within this range.</p>
    316         *
    317         * <p>The exact comparison implementation varies by subclass. It is
    318         * intended that an <code>int</code> specific subclass will compare using
    319         * <code>int</code> comparison.</p>
    320         *
    321         * <p><code>null</code> is handled and returns <code>false</code>.</p>
    322         *
    323         * <p>This implementation uses the {@link #containsNumber(Number)} method.
    324         * Subclasses may be able to optimise this.</p>
    325         *
    326         * @param range  the range to test, may be <code>null</code>
    327         * @return <code>true</code> if the specified range occurs entirely within
    328         *  this range; otherwise, <code>false</code>
    329         * @throws IllegalArgumentException if the <code>Range</code> cannot be compared
    330         */
    331        public boolean containsRange(Range range) {
    332            if (range == null) {
    333                return false;
    334            }
    335            return containsNumber(range.getMinimumNumber())
    336                && containsNumber(range.getMaximumNumber());
    337        }
    338
    339        /**
    340         * <p>Tests whether the specified range overlaps with this range.</p>
    341         *
    342         * <p>The exact comparison implementation varies by subclass. It is
    343         * intended that an <code>int</code> specific subclass will compare using
    344         * <code>int</code> comparison.</p>
    345         *
    346         * <p><code>null</code> is handled and returns <code>false</code>.</p>
    347         *
    348         * <p>This implementation uses the {@link #containsNumber(Number)} and
    349         * {@link #containsRange(Range)} methods.
    350         * Subclasses may be able to optimise this.</p>
    351         *
    352         * @param range  the range to test, may be <code>null</code>
    353         * @return <code>true</code> if the specified range overlaps with this
    354         *  range; otherwise, <code>false</code>
    355         * @throws IllegalArgumentException if the <code>Range</code> cannot be compared
    356         */
    357        public boolean overlapsRange(Range range) {
    358            if (range == null) {
    359                return false;
    360            }
    361            return range.containsNumber(getMinimumNumber())
    362                || range.containsNumber(getMaximumNumber())
    363                || containsNumber(range.getMinimumNumber());
    364        }
    365
    366        // Basics
    367        //--------------------------------------------------------------------
    368
    369        /**
    370         * <p>Compares this range to another object to test if they are equal.</p>.
    371         *
    372         * <p>To be equal, the class, minimum and maximum must be equal.</p>
    373         *
    374         * <p>This implementation uses the {@link #getMinimumNumber()} and
    375         * {@link #getMaximumNumber()} methods.
    376         * Subclasses may be able to optimise this.</p>
    377         *
    378         * @param obj the reference object with which to compare
    379         * @return <code>true</code> if this object is equal
    380         */
    381        public boolean equals(Object obj) {
    382            if (obj == this) {
    383                return true;
    384            } else if (obj == null || obj.getClass() != getClass()) {
    385                return false;
    386            } else {
    387                Range range = (Range) obj;
    388                return getMinimumNumber().equals(range.getMinimumNumber()) &&
    389                       getMaximumNumber().equals(range.getMaximumNumber());
    390            }
    391        }
    392
    393        /**
    394         * <p>Gets a hashCode for the range.</p>
    395         *
    396         * <p>This implementation uses the {@link #getMinimumNumber()} and
    397         * {@link #getMaximumNumber()} methods.
    398         * Subclasses may be able to optimise this.</p>
    399         *
    400         * @return a hash code value for this object
    401         */
    402        public int hashCode() {
    403            int result = 17;
    404            result = 37 * result + getClass().hashCode();
    405            result = 37 * result + getMinimumNumber().hashCode();
    406            result = 37 * result + getMaximumNumber().hashCode();
    407            return result;
    408        }
    409
    410        /**
    411         * <p>Gets the range as a <code>String</code>.</p>
    412         *
    413         * <p>The format of the String is 'Range[<i>min</i>,<i>max</i>]'.</p>
    414         *
    415         * <p>This implementation uses the {@link #getMinimumNumber()} and
    416         * {@link #getMaximumNumber()} methods.
    417         * Subclasses may be able to optimise this.</p>
    418         *
    419         * @return the <code>String</code> representation of this range
    420         */
    421        public String toString() {
    422            StringBuffer buf = new StringBuffer(32);
    423            buf.append("Range[");
    424            buf.append(getMinimumNumber());
    425            buf.append(',');
    426            buf.append(getMaximumNumber());
    427            buf.append(']');
    428            return buf.toString();
    429        }
    430
    431    }