View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.lang.math;
18  
19  /**
20   * <p><code>Range</code> represents a range of numbers of the same type.</p>
21   * 
22   * <p>Specific subclasses hold the range values as different types. Each
23   * subclass should be immutable and {@link java.io.Serializable Serializable}
24   * if possible.</p>
25   *
26   * @author Stephen Colebourne
27   * @since 2.0
28   * @version $Id: Range.java 437554 2006-08-28 06:21:41Z bayard $
29   */
30  public abstract class Range {
31  
32      /**
33       * <p>Constructs a new range.</p>
34       */
35      public Range() {
36          super();
37      }
38  
39      // Accessors
40      //--------------------------------------------------------------------
41  
42      /**
43       * <p>Gets the minimum number in this range.</p>
44       *
45       * @return the minimum number in this range
46       */
47      public abstract Number getMinimumNumber();
48  
49      /**
50       * <p>Gets the minimum number in this range as a <code>long</code>.</p>
51       * 
52       * <p>This implementation uses the {@link #getMinimumNumber()} method. 
53       * Subclasses may be able to optimise this.</p>
54       *
55       * @return the minimum number in this range
56       */
57      public long getMinimumLong() {
58          return getMinimumNumber().longValue();
59      }
60  
61      /**
62       * <p>Gets the minimum number in this range as a <code>int</code>.</p>
63       * 
64       * <p>This implementation uses the {@link #getMinimumNumber()} method. 
65       * Subclasses may be able to optimise this.</p>
66       *
67       * @return the minimum number in this range
68       */
69      public int getMinimumInteger() {
70          return getMinimumNumber().intValue();
71      }
72  
73      /**
74       * <p>Gets the minimum number in this range as a <code>double</code>.</p>
75       * 
76       * <p>This implementation uses the {@link #getMinimumNumber()} method. 
77       * Subclasses may be able to optimise this.</p>
78       *
79       * @return the minimum number in this range
80       */
81      public double getMinimumDouble() {
82          return getMinimumNumber().doubleValue();
83      }
84  
85      /**
86       * <p>Gets the minimum number in this range as a <code>float</code>.</p>
87       * 
88       * <p>This implementation uses the {@link #getMinimumNumber()} method. 
89       * Subclasses may be able to optimise this.</p>
90       *
91       * @return the minimum number in this range
92       */
93      public float getMinimumFloat() {
94          return getMinimumNumber().floatValue();
95      }
96  
97      /**
98       * <p>Gets the maximum number in this range.</p>
99       *
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 }