1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package net.sf.morph.util;
17
18 import java.lang.reflect.Constructor;
19 import java.lang.reflect.Method;
20 import java.math.BigDecimal;
21 import java.math.BigInteger;
22 import java.util.Comparator;
23 import java.util.HashMap;
24 import java.util.Iterator;
25 import java.util.Map;
26 import java.util.Set;
27
28 import net.sf.composite.util.ObjectUtils;
29 import net.sf.morph.reflect.ReflectionException;
30
31 /**
32 * Various values and utility functions that are useful when working with
33 * numbers.
34 *
35 * @author Matt Sgarlata
36 * @since Dec 15, 2004
37 */
38 public abstract class NumberUtils {
39
40 private static abstract class NumberFactory {
41 static Class[] paramTypes = new Class[] { String.class };
42 Number get(String s) {
43 try {
44 return (Number) iget(s);
45 } catch (Exception e) {
46 throw new ReflectionException(e);
47 }
48 }
49 protected abstract Object iget(String s) throws Exception;
50 }
51
52 private static class MethodNumberFactory extends NumberFactory {
53 private Method m;
54 MethodNumberFactory(Class c) throws Exception {
55 m = c.getMethod("valueOf", paramTypes);
56 }
57 protected Object iget(String s) throws Exception {
58 return m.invoke(null, new Object[] { s });
59 }
60 }
61
62 private static class ConstructorNumberFactory extends NumberFactory {
63 private Constructor cs;
64 ConstructorNumberFactory(Class c) throws Exception {
65 cs = c.getConstructor(paramTypes);
66 }
67 protected Object iget(String s) throws Exception {
68 return cs.newInstance(new Object[] { s });
69 }
70 }
71
72 private static class NarrownessComparator implements Comparator {
73 /**
74 * {@inheritDoc}
75 */
76 public int compare(Object arg0, Object arg1) {
77 if (arg0 == arg1) {
78 return 0;
79 }
80 Class c0 = getType(arg0);
81 Class c1 = getType(arg1);
82 if (c0 == c1 || c0 == null || c1 == null) {
83 return 0;
84 }
85 return getMaximumForType(c0).compareTo(getMaximumForType(c1));
86 }
87
88 private Class getType(Object o) {
89 if (MAXIMUMS_FOR_TYPES.containsKey(o)) {
90 return (Class) o;
91 }
92 Class test = ClassUtils.getClass(o);
93 return MAXIMUMS_FOR_TYPES.containsKey(test) ? test : null;
94 }
95 }
96
97 /**
98 * A Map of BigDecimals keyed by Class that indicate the maximum value that
99 * the given (Number) Class may taken on.
100 */
101 public static final Map MAXIMUMS_FOR_TYPES;
102
103 /**
104 * A Map of BigDecimals keyed by Class that indicate the minimum value that
105 * the given (Number) Class may taken on.
106 */
107 public static final Map MINIMUMS_FOR_TYPES;
108
109 /**
110 * Comparator of class/object type narrowness.
111 */
112 public static final Comparator NARROWNESS_COMPARATOR = new NarrownessComparator();
113
114 private static final Map WRAPPERS_FOR_PRIMITIVE_TYPES;
115
116 private static final Map NUMBER_FACTORIES;
117
118 /**
119 * Used by {@link NumberUtils#isNumber(Class)}.
120 */
121 protected static final Class[] BASE_NUMBER_TYPES;
122
123 /**
124 * The maximum value a long can have.
125 *
126 * @see Long#MAX_VALUE
127 */
128 public static final BigDecimal MAX_LONG = new BigDecimal("" + Long.MAX_VALUE);
129
130 /**
131 * The maximum value an integer can have.
132 *
133 * @see Integer#MAX_VALUE
134 */
135 public static final BigDecimal MAX_INTEGER = new BigDecimal("" + Integer.MAX_VALUE);
136
137 /**
138 * The maximum value a short can have.
139 *
140 * @see Short#MAX_VALUE
141 */
142 public static final BigDecimal MAX_SHORT = new BigDecimal("" + Short.MAX_VALUE);
143
144 /**
145 * The maximum value a byte can have.
146 *
147 * @see Byte#MAX_VALUE
148 */
149 public static final BigDecimal MAX_BYTE = new BigDecimal("" + Byte.MAX_VALUE);
150
151 /**
152 * The maximum value a double can have.
153 *
154 * @see Double#MAX_VALUE
155 */
156 public static final BigDecimal MAX_DOUBLE = new BigDecimal("" + Double.MAX_VALUE);
157
158 /**
159 * The maximum value a float can have.
160 *
161 * @see Float#MAX_VALUE
162 */
163 public static final BigDecimal MAX_FLOAT = new BigDecimal("" + Float.MAX_VALUE);
164
165 /**
166 * The minimum value a long can have.
167 *
168 * @see Long#MIN_VALUE
169 */
170 public static final BigDecimal MIN_LONG = new BigDecimal("" + Long.MIN_VALUE);
171
172 /**
173 * The minimum value an integer can have.
174 *
175 * @see Long#MIN_VALUE
176 */
177 public static final BigDecimal MIN_INTEGER = new BigDecimal("" + Integer.MIN_VALUE);
178
179 /**
180 * The minimum value a short can have.
181 *
182 * @see Long#MIN_VALUE
183 */
184 public static final BigDecimal MIN_SHORT = new BigDecimal("" + Short.MIN_VALUE);
185
186 /**
187 * The minimum value a byte can have.
188 *
189 * @see Long#MIN_VALUE
190 */
191 public static final BigDecimal MIN_BYTE = new BigDecimal("" + Byte.MIN_VALUE);
192
193 /**
194 * The minimum value a double can have.
195 *
196 * @see Long#MIN_VALUE
197 */
198 public static final BigDecimal MIN_DOUBLE = new BigDecimal("-" + Double.MAX_VALUE);
199
200 /**
201 * The minimum value a float can have.
202 *
203 * @see Long#MIN_VALUE
204 */
205 public static final BigDecimal MIN_FLOAT = new BigDecimal("-" + Float.MAX_VALUE);
206
207 /**
208 * A BigDecimal containing the value zero (0).
209 */
210 public static final BigDecimal ZERO = new BigDecimal("0");
211
212 static {
213
214
215 MAXIMUMS_FOR_TYPES = new HashMap();
216 MAXIMUMS_FOR_TYPES.put(Long.class, MAX_LONG);
217 MAXIMUMS_FOR_TYPES.put(Long.TYPE, MAX_LONG);
218 MAXIMUMS_FOR_TYPES.put(Integer.class, MAX_INTEGER);
219 MAXIMUMS_FOR_TYPES.put(Integer.TYPE, MAX_INTEGER);
220 MAXIMUMS_FOR_TYPES.put(Short.class, MAX_SHORT);
221 MAXIMUMS_FOR_TYPES.put(Short.TYPE, MAX_SHORT);
222 MAXIMUMS_FOR_TYPES.put(Byte.class, MAX_BYTE);
223 MAXIMUMS_FOR_TYPES.put(Byte.TYPE, MAX_BYTE);
224 MAXIMUMS_FOR_TYPES.put(Double.class, MAX_DOUBLE);
225 MAXIMUMS_FOR_TYPES.put(Double.TYPE, MAX_DOUBLE);
226 MAXIMUMS_FOR_TYPES.put(Float.class, MAX_FLOAT);
227 MAXIMUMS_FOR_TYPES.put(Float.TYPE, MAX_FLOAT);
228
229
230
231 MINIMUMS_FOR_TYPES = new HashMap();
232 MINIMUMS_FOR_TYPES.put(Long.class, MIN_LONG);
233 MINIMUMS_FOR_TYPES.put(Long.TYPE, MIN_LONG);
234 MINIMUMS_FOR_TYPES.put(Integer.class, MIN_INTEGER);
235 MINIMUMS_FOR_TYPES.put(Integer.TYPE, MIN_INTEGER);
236 MINIMUMS_FOR_TYPES.put(Short.class, MIN_SHORT);
237 MINIMUMS_FOR_TYPES.put(Short.TYPE, MIN_SHORT);
238 MINIMUMS_FOR_TYPES.put(Byte.class, MIN_BYTE);
239 MINIMUMS_FOR_TYPES.put(Byte.TYPE, MIN_BYTE);
240 MINIMUMS_FOR_TYPES.put(Double.class, MIN_DOUBLE);
241 MINIMUMS_FOR_TYPES.put(Double.TYPE, MIN_DOUBLE);
242 MINIMUMS_FOR_TYPES.put(Float.class, MIN_FLOAT);
243 MINIMUMS_FOR_TYPES.put(Float.TYPE, MIN_FLOAT);
244
245 WRAPPERS_FOR_PRIMITIVE_TYPES = new HashMap();
246 WRAPPERS_FOR_PRIMITIVE_TYPES.put(Long.TYPE, Long.class);
247 WRAPPERS_FOR_PRIMITIVE_TYPES.put(Integer.TYPE, Integer.class);
248 WRAPPERS_FOR_PRIMITIVE_TYPES.put(Short.TYPE, Short.class);
249 WRAPPERS_FOR_PRIMITIVE_TYPES.put(Byte.TYPE, Byte.class);
250 WRAPPERS_FOR_PRIMITIVE_TYPES.put(Double.TYPE, Double.class);
251 WRAPPERS_FOR_PRIMITIVE_TYPES.put(Float.TYPE, Float.class);
252
253
254 WRAPPERS_FOR_PRIMITIVE_TYPES.put(Void.TYPE, Void.class);
255
256 NUMBER_FACTORIES = new HashMap();
257 try {
258 for (Iterator it = WRAPPERS_FOR_PRIMITIVE_TYPES.entrySet().iterator(); it.hasNext();) {
259 Map.Entry e = (Map.Entry) it.next();
260 Class c = (Class) e.getValue();
261 if (c == Void.class) {
262 continue;
263 }
264 NumberFactory nf = new MethodNumberFactory(c);
265 NUMBER_FACTORIES.put(c, nf);
266 NUMBER_FACTORIES.put(e.getKey(), nf);
267 }
268 NUMBER_FACTORIES.put(BigInteger.class, new ConstructorNumberFactory(BigInteger.class));
269 NUMBER_FACTORIES.put(BigDecimal.class, new ConstructorNumberFactory(BigDecimal.class));
270 } catch (Exception e) {
271 throw new ReflectionException(e);
272 }
273
274 Set baseNumberTypes = ContainerUtils.createOrderedSet();
275 baseNumberTypes.addAll(MAXIMUMS_FOR_TYPES.keySet());
276 baseNumberTypes.add(Number.class);
277 BASE_NUMBER_TYPES = (Class[]) baseNumberTypes.toArray(new Class[baseNumberTypes.size()]);
278 }
279
280 /**
281 * Returns the maximum allowed value for the given type, which must be a
282 * number.
283 *
284 * @param type
285 * the type
286 * @return the maximum allowed value for the given type, if
287 * <code>type</code> is a number or <br>
288 * <code>null</code>, otherwise
289 */
290 public static BigDecimal getMaximumForType(Class type) {
291 return (BigDecimal) MAXIMUMS_FOR_TYPES.get(type);
292 }
293
294 /**
295 * Returns the minimum allowed value for the given type, which must be a
296 * number.
297 *
298 * @param type
299 * the type
300 * @return the minimum allowed value for the given type, if
301 * <code>type</code> is a number or <br>
302 * <code>null</code>, otherwise
303 */
304 public static BigDecimal getMinimumForType(Class type) {
305 return (BigDecimal) MINIMUMS_FOR_TYPES.get(type);
306 }
307
308 /**
309 * Converts the given number to a BigDecimal.
310 *
311 * @param number
312 * the number to convert
313 * @return <code>null</code>, if number is <code>null</code> or <br>
314 * the given number as a BigDecimal, otherwise
315 */
316 public static BigDecimal numberToBigDecimal(Number number) {
317 return number == null ? null : new BigDecimal(number.toString());
318 }
319
320 /**
321 * Determines if the given type is a number type. A number type is any type
322 * that is a subclass of <code>java.lang.Number</code> or is a primitive
323 * number type.
324 *
325 * @param type
326 * the type to test
327 * @return <code>true</code> if the given type is a number type or
328 * <code>false</code>, otherwise
329 */
330 public static boolean isNumber(Class type) {
331 return ClassUtils.inheritanceContains(BASE_NUMBER_TYPES, type);
332 }
333
334 /**
335 * Returns <code>true</code> if <code>number</code> is capable of
336 * containing a decimal (fractional) component. This method returns
337 * <code>true</code> for Floats, Doubles, and Longs.
338 *
339 * @param number
340 * the number to test
341 * @return <code>false</code>, if <code>number</code> is
342 * <code>null</code> or <br>
343 * <code>true</code> if <code>number</code> is capable of
344 * containing a decimal (fractional) component or <br>
345 * <code>false</code>, otherwise.
346 */
347 public static boolean isDecimal(Number number) {
348 return number != null && (number instanceof Float || number instanceof Double || number instanceof BigDecimal);
349 }
350
351 /**
352 * Implementation of isTooBigForType and isTooSmallForType
353 */
354 protected static boolean isOutOfBoundsForType(Map boundMap, Number number,
355 Class type, int badCompareToResult) {
356 if (number == null || type == BigInteger.class || type == BigDecimal.class) {
357 return false;
358 }
359 BigDecimal boundForType = (BigDecimal) boundMap.get(type);
360
361
362 if (boundForType == null) {
363 throw new IllegalArgumentException("Unable to determine bounds for type "
364 + ObjectUtils.getObjectDescription(type));
365 }
366 return NumberUtils.numberToBigDecimal(number).compareTo(boundForType) == badCompareToResult;
367 }
368
369 /**
370 * Returns <code>true</code> if <code>number</code> represents a value
371 * too large to be stored in an instance of the given <code>type</code>.
372 *
373 * @param number
374 * the number to test
375 * @param the
376 * type to test
377 * @return <code>true</code>, if <code>number</code> represents a value
378 * too large to be stored in an instance of the given
379 * <code>type</code> or <br>
380 * <code>false</code>, otherwise
381 * @throws IllegalArgumentException
382 * if the maximum value cannot be determined for the given type
383 */
384 public static boolean isTooBigForType(Number number, Class type) {
385 return isOutOfBoundsForType(MAXIMUMS_FOR_TYPES, number, type, 1);
386 }
387
388 /**
389 * Returns <code>true</code> if <code>number</code> represents a value
390 * too small (i.e. - with too large a negative absolute value) to be stored
391 * in an instance of the given <code>type</code>.
392 *
393 * @param number
394 * the number to test
395 * @param the
396 * type to test
397 * @return <code>true</code>, if <code>number</code> represents a value
398 * too small (i.e. - with too large a negative absolute value) to be
399 * stored in an instance of the given <code>type</code> or <br>
400 * <code>false</code>, otherwise
401 * @throws IllegalArgumentException
402 * if the minimum value cannot be determined for the given type
403 */
404 public static boolean isTooSmallForType(Number number, Class type) {
405 return isOutOfBoundsForType(MINIMUMS_FOR_TYPES, number, type, -1);
406 }
407
408 /**
409 * Returns <code>true</code> if <code>number</code> has too large an
410 * absolute value to be stored in an instance of the given <code>type</code>.
411 *
412 * @param number
413 * the number to test
414 * @param the
415 * type to test
416 * @return <code>true</code>, if <code>number</code> has too large an
417 * absolute value to be stored in an instance of the given
418 * <code>type</code> or <br>
419 * <code>false</code>, otherwise
420 */
421 public static boolean isOutOfBoundsForType(Number number, Class type) {
422 return
423 number != null &&
424 (isTooBigForType(number, type) ||
425 isTooSmallForType(number, type));
426 }
427
428 /**
429 * @deprecated for {@link ClassUtils#getPrimitiveWrapper(Class)}
430 * @param type
431 * @return Class
432 */
433 public static Class getWrapperForPrimitiveType(Class type) {
434 return (Class) WRAPPERS_FOR_PRIMITIVE_TYPES.get(type);
435 }
436
437 /**
438 * Get a number from a String.
439 * @param type
440 * @param s
441 * @return Number
442 * @throws Exception
443 */
444 public static Number getNumber(Class type, String s) throws Exception {
445 return ((NumberFactory) NUMBER_FACTORIES.get(type)).get(s);
446 }
447
448 }