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    }