View Javadoc

1   /*
2    * Copyright 2004-2005, 2008 the original author or authors.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5    * use this file except in compliance with the License. You may obtain a copy of
6    * the License at
7    * 
8    * http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations under
14   * the License.
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 		// the .TYPE entries probably aren't needed, but they don't hurt
214 		// anything :)
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 		// the .TYPE entries probably aren't needed, but they don't hurt
230 		// anything :)
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 		// not sure if this is valid, but putting it in for now
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 		// if the comparison equals the bad compare to result, return true
361 		// to indicate that the number is indeed out of bounds
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 }