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 }