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    }