    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.lang3;
    018
    019    import java.io.Serializable;
    020    import java.util.Comparator;
    021
    022    /**
    023     * <p><code>Range</code> represents an immutable range of numbers of the same type.</p>
    024     * <p>The objects need to either be implementations of <code>java.lang.Comparable</code>
    025     * or you need to supply a <code>java.util.Comparator</code>. </p>
    026     *
    027     * <p>#ThreadSafe#</p>
    028     * @author Apache Software Foundation
    029     * @since 3.0
    030     * @version $Id: Range.java 967237 2010-07-23 20:08:57Z mbenson $
    031     */
    032    public final class Range<T> implements Serializable {
    033
    034        /**
    035         * Required for serialization support.
    036         *
    037         * @see java.io.Serializable
    038         */
    039        private static final long serialVersionUID = 1L;
    040
    041        /**
    042         * The ordering scheme used in this range.
    043         */
    044        private final Comparator<T> comparator;
    045
    046        /**
    047         * The minimum value in this range (inclusive).
    048         */
    049        private final T minimum;
    050
    051        /**
    052         * The maximum value in this range (inclusive).
    053         */
    054        private final T maximum;
    055
    056        /**
    057         * Cached output hashCode (class is immutable).
    058         */
    059        private transient int hashCode = 0;
    060
    061        /**
    062         * Cached output toString (class is immutable).
    063         */
    064        private transient String toString = null;
    065
    066        /**
    067         * <p>Constructs a new <code>Range</code> using the specified
    068         * element as both the minimum and maximum in this range.</p>
    069         * <p>The range uses the natural ordering of the elements to
    070         * determine where values lie in the range.</p>
    071         *
    072         * @param element  the value to use for this range, must not be <code>null</code>
    073         * @return the new range object
    074         * @throws IllegalArgumentException if the value is <code>null</code>
    075         * @throws ClassCastException if the value is not Comparable
    076         */
    077        public static <T extends Comparable<T>> Range<T> is(T element) {
    078            return new Range<T>(element, element, ComparableComparator.<T>getInstance());
    079        }
    080
    081        /**
    082         * <p>Constructs a new <code>Range</code> with the specified
    083         * minimum and maximum values (both inclusive).</p>
    084         * <p>The range uses the natural ordering of the elements to
    085         * determine where values lie in the range.</p>
    086         *
    087         * <p>The arguments may be passed in the order (min,max) or (max,min). The
    088         * getMinimum and getMaximum methods will return the correct values.</p>
    089         *
    090         * @param element1  first value that defines the edge of the range, inclusive
    091         * @param element2  second value that defines the edge of the range, inclusive
    092         * @return the new range object
    093         * @throws IllegalArgumentException if either value is <code>null</code>
    094         * @throws ClassCastException if either value is not Comparable
    095         */
    096        public static <T extends Comparable<T>> Range<T> between(T element1, T element2) {
    097            return new Range<T>( element1, element2, ComparableComparator.<T>getInstance());
    098        }
    099
    100        /**
    101         * <p>Constructs a new <code>Range</code> using the specified
    102         * element as both the minimum and maximum in this range.</p>
    103         * <p>The range uses the passed in <code>Comparator</code> to
    104         * determine where values lie in the range.</p>
    105         *
    106         * @param element  the value to use for this range, must not be <code>null</code>
    107         * @param c comparator to be used
    108         * @return the new range object
    109         * @throws IllegalArgumentException if the value is <code>null</code>
    110         */
    111        public static <T> Range<T> is(T element, Comparator<T> c) {
    112            return new Range<T>(element, element, c);
    113        }
    114
    115        /**
    116         * <p>Constructs a new <code>Range</code> with the specified
    117         * minimum and maximum values (both inclusive).</p>
    118         * <p>The range uses the passed in <code>Comparator</code> to
    119         * determine where values lie in the range.</p>
    120         *
    121         * <p>The arguments may be passed in the order (min,max) or (max,min). The
    122         * getMinimum and getMaximum methods will return the correct values.</p>
    123         *
    124         * @param element1  first value that defines the edge of the range, inclusive
    125         * @param element2  second value that defines the edge of the range, inclusive
    126         * @param c comparator to be used
    127         * @return the new range object
    128         * @throws IllegalArgumentException if either value is <code>null</code>
    129         */
    130        public static <T> Range<T> between(T element1, T element2, Comparator<T> c) {
    131            return new Range<T>(element1, element2, c);
    132        }
    133
    134        private Range(T element1, T element2, Comparator<T> c) {
    135            if(element1 == null || element2 == null) {
    136                throw new IllegalArgumentException("Elements in a range must not be null: element1=" +
    137                                                   element1 + ", element2=" + element2);
    138            }
    139
    140            if(c == null) {
    141                throw new IllegalArgumentException("Comparator must not be null");
    142            }
    143
    144            if(c.compare(element1, element2) < 1) {
    145                this.minimum = element1;
    146                this.maximum = element2;
    147            } else {
    148                this.minimum = element2;
    149                this.maximum = element1;
    150            }
    151            this.comparator = c;
    152        }
    153
    154        // Accessors
    155        //--------------------------------------------------------------------
    156
    157        /**
    158         * <p>Gets the minimum value in this range.</p>
    159         *
    160         * @return the minimum value in this range
    161         */
    162        public T getMinimum() {
    163            return this.minimum;
    164        }
    165
    166        /**
    167         * <p>Gets the maximum value in this range.</p>
    168         *
    169         * @return the maximum value in this range
    170         */
    171        public T getMaximum() {
    172            return this.maximum;
    173        }
    174
    175        /**
    176         * <p>Gets the comparator being used to determine if objects are within the range. </p>
    177         *
    178         * @return the comparator being used
    179         */
    180        public Comparator<T> getComparator() {
    181            return this.comparator;
    182        }
    183
    184        /**
    185         * <p>Whether or not the Range is using the default natural comparison method
    186         * to compare elements. </p>
    187         *
    188         * @return whether or not the default Comparator is in use
    189         */
    190        public boolean isDefaultNaturalOrdering() {
    191            return this.comparator == ComparableComparator.INSTANCE;
    192        }
    193
    194        // Include tests
    195        //--------------------------------------------------------------------
    196
    197        /**
    198         * <p>Tests whether the specified element occurs within this range.</p>
    199         *
    200         * <p><code>null</code> is handled and returns <code>false</code>.</p>
    201         *
    202         * @param element  the element to test, may be <code>null</code>
    203         * @return <code>true</code> if the specified element occurs within this range
    204         */
    205        public boolean contains(T element) {
    206            if(element == null) {
    207                return false;
    208            }
    209            return (comparator.compare(element, this.minimum) > -1) && (comparator.compare(element, this.maximum) < 1);
    210        }
    211
    212        /**
    213         * <p>Tests whether the specified element occurs before this range.</p>
    214         *
    215         * <p><code>null</code> is handled and returns <code>false</code>.</p>
    216         *
    217         * @param element  the element to test, may be <code>null</code>
    218         * @return <code>true</code> if the specified element occurs before this range
    219         */
    220        public boolean elementBefore(T element) {
    221            if (element == null) {
    222                return false;
    223            }
    224
    225            return this.comparator.compare(element, this.minimum) < 0;
    226        }
    227
    228        /**
    229         * <p>Tests whether the specified element occurs after this range.</p>
    230         *
    231         * <p><code>null</code> is handled and returns <code>false</code>.</p>
    232         *
    233         * @param element  the element to test, may be <code>null</code>
    234         * @return <code>true</code> if the specified element occurs after this range
    235         */
    236        public boolean elementAfter(T element) {
    237            if (element == null) {
    238                return false;
    239            }
    240
    241            return this.comparator.compare(element, this.maximum) > 0;
    242        }
    243
    244        /**
    245         * <p>Tests where the specified element occurs relative to this range.</p>
    246         * <p>The API is reminiscent of the Comparable interface returning <code>-1</code> if
    247         * the element is before the range, <code>0</code> if contained within the range and
    248         * <code>1</code> if the element is after the range. </p>
    249         *
    250         * @param element  the element to test
    251         * @return -1, 0 or +1 depending on the element's location relative to the range
    252         */
    253        public int elementCompareTo(T element) {
    254            if(element == null) {
    255                // Comparable API says throw NPE on null
    256                throw new NullPointerException("Element is null");
    257            }
    258            if(elementBefore(element)) {
    259                return -1;
    260            } else
    261            if(elementAfter(element)) {
    262                return 1;
    263            } else {
    264                return 0;
    265            }
    266        }
    267
    268        // Range tests
    269        //--------------------------------------------------------------------
    270
    271        /**
    272         * <p>Tests whether the specified range occurs entirely within this range.</p>
    273         *
    274         * <p><code>null</code> is handled and returns <code>false</code>.</p>
    275         *
    276         * @param range  the range to test, may be <code>null</code>
    277         * @return <code>true</code> if the specified range occurs entirely within
    278         *  this range; otherwise, <code>false</code>
    279         * @throws IllegalArgumentException if the <code>Range</code> cannot be compared
    280         */
    281        public boolean containsRange(Range<T> range) {
    282            if (range == null) {
    283                return false;
    284            }
    285            return contains(range.getMinimum())
    286                && contains(range.getMaximum());
    287        }
    288
    289        /**
    290         * <p>Tests whether the specified range overlaps with this range.</p>
    291         *
    292         * <p><code>null</code> is handled and returns <code>false</code>.</p>
    293         *
    294         * @param range  the range to test, may be <code>null</code>
    295         * @return <code>true</code> if the specified range overlaps with this
    296         *  range; otherwise, <code>false</code>
    297         * @throws IllegalArgumentException if the <code>Range</code> cannot be compared
    298         */
    299        public boolean overlapsRange(Range<T> range) {
    300            if (range == null) {
    301                return false;
    302            }
    303            return range.contains(this.minimum)
    304                || range.contains(this.maximum)
    305                || contains(range.getMinimum());
    306        }
    307
    308        // Basics
    309        //--------------------------------------------------------------------
    310
    311        /**
    312         * <p>Compares this range to another object to test if they are equal.</p>.
    313         *
    314         * <p>To be equal, the class, minimum and maximum must be equal.</p>
    315         *
    316         * @param obj the reference object with which to compare
    317         * @return <code>true</code> if this object is equal
    318         */
    319        @Override
    320        public boolean equals(Object obj) {
    321            if (obj == this) {
    322                return true;
    323            } else if (obj == null || obj.getClass() != getClass()) {
    324                return false;
    325            } else {
    326                @SuppressWarnings("unchecked") // OK because we checked the class above
    327                Range<T> range = (Range<T>) obj;
    328                return getMinimum().equals(range.getMinimum()) &&
    329                       getMaximum().equals(range.getMaximum());
    330            }
    331        }
    332
    333        /**
    334         * <p>Gets a hashCode for the range.</p>
    335         *
    336         * @return a hash code value for this object
    337         */
    338        @Override
    339        public int hashCode() {
    340            int result = hashCode;
    341            if (hashCode == 0) {
    342                result = 17;
    343                result = 37 * result + getClass().hashCode();
    344                result = 37 * result + this.minimum.hashCode();
    345                result = 37 * result + this.maximum.hashCode();
    346                hashCode = result;
    347            }
    348            return result;
    349        }
    350
    351        /**
    352         * <p>Gets the range as a <code>String</code>.</p>
    353         *
    354         * <p>The format of the String is 'Range[<i>min</i>,<i>max</i>]'.</p>
    355         *
    356         * @return the <code>String</code> representation of this range
    357         */
    358        @Override
    359        public String toString() {
    360            String result = toString;
    361            if (result == null) {
    362                StringBuilder buf = new StringBuilder(32);
    363                buf.append("Range[");
    364                buf.append(this.minimum);
    365                buf.append(',');
    366                buf.append(this.maximum);
    367                buf.append(']');
    368                result = buf.toString();
    369                toString = result;
    370            }
    371            return result;
    372        }
    373
    374        // Taken from Commons Collections - documentation removed as not a public class
    375        private static class ComparableComparator<E extends Comparable<? super E>> implements Comparator<E>, Serializable {
    376
    377            private static final long serialVersionUID = 1L;
    378
    379            @SuppressWarnings("rawtypes") // Comparator works for all types
    380            public static final ComparableComparator<?> INSTANCE = new ComparableComparator();
    381
    382            @SuppressWarnings("unchecked") // OK to cast, because comparator works for all types
    383            public static <E extends Comparable<? super E>> ComparableComparator<E> getInstance() {
    384                return (ComparableComparator<E>) INSTANCE;
    385            }
    386
    387            public ComparableComparator() {
    388                super();
    389            }
    390
    391            public int compare(E obj1, E obj2) {
    392                return obj1.compareTo(obj2);
    393            }
    394
    395            @Override
    396            public int hashCode() {
    397                return "ComparableComparator".hashCode();
    398            }
    399
    400            @Override
    401            public boolean equals(Object object) {
    402                return (this == object) ||
    403                       ((null != object) && (object.getClass().equals(this.getClass())));
    404            }
    405
    406        }
    407
    408    }