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>NumberRange</code> represents an inclusive range of 025 * {@link java.lang.Number} objects of the same type.</p> 026 * 027 * @author Apache Software Foundation 028 * @author <a href="mailto:chrise@esha.com">Christopher Elkins</a> 029 * @since 2.0 (previously in org.apache.commons.lang) 030 * @version $Id: NumberRange.java 1057072 2011-01-10 01:55:57Z niallp $ 031 */ 032 public final class NumberRange extends Range implements Serializable { 033 034 /** 035 * Required for serialization support. 036 * 037 * @see java.io.Serializable 038 */ 039 private static final long serialVersionUID = 71849363892710L; 040 041 /** 042 * The minimum number in this range. 043 */ 044 private final Number min; 045 /** 046 * The maximum number in this range. 047 */ 048 private final Number max; 049 050 /** 051 * Cached output hashCode (class is immutable). 052 */ 053 private transient int hashCode = 0; 054 /** 055 * Cached output toString (class is immutable). 056 */ 057 private transient String toString = null; 058 059 /** 060 * <p>Constructs a new <code>NumberRange</code> using the specified 061 * number as both the minimum and maximum in this range.</p> 062 * 063 * @param num the number to use for this range 064 * @throws IllegalArgumentException if the number is <code>null</code> 065 * @throws IllegalArgumentException if the number doesn't implement <code>Comparable</code> 066 * @throws IllegalArgumentException if the number is <code>Double.NaN</code> or <code>Float.NaN</code> 067 */ 068 public NumberRange(Number num) { 069 if (num == null) { 070 throw new IllegalArgumentException("The number must not be null"); 071 } 072 if (num instanceof Comparable == false) { 073 throw new IllegalArgumentException("The number must implement Comparable"); 074 } 075 if (num instanceof Double && ((Double) num).isNaN()) { 076 throw new IllegalArgumentException("The number must not be NaN"); 077 } 078 if (num instanceof Float && ((Float) num).isNaN()) { 079 throw new IllegalArgumentException("The number must not be NaN"); 080 } 081 082 this.min = num; 083 this.max = num; 084 } 085 086 /** 087 * <p>Constructs a new <code>NumberRange</code> with the specified 088 * minimum and maximum numbers (both inclusive).</p> 089 * 090 * <p>The arguments may be passed in the order (min,max) or (max,min). The 091 * {@link #getMinimumNumber()} and {@link #getMaximumNumber()} methods will return the 092 * correct value.</p> 093 * 094 * <p>This constructor is designed to be used with two <code>Number</code> 095 * objects of the same type. If two objects of different types are passed in, 096 * an exception is thrown.</p> 097 * 098 * @param num1 first number that defines the edge of the range, inclusive 099 * @param num2 second number that defines the edge of the range, inclusive 100 * @throws IllegalArgumentException if either number is <code>null</code> 101 * @throws IllegalArgumentException if the numbers are of different types 102 * @throws IllegalArgumentException if the numbers don't implement <code>Comparable</code> 103 */ 104 public NumberRange(Number num1, Number num2) { 105 if (num1 == null || num2 == null) { 106 throw new IllegalArgumentException("The numbers must not be null"); 107 } 108 if (num1.getClass() != num2.getClass()) { 109 throw new IllegalArgumentException("The numbers must be of the same type"); 110 } 111 if (num1 instanceof Comparable == false) { 112 throw new IllegalArgumentException("The numbers must implement Comparable"); 113 } 114 if (num1 instanceof Double) { 115 if (((Double) num1).isNaN() || ((Double) num2).isNaN()) { 116 throw new IllegalArgumentException("The number must not be NaN"); 117 } 118 } else if (num1 instanceof Float) { 119 if (((Float) num1).isNaN() || ((Float) num2).isNaN()) { 120 throw new IllegalArgumentException("The number must not be NaN"); 121 } 122 } 123 124 int compare = ((Comparable) num1).compareTo(num2); 125 if (compare == 0) { 126 this.min = num1; 127 this.max = num1; 128 } else if (compare > 0) { 129 this.min = num2; 130 this.max = num1; 131 } else { 132 this.min = num1; 133 this.max = num2; 134 } 135 } 136 137 // Accessors 138 //-------------------------------------------------------------------- 139 140 /** 141 * <p>Returns the minimum number in this range.</p> 142 * 143 * @return the minimum number in this range 144 */ 145 public Number getMinimumNumber() { 146 return min; 147 } 148 149 /** 150 * <p>Returns the maximum number in this range.</p> 151 * 152 * @return the maximum number in this range 153 */ 154 public Number getMaximumNumber() { 155 return max; 156 } 157 158 // Tests 159 //-------------------------------------------------------------------- 160 161 /** 162 * <p>Tests whether the specified <code>number</code> occurs within 163 * this range.</p> 164 * 165 * <p><code>null</code> is handled and returns <code>false</code>.</p> 166 * 167 * @param number the number to test, may be <code>null</code> 168 * @return <code>true</code> if the specified number occurs within this range 169 * @throws IllegalArgumentException if the number is of a different type to the range 170 */ 171 public boolean containsNumber(Number number) { 172 if (number == null) { 173 return false; 174 } 175 if (number.getClass() != min.getClass()) { 176 throw new IllegalArgumentException("The number must be of the same type as the range numbers"); 177 } 178 int compareMin = ((Comparable) min).compareTo(number); 179 int compareMax = ((Comparable) max).compareTo(number); 180 return compareMin <= 0 && compareMax >= 0; 181 } 182 183 // Range tests 184 //-------------------------------------------------------------------- 185 // use Range implementations 186 187 // Basics 188 //-------------------------------------------------------------------- 189 190 /** 191 * <p>Compares this range to another object to test if they are equal.</p>. 192 * 193 * <p>To be equal, the class, minimum and maximum must be equal.</p> 194 * 195 * @param obj the reference object with which to compare 196 * @return <code>true</code> if this object is equal 197 */ 198 public boolean equals(Object obj) { 199 if (obj == this) { 200 return true; 201 } 202 if (obj instanceof NumberRange == false) { 203 return false; 204 } 205 NumberRange range = (NumberRange) obj; 206 return min.equals(range.min) && max.equals(range.max); 207 } 208 209 /** 210 * <p>Gets a hashCode for the range.</p> 211 * 212 * @return a hash code value for this object 213 */ 214 public int hashCode() { 215 if (hashCode == 0) { 216 hashCode = 17; 217 hashCode = 37 * hashCode + getClass().hashCode(); 218 hashCode = 37 * hashCode + min.hashCode(); 219 hashCode = 37 * hashCode + max.hashCode(); 220 } 221 return hashCode; 222 } 223 224 /** 225 * <p>Gets the range as a <code>String</code>.</p> 226 * 227 * <p>The format of the String is 'Range[<i>min</i>,<i>max</i>]'.</p> 228 * 229 * @return the <code>String</code> representation of this range 230 */ 231 public String toString() { 232 if (toString == null) { 233 StrBuilder buf = new StrBuilder(32); 234 buf.append("Range["); 235 buf.append(min); 236 buf.append(','); 237 buf.append(max); 238 buf.append(']'); 239 toString = buf.toString(); 240 } 241 return toString; 242 } 243 244 }