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