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