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 import java.io.Serializable;
20
21 /**
22 * <p><code>FloatRange</code> represents an inclusive range of <code>float</code>s.</p>
23 *
24 * @author Stephen Colebourne
25 * @since 2.0
26 * @version $Id: FloatRange.java 437554 2006-08-28 06:21:41Z bayard $
27 */
28 public final class FloatRange extends Range implements Serializable {
29
30 /**
31 * Required for serialization support.
32 *
33 * @see java.io.Serializable
34 */
35 private static final long serialVersionUID = 71849363892750L;
36
37 /**
38 * The minimum number in this range (inclusive).
39 */
40 private final float min;
41 /**
42 * The maximum number in this range (inclusive).
43 */
44 private final float max;
45
46 /**
47 * Cached output minObject (class is immutable).
48 */
49 private transient Float minObject = null;
50 /**
51 * Cached output maxObject (class is immutable).
52 */
53 private transient Float maxObject = null;
54 /**
55 * Cached output hashCode (class is immutable).
56 */
57 private transient int hashCode = 0;
58 /**
59 * Cached output toString (class is immutable).
60 */
61 private transient String toString = null;
62
63 /**
64 * <p>Constructs a new <code>FloatRange</code> using the specified
65 * number as both the minimum and maximum in this range.</p>
66 *
67 * @param number the number to use for this range
68 * @throws IllegalArgumentException if the number is <code>NaN</code>
69 */
70 public FloatRange(float number) {
71 super();
72 if (Float.isNaN(number)) {
73 throw new IllegalArgumentException("The number must not be NaN");
74 }
75 this.min = number;
76 this.max = number;
77 }
78
79 /**
80 * <p>Constructs a new <code>FloatRange</code> using the specified
81 * number as both the minimum and maximum in this range.</p>
82 *
83 * @param number the number to use for this range, must not
84 * be <code>null</code>
85 * @throws IllegalArgumentException if the number is <code>null</code>
86 * @throws IllegalArgumentException if the number is <code>NaN</code>
87 */
88 public FloatRange(Number number) {
89 super();
90 if (number == null) {
91 throw new IllegalArgumentException("The number must not be null");
92 }
93 this.min = number.floatValue();
94 this.max = number.floatValue();
95 if (Float.isNaN(min) || Float.isNaN(max)) {
96 throw new IllegalArgumentException("The number must not be NaN");
97 }
98 if (number instanceof Float) {
99 this.minObject = (Float) number;
100 this.maxObject = (Float) number;
101 }
102 }
103
104 /**
105 * <p>Constructs a new <code>FloatRange</code> with the specified
106 * minimum and maximum numbers (both inclusive).</p>
107 *
108 * <p>The arguments may be passed in the order (min,max) or (max,min). The
109 * getMinimum and getMaximum methods will return the correct values.</p>
110 *
111 * @param number1 first number that defines the edge of the range, inclusive
112 * @param number2 second number that defines the edge of the range, inclusive
113 * @throws IllegalArgumentException if either number is <code>NaN</code>
114 */
115 public FloatRange(float number1, float number2) {
116 super();
117 if (Float.isNaN(number1) || Float.isNaN(number2)) {
118 throw new IllegalArgumentException("The numbers must not be NaN");
119 }
120 if (number2 < number1) {
121 this.min = number2;
122 this.max = number1;
123 } else {
124 this.min = number1;
125 this.max = number2;
126 }
127 }
128
129 /**
130 * <p>Constructs a new <code>FloatRange</code> with the specified
131 * minimum and maximum numbers (both inclusive).</p>
132 *
133 * <p>The arguments may be passed in the order (min,max) or (max,min). The
134 * getMinimum and getMaximum methods will return the correct values.</p>
135 *
136 * @param number1 first number that defines the edge of the range, inclusive
137 * @param number2 second number that defines the edge of the range, inclusive
138 * @throws IllegalArgumentException if either number is <code>null</code>
139 * @throws IllegalArgumentException if either number is <code>NaN</code>
140 */
141 public FloatRange(Number number1, Number number2) {
142 super();
143 if (number1 == null || number2 == null) {
144 throw new IllegalArgumentException("The numbers must not be null");
145 }
146 float number1val = number1.floatValue();
147 float number2val = number2.floatValue();
148 if (Float.isNaN(number1val) || Float.isNaN(number2val)) {
149 throw new IllegalArgumentException("The numbers must not be NaN");
150 }
151 if (number2val < number1val) {
152 this.min = number2val;
153 this.max = number1val;
154 if (number2 instanceof Float) {
155 this.minObject = (Float) number2;
156 }
157 if (number1 instanceof Float) {
158 this.maxObject = (Float) number1;
159 }
160 } else {
161 this.min = number1val;
162 this.max = number2val;
163 if (number1 instanceof Float) {
164 this.minObject = (Float) number1;
165 }
166 if (number2 instanceof Float) {
167 this.maxObject = (Float) number2;
168 }
169 }
170 }
171
172 // Accessors
173 //--------------------------------------------------------------------
174
175 /**
176 * <p>Returns the minimum number in this range.</p>
177 *
178 * @return the minimum number in this range
179 */
180 public Number getMinimumNumber() {
181 if (minObject == null) {
182 minObject = new Float(min);
183 }
184 return minObject;
185 }
186
187 /**
188 * <p>Gets the minimum number in this range as a <code>long</code>.</p>
189 *
190 * <p>This conversion can lose information for large values or decimals.</p>
191 *
192 * @return the minimum number in this range
193 */
194 public long getMinimumLong() {
195 return (long) min;
196 }
197
198 /**
199 * <p>Gets the minimum number in this range as a <code>int</code>.</p>
200 *
201 * <p>This conversion can lose information for large values or decimals.</p>
202 *
203 * @return the minimum number in this range
204 */
205 public int getMinimumInteger() {
206 return (int) min;
207 }
208
209 /**
210 * <p>Gets the minimum number in this range as a <code>double</code>.</p>
211 *
212 * @return the minimum number in this range
213 */
214 public double getMinimumDouble() {
215 return min;
216 }
217
218 /**
219 * <p>Gets the minimum number in this range as a <code>float</code>.</p>
220 *
221 * @return the minimum number in this range
222 */
223 public float getMinimumFloat() {
224 return min;
225 }
226
227 /**
228 * <p>Returns the maximum number in this range.</p>
229 *
230 * @return the maximum number in this range
231 */
232 public Number getMaximumNumber() {
233 if (maxObject == null) {
234 maxObject = new Float(max);
235 }
236 return maxObject;
237 }
238
239 /**
240 * <p>Gets the maximum number in this range as a <code>long</code>.</p>
241 *
242 * <p>This conversion can lose information for large values or decimals.</p>
243 *
244 * @return the maximum number in this range
245 */
246 public long getMaximumLong() {
247 return (long) max;
248 }
249
250 /**
251 * <p>Gets the maximum number in this range as a <code>int</code>.</p>
252 *
253 * <p>This conversion can lose information for large values or decimals.</p>
254 *
255 * @return the maximum number in this range
256 */
257 public int getMaximumInteger() {
258 return (int) max;
259 }
260
261 /**
262 * <p>Gets the maximum number in this range as a <code>double</code>.</p>
263 *
264 * @return the maximum number in this range
265 */
266 public double getMaximumDouble() {
267 return max;
268 }
269
270 /**
271 * <p>Gets the maximum number in this range as a <code>float</code>.</p>
272 *
273 * @return the maximum number in this range
274 */
275 public float getMaximumFloat() {
276 return max;
277 }
278
279 // Tests
280 //--------------------------------------------------------------------
281
282 /**
283 * <p>Tests whether the specified <code>number</code> occurs within
284 * this range using <code>float</code> comparison.</p>
285 *
286 * <p><code>null</code> is handled and returns <code>false</code>.</p>
287 *
288 * @param number the number to test, may be <code>null</code>
289 * @return <code>true</code> if the specified number occurs within this range
290 */
291 public boolean containsNumber(Number number) {
292 if (number == null) {
293 return false;
294 }
295 return containsFloat(number.floatValue());
296 }
297
298 /**
299 * <p>Tests whether the specified <code>float</code> occurs within
300 * this range using <code>float</code> comparison.</p>
301 *
302 * <p>This implementation overrides the superclass for performance as it is
303 * the most common case.</p>
304 *
305 * @param value the float to test
306 * @return <code>true</code> if the specified number occurs within this
307 * range by <code>float</code> comparison
308 */
309 public boolean containsFloat(float value) {
310 return value >= min && value <= max;
311 }
312
313 // Range tests
314 //--------------------------------------------------------------------
315
316 /**
317 * <p>Tests whether the specified range occurs entirely within this range
318 * using <code>float</code> comparison.</p>
319 *
320 * <p><code>null</code> is handled and returns <code>false</code>.</p>
321 *
322 * @param range the range to test, may be <code>null</code>
323 * @return <code>true</code> if the specified range occurs entirely within this range
324 * @throws IllegalArgumentException if the range is not of this type
325 */
326 public boolean containsRange(Range range) {
327 if (range == null) {
328 return false;
329 }
330 return containsFloat(range.getMinimumFloat()) &&
331 containsFloat(range.getMaximumFloat());
332 }
333
334 /**
335 * <p>Tests whether the specified range overlaps with this range
336 * using <code>float</code> comparison.</p>
337 *
338 * <p><code>null</code> is handled and returns <code>false</code>.</p>
339 *
340 * @param range the range to test, may be <code>null</code>
341 * @return <code>true</code> if the specified range overlaps with this range
342 */
343 public boolean overlapsRange(Range range) {
344 if (range == null) {
345 return false;
346 }
347 return range.containsFloat(min) ||
348 range.containsFloat(max) ||
349 containsFloat(range.getMinimumFloat());
350 }
351
352 // Basics
353 //--------------------------------------------------------------------
354
355 /**
356 * <p>Compares this range to another object to test if they are equal.</p>.
357 *
358 * <p>To be equal, the class, minimum and maximum must be equal.</p>
359 *
360 * @param obj the reference object with which to compare
361 * @return <code>true</code> if this object is equal
362 */
363 public boolean equals(Object obj) {
364 if (obj == this) {
365 return true;
366 }
367 if (obj instanceof FloatRange == false) {
368 return false;
369 }
370 FloatRange range = (FloatRange) obj;
371 return (Float.floatToIntBits(min) == Float.floatToIntBits(range.min) &&
372 Float.floatToIntBits(max) == Float.floatToIntBits(range.max));
373 }
374
375 /**
376 * <p>Gets a hashCode for the range.</p>
377 *
378 * @return a hash code value for this object
379 */
380 public int hashCode() {
381 if (hashCode == 0) {
382 hashCode = 17;
383 hashCode = 37 * hashCode + getClass().hashCode();
384 hashCode = 37 * hashCode + Float.floatToIntBits(min);
385 hashCode = 37 * hashCode + Float.floatToIntBits(max);
386 }
387 return hashCode;
388 }
389
390 /**
391 * <p>Gets the range as a <code>String</code>.</p>
392 *
393 * <p>The format of the String is 'Range[<i>min</i>,<i>max</i>]'.</p>
394 *
395 * @return the <code>String</code> representation of this range
396 */
397 public String toString() {
398 if (toString == null) {
399 StringBuffer buf = new StringBuffer(32);
400 buf.append("Range[");
401 buf.append(min);
402 buf.append(',');
403 buf.append(max);
404 buf.append(']');
405 toString = buf.toString();
406 }
407 return toString;
408 }
409
410 }