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 java.io.Serializable;
020:
021: /**
022: * <p><code>DoubleRange</code> represents an inclusive range of <code>double</code>s.</p>
023: *
024: * @author Stephen Colebourne
025: * @since 2.0
026: * @version $Id: DoubleRange.java 437554 2006-08-28 06:21:41Z bayard $
027: */
028: public final class DoubleRange extends Range implements Serializable {
029:
030: /**
031: * Required for serialization support.
032: *
033: * @see java.io.Serializable
034: */
035: private static final long serialVersionUID = 71849363892740L;
036:
037: /**
038: * The minimum number in this range (inclusive).
039: */
040: private final double min;
041: /**
042: * The maximum number in this range (inclusive).
043: */
044: private final double max;
045:
046: /**
047: * Cached output minObject (class is immutable).
048: */
049: private transient Double minObject = null;
050: /**
051: * Cached output maxObject (class is immutable).
052: */
053: private transient Double maxObject = null;
054: /**
055: * Cached output hashCode (class is immutable).
056: */
057: private transient int hashCode = 0;
058: /**
059: * Cached output toString (class is immutable).
060: */
061: private transient String toString = null;
062:
063: /**
064: * <p>Constructs a new <code>DoubleRange</code> using the specified
065: * number as both the minimum and maximum in this range.</p>
066: *
067: * @param number the number to use for this range
068: * @throws IllegalArgumentException if the number is <code>NaN</code>
069: */
070: public DoubleRange(double number) {
071: super ();
072: if (Double.isNaN(number)) {
073: throw new IllegalArgumentException(
074: "The number must not be NaN");
075: }
076: this .min = number;
077: this .max = number;
078: }
079:
080: /**
081: * <p>Constructs a new <code>DoubleRange</code> using the specified
082: * number as both the minimum and maximum in this range.</p>
083: *
084: * @param number the number to use for this range, must not
085: * be <code>null</code>
086: * @throws IllegalArgumentException if the number is <code>null</code>
087: * @throws IllegalArgumentException if the number is <code>NaN</code>
088: */
089: public DoubleRange(Number number) {
090: super ();
091: if (number == null) {
092: throw new IllegalArgumentException(
093: "The number must not be null");
094: }
095: this .min = number.doubleValue();
096: this .max = number.doubleValue();
097: if (Double.isNaN(min) || Double.isNaN(max)) {
098: throw new IllegalArgumentException(
099: "The number must not be NaN");
100: }
101: if (number instanceof Double) {
102: this .minObject = (Double) number;
103: this .maxObject = (Double) number;
104: }
105: }
106:
107: /**
108: * <p>Constructs a new <code>DoubleRange</code> with the specified
109: * minimum and maximum numbers (both inclusive).</p>
110: *
111: * <p>The arguments may be passed in the order (min,max) or (max,min). The
112: * getMinimum and getMaximum methods will return the correct values.</p>
113: *
114: * @param number1 first number that defines the edge of the range, inclusive
115: * @param number2 second number that defines the edge of the range, inclusive
116: * @throws IllegalArgumentException if either number is <code>NaN</code>
117: */
118: public DoubleRange(double number1, double number2) {
119: super ();
120: if (Double.isNaN(number1) || Double.isNaN(number2)) {
121: throw new IllegalArgumentException(
122: "The numbers must not be NaN");
123: }
124: if (number2 < number1) {
125: this .min = number2;
126: this .max = number1;
127: } else {
128: this .min = number1;
129: this .max = number2;
130: }
131: }
132:
133: /**
134: * <p>Constructs a new <code>DoubleRange</code> with the specified
135: * minimum and maximum numbers (both inclusive).</p>
136: *
137: * <p>The arguments may be passed in the order (min,max) or (max,min). The
138: * getMinimum and getMaximum methods will return the correct values.</p>
139: *
140: * @param number1 first number that defines the edge of the range, inclusive
141: * @param number2 second number that defines the edge of the range, inclusive
142: * @throws IllegalArgumentException if either number is <code>null</code>
143: * @throws IllegalArgumentException if either number is <code>NaN</code>
144: */
145: public DoubleRange(Number number1, Number number2) {
146: super ();
147: if (number1 == null || number2 == null) {
148: throw new IllegalArgumentException(
149: "The numbers must not be null");
150: }
151: double number1val = number1.doubleValue();
152: double number2val = number2.doubleValue();
153: if (Double.isNaN(number1val) || Double.isNaN(number2val)) {
154: throw new IllegalArgumentException(
155: "The numbers must not be NaN");
156: }
157: if (number2val < number1val) {
158: this .min = number2val;
159: this .max = number1val;
160: if (number2 instanceof Double) {
161: this .minObject = (Double) number2;
162: }
163: if (number1 instanceof Double) {
164: this .maxObject = (Double) number1;
165: }
166: } else {
167: this .min = number1val;
168: this .max = number2val;
169: if (number1 instanceof Double) {
170: this .minObject = (Double) number1;
171: }
172: if (number2 instanceof Double) {
173: this .maxObject = (Double) number2;
174: }
175: }
176: }
177:
178: // Accessors
179: //--------------------------------------------------------------------
180:
181: /**
182: * <p>Returns the minimum number in this range.</p>
183: *
184: * @return the minimum number in this range
185: */
186: public Number getMinimumNumber() {
187: if (minObject == null) {
188: minObject = new Double(min);
189: }
190: return minObject;
191: }
192:
193: /**
194: * <p>Gets the minimum number in this range as a <code>long</code>.</p>
195: *
196: * <p>This conversion can lose information for large values or decimals.</p>
197: *
198: * @return the minimum number in this range
199: */
200: public long getMinimumLong() {
201: return (long) min;
202: }
203:
204: /**
205: * <p>Gets the minimum number in this range as a <code>int</code>.</p>
206: *
207: * <p>This conversion can lose information for large values or decimals.</p>
208: *
209: * @return the minimum number in this range
210: */
211: public int getMinimumInteger() {
212: return (int) min;
213: }
214:
215: /**
216: * <p>Gets the minimum number in this range as a <code>double</code>.</p>
217: *
218: * @return the minimum number in this range
219: */
220: public double getMinimumDouble() {
221: return min;
222: }
223:
224: /**
225: * <p>Gets the minimum number in this range as a <code>float</code>.</p>
226: *
227: * <p>This conversion can lose information for large values.</p>
228: *
229: * @return the minimum number in this range
230: */
231: public float getMinimumFloat() {
232: return (float) min;
233: }
234:
235: /**
236: * <p>Returns the maximum number in this range.</p>
237: *
238: * @return the maximum number in this range
239: */
240: public Number getMaximumNumber() {
241: if (maxObject == null) {
242: maxObject = new Double(max);
243: }
244: return maxObject;
245: }
246:
247: /**
248: * <p>Gets the maximum number in this range as a <code>long</code>.</p>
249: *
250: * <p>This conversion can lose information for large values or decimals.</p>
251: *
252: * @return the maximum number in this range
253: */
254: public long getMaximumLong() {
255: return (long) max;
256: }
257:
258: /**
259: * <p>Gets the maximum number in this range as a <code>int</code>.</p>
260: *
261: * <p>This conversion can lose information for large values or decimals.</p>
262: *
263: * @return the maximum number in this range
264: */
265: public int getMaximumInteger() {
266: return (int) max;
267: }
268:
269: /**
270: * <p>Gets the maximum number in this range as a <code>double</code>.</p>
271: *
272: * @return the maximum number in this range
273: */
274: public double getMaximumDouble() {
275: return max;
276: }
277:
278: /**
279: * <p>Gets the maximum number in this range as a <code>float</code>.</p>
280: *
281: * <p>This conversion can lose information for large values.</p>
282: *
283: * @return the maximum number in this range
284: */
285: public float getMaximumFloat() {
286: return (float) max;
287: }
288:
289: // Tests
290: //--------------------------------------------------------------------
291:
292: /**
293: * <p>Tests whether the specified <code>number</code> occurs within
294: * this range using <code>double</code> comparison.</p>
295: *
296: * <p><code>null</code> is handled and returns <code>false</code>.</p>
297: *
298: * @param number the number to test, may be <code>null</code>
299: * @return <code>true</code> if the specified number occurs within this range
300: */
301: public boolean containsNumber(Number number) {
302: if (number == null) {
303: return false;
304: }
305: return containsDouble(number.doubleValue());
306: }
307:
308: /**
309: * <p>Tests whether the specified <code>double</code> occurs within
310: * this range using <code>double</code> comparison.</p>
311: *
312: * <p>This implementation overrides the superclass for performance as it is
313: * the most common case.</p>
314: *
315: * @param value the double to test
316: * @return <code>true</code> if the specified number occurs within this
317: * range by <code>double</code> comparison
318: */
319: public boolean containsDouble(double value) {
320: return value >= min && value <= max;
321: }
322:
323: // Range tests
324: //--------------------------------------------------------------------
325:
326: /**
327: * <p>Tests whether the specified range occurs entirely within this range
328: * using <code>double</code> comparison.</p>
329: *
330: * <p><code>null</code> is handled and returns <code>false</code>.</p>
331: *
332: * @param range the range to test, may be <code>null</code>
333: * @return <code>true</code> if the specified range occurs entirely within this range
334: * @throws IllegalArgumentException if the range is not of this type
335: */
336: public boolean containsRange(Range range) {
337: if (range == null) {
338: return false;
339: }
340: return containsDouble(range.getMinimumDouble())
341: && containsDouble(range.getMaximumDouble());
342: }
343:
344: /**
345: * <p>Tests whether the specified range overlaps with this range
346: * using <code>double</code> comparison.</p>
347: *
348: * <p><code>null</code> is handled and returns <code>false</code>.</p>
349: *
350: * @param range the range to test, may be <code>null</code>
351: * @return <code>true</code> if the specified range overlaps with this range
352: */
353: public boolean overlapsRange(Range range) {
354: if (range == null) {
355: return false;
356: }
357: return range.containsDouble(min) || range.containsDouble(max)
358: || containsDouble(range.getMinimumDouble());
359: }
360:
361: // Basics
362: //--------------------------------------------------------------------
363:
364: /**
365: * <p>Compares this range to another object to test if they are equal.</p>.
366: *
367: * <p>To be equal, the class, minimum and maximum must be equal.</p>
368: *
369: * @param obj the reference object with which to compare
370: * @return <code>true</code> if this object is equal
371: */
372: public boolean equals(Object obj) {
373: if (obj == this ) {
374: return true;
375: }
376: if (obj instanceof DoubleRange == false) {
377: return false;
378: }
379: DoubleRange range = (DoubleRange) obj;
380: return (Double.doubleToLongBits(min) == Double
381: .doubleToLongBits(range.min) && Double
382: .doubleToLongBits(max) == Double
383: .doubleToLongBits(range.max));
384: }
385:
386: /**
387: * <p>Gets a hashCode for the range.</p>
388: *
389: * @return a hash code value for this object
390: */
391: public int hashCode() {
392: if (hashCode == 0) {
393: hashCode = 17;
394: hashCode = 37 * hashCode + getClass().hashCode();
395: long lng = Double.doubleToLongBits(min);
396: hashCode = 37 * hashCode + ((int) (lng ^ (lng >> 32)));
397: lng = Double.doubleToLongBits(max);
398: hashCode = 37 * hashCode + ((int) (lng ^ (lng >> 32)));
399: }
400: return hashCode;
401: }
402:
403: /**
404: * <p>Gets the range as a <code>String</code>.</p>
405: *
406: * <p>The format of the String is 'Range[<i>min</i>,<i>max</i>]'.</p>
407: *
408: * @return the <code>String</code> representation of this range
409: */
410: public String toString() {
411: if (toString == null) {
412: StringBuffer buf = new StringBuffer(32);
413: buf.append("Range[");
414: buf.append(min);
415: buf.append(',');
416: buf.append(max);
417: buf.append(']');
418: toString = buf.toString();
419: }
420: return toString;
421: }
422:
423: }
|