1. /* ====================================================================
  2. * The Apache Software License, Version 1.1
  3. *
  4. * Copyright (c) 2002-2003 The Apache Software Foundation. All rights
  5. * reserved.
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions
  9. * are met:
  10. *
  11. * 1. Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. *
  14. * 2. Redistributions in binary form must reproduce the above copyright
  15. * notice, this list of conditions and the following disclaimer in
  16. * the documentation and/or other materials provided with the
  17. * distribution.
  18. *
  19. * 3. The end-user documentation included with the redistribution, if
  20. * any, must include the following acknowledgement:
  21. * "This product includes software developed by the
  22. * Apache Software Foundation (http://www.apache.org/)."
  23. * Alternately, this acknowledgement may appear in the software itself,
  24. * if and wherever such third-party acknowledgements normally appear.
  25. *
  26. * 4. The names "The Jakarta Project", "Commons", and "Apache Software
  27. * Foundation" must not be used to endorse or promote products derived
  28. * from this software without prior written permission. For written
  29. * permission, please contact apache@apache.org.
  30. *
  31. * 5. Products derived from this software may not be called "Apache"
  32. * nor may "Apache" appear in their names without prior written
  33. * permission of the Apache Software Foundation.
  34. *
  35. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  36. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  37. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  38. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  39. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  40. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  41. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  42. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  43. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  44. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  45. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  46. * SUCH DAMAGE.
  47. * ====================================================================
  48. *
  49. * This software consists of voluntary contributions made by many
  50. * individuals on behalf of the Apache Software Foundation. For more
  51. * information on the Apache Software Foundation, please see
  52. * <http://www.apache.org/>.
  53. */
  54. package org.apache.commons.lang.math;
  55. import java.io.Serializable;
  56. /**
  57. * <p><code>NumberRange</code> represents an inclusive range of
  58. * {@link java.lang.Number} objects of the same type.</p>
  59. *
  60. * @author <a href="mailto:chrise@esha.com">Christopher Elkins</a>
  61. * @author Stephen Colebourne
  62. * @since 2.0 (previously in org.apache.commons.lang)
  63. * @version $Id: NumberRange.java,v 1.6 2003/08/18 02:22:24 bayard Exp $
  64. */
  65. public final class NumberRange extends Range implements Serializable {
  66. private static final long serialVersionUID = 71849363892710L;
  67. /**
  68. * The minimum number in this range.
  69. */
  70. private final Number min;
  71. /**
  72. * The maximum number in this range.
  73. */
  74. private final Number max;
  75. /**
  76. * Cached output hashCode (class is immutable).
  77. */
  78. private transient int hashCode = 0;
  79. /**
  80. * Cached output toString (class is immutable).
  81. */
  82. private transient String toString = null;
  83. /**
  84. * <p>Constructs a new <code>NumberRange</code> using the specified
  85. * number as both the minimum and maximum in this range.</p>
  86. *
  87. * @param num the number to use for this range
  88. * @throws IllegalArgumentException if the number is <code>null</code>
  89. * @throws IllegalArgumentException if the number doesn't implement <code>Comparable</code>
  90. * @throws IllegalArgumentException if the number is <code>Double.NaN</code> or <code>Float.NaN</code>
  91. */
  92. public NumberRange(Number num) {
  93. if (num == null) {
  94. throw new IllegalArgumentException("The number must not be null");
  95. }
  96. if (num instanceof Comparable == false) {
  97. throw new IllegalArgumentException("The number must implement Comparable");
  98. }
  99. if (num instanceof Double && ((Double) num).isNaN()) {
  100. throw new IllegalArgumentException("The number must not be NaN");
  101. }
  102. if (num instanceof Float && ((Float) num).isNaN()) {
  103. throw new IllegalArgumentException("The number must not be NaN");
  104. }
  105. this.min = num;
  106. this.max = num;
  107. }
  108. /**
  109. * <p>Constructs a new <code>NumberRange</code> with the specified
  110. * minimum and maximum numbers (both inclusive).</p>
  111. *
  112. * <p>The arguments may be passed in the order (min,max) or (max,min). The
  113. * {@link #getMinimumNumber()} and {@link #getMaximumNumber()} methods will return the
  114. * correct value.</p>
  115. *
  116. * <p>This constructor is designed to be used with two <code>Number</code>
  117. * objects of the same type. If two objects of different types are passed in,
  118. * an exception is thrown.</p>
  119. *
  120. * @param num1 first number that defines the edge of the range, inclusive
  121. * @param num2 second number that defines the edge of the range, inclusive
  122. * @throws IllegalArgumentException if either number is <code>null</code>
  123. * @throws IllegalArgumentException if the numbers are of different types
  124. * @throws IllegalArgumentException if the numbers don't implement <code>Comparable</code>
  125. */
  126. public NumberRange(Number num1, Number num2) {
  127. if (num1 == null || num2 == null) {
  128. throw new IllegalArgumentException("The numbers must not be null");
  129. }
  130. if (num1.getClass() != num2.getClass()) {
  131. throw new IllegalArgumentException("The numbers must be of the same type");
  132. }
  133. if (num1 instanceof Comparable == false) {
  134. throw new IllegalArgumentException("The numbers must implement Comparable");
  135. }
  136. if (num1 instanceof Double) {
  137. if (((Double) num1).isNaN() || ((Double) num2).isNaN()) {
  138. throw new IllegalArgumentException("The number must not be NaN");
  139. }
  140. } else if (num1 instanceof Float) {
  141. if (((Float) num1).isNaN() || ((Float) num2).isNaN()) {
  142. throw new IllegalArgumentException("The number must not be NaN");
  143. }
  144. }
  145. int compare = ((Comparable) num1).compareTo(num2);
  146. if (compare == 0) {
  147. this.min = num1;
  148. this.max = num1;
  149. } else if (compare > 0) {
  150. this.min = num2;
  151. this.max = num1;
  152. } else {
  153. this.min = num1;
  154. this.max = num2;
  155. }
  156. }
  157. // Accessors
  158. //--------------------------------------------------------------------
  159. /**
  160. * <p>Returns the minimum number in this range.</p>
  161. *
  162. * @return the minimum number in this range
  163. */
  164. public Number getMinimumNumber() {
  165. return min;
  166. }
  167. /**
  168. * <p>Returns the maximum number in this range.</p>
  169. *
  170. * @return the maximum number in this range
  171. */
  172. public Number getMaximumNumber() {
  173. return max;
  174. }
  175. // Tests
  176. //--------------------------------------------------------------------
  177. /**
  178. * <p>Tests whether the specified <code>number</code> occurs within
  179. * this range.</p>
  180. *
  181. * <p><code>null</code> is handled and returns <code>false</code>.</p>
  182. *
  183. * @param number the number to test, may be <code>null</code>
  184. * @return <code>true</code> if the specified number occurs within this range
  185. * @throws IllegalArgumentException if the number is of a different type to the range
  186. */
  187. public boolean containsNumber(Number number) {
  188. if (number == null) {
  189. return false;
  190. }
  191. if (number.getClass() != min.getClass()) {
  192. throw new IllegalArgumentException("The number must be of the same type as the range numbers");
  193. }
  194. int compareMin = ((Comparable) min).compareTo(number);
  195. int compareMax = ((Comparable) max).compareTo(number);
  196. return (compareMin <= 0 && compareMax >= 0);
  197. }
  198. // Range tests
  199. //--------------------------------------------------------------------
  200. // use Range implementations
  201. // Basics
  202. //--------------------------------------------------------------------
  203. /**
  204. * <p>Compares this range to another object to test if they are equal.</p>.
  205. *
  206. * <p>To be equal, the class, minimum and maximum must be equal.</p>
  207. *
  208. * @param obj the reference object with which to compare
  209. * @return <code>true</code> if this object is equal
  210. */
  211. public boolean equals(Object obj) {
  212. if (obj == this) {
  213. return true;
  214. }
  215. if (obj instanceof NumberRange == false) {
  216. return false;
  217. }
  218. NumberRange range = (NumberRange) obj;
  219. return min.equals(range.min) && max.equals(range.max);
  220. }
  221. /**
  222. * <p>Gets a hashCode for the range.</p>
  223. *
  224. * @return a hash code value for this object
  225. */
  226. public int hashCode() {
  227. if (hashCode == 0) {
  228. hashCode = 17;
  229. hashCode = 37 * hashCode + getClass().hashCode();
  230. hashCode = 37 * hashCode + min.hashCode();
  231. hashCode = 37 * hashCode + max.hashCode();
  232. }
  233. return hashCode;
  234. }
  235. /**
  236. * <p>Gets the range as a <code>String</code>.</p>
  237. *
  238. * <p>The format of the String is 'Range[<i>min</i>,<i>max</i>]'.</p>
  239. *
  240. * @return the <code>String</code> representation of this range
  241. */
  242. public String toString() {
  243. if (toString == null) {
  244. StringBuffer buf = new StringBuffer(32);
  245. buf.append("Range[");
  246. buf.append(min);
  247. buf.append(',');
  248. buf.append(max);
  249. buf.append(']');
  250. toString = buf.toString();
  251. }
  252. return toString;
  253. }
  254. }