001: package org.apache.velocity.runtime.parser.node;
002:
003: /*
004: * Licensed to the Apache Software Foundation (ASF) under one
005: * or more contributor license agreements. See the NOTICE file
006: * distributed with this work for additional information
007: * regarding copyright ownership. The ASF licenses this file
008: * to you under the Apache License, Version 2.0 (the
009: * "License"); you may not use this file except in compliance
010: * with the License. You may obtain a copy of the License at
011: *
012: * http://www.apache.org/licenses/LICENSE-2.0
013: *
014: * Unless required by applicable law or agreed to in writing,
015: * software distributed under the License is distributed on an
016: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017: * KIND, either express or implied. See the License for the
018: * specific language governing permissions and limitations
019: * under the License.
020: */
021:
022: import java.math.BigDecimal;
023: import java.math.BigInteger;
024: import java.util.HashMap;
025: import java.util.Map;
026: import java.util.List;
027: import java.util.ArrayList;
028:
029: /**
030: * Utility-class for all arithmetic-operations.<br><br>
031: *
032: * All operations (+ - / *) return a Number which type is the type of the bigger argument.<br>
033: * Example:<br>
034: * <code>add ( new Integer(10), new Integer(1))</code> will return an <code>Integer</code>-Object with the value 11<br>
035: * <code>add ( new Long(10), new Integer(1))</code> will return an <code>Long</code>-Object with the value 11<br>
036: * <code>add ( new Integer(10), new Float(1))</code> will return an <code>Float</code>-Object with the value 11<br><br>
037: *
038: * Overflow checking:<br>
039: * For integral values (byte, short, int) there is an implicit overflow correction (the next "bigger"
040: * type will be returned). For example, if you call <code>add (new Integer (Integer.MAX_VALUE), 1)</code> a
041: * <code>Long</code>-object will be returned with the correct value of <code>Integer.MAX_VALUE+1</code>.<br>
042: * In addition to that the methods <code>multiply</code>,<code>add</code> and <code>substract</code> implement overflow
043: * checks for <code>long</code>-values. That means that if an overflow occurs while working with long values a BigInteger
044: * will be returned.<br>
045: * For all other operations and types (such as Float and Double) there is no overflow checking.
046: *
047: * @author <a href="mailto:pero@antaramusic.de">Peter Romianowski</a>
048: */
049:
050: public abstract class MathUtils {
051:
052: /**
053: * A BigDecimal representing the number 0
054: */
055: protected static final BigDecimal DECIMAL_ZERO = new BigDecimal(
056: BigInteger.ZERO);
057:
058: /**
059: * The constants are used to determine in which context we have to calculate.
060: */
061: protected static final int BASE_LONG = 0;
062: protected static final int BASE_FLOAT = 1;
063: protected static final int BASE_DOUBLE = 2;
064: protected static final int BASE_BIGINTEGER = 3;
065: protected static final int BASE_BIGDECIMAL = 4;
066:
067: /**
068: * The <code>Class</code>-object is key, the maximum-value is the value
069: */
070: protected static final Map ints = new HashMap();
071: static {
072: ints.put(Byte.class, BigDecimal.valueOf(Byte.MAX_VALUE));
073: ints.put(Short.class, BigDecimal.valueOf(Short.MAX_VALUE));
074: ints.put(Integer.class, BigDecimal.valueOf(Integer.MAX_VALUE));
075: ints.put(Long.class, BigDecimal.valueOf(Long.MAX_VALUE));
076: ints.put(BigInteger.class, BigDecimal.valueOf(-1));
077: }
078:
079: /**
080: * The "size" of the number-types - ascending.
081: */
082: protected static final List typesBySize = new ArrayList();
083: static {
084: typesBySize.add(Byte.class);
085: typesBySize.add(Short.class);
086: typesBySize.add(Integer.class);
087: typesBySize.add(Long.class);
088: typesBySize.add(Float.class);
089: typesBySize.add(Double.class);
090: }
091:
092: /**
093: * Convert the given Number to a BigDecimal
094: * @param n
095: * @return The number as BigDecimal
096: */
097: public static BigDecimal toBigDecimal(Number n) {
098:
099: if (n instanceof BigDecimal) {
100: return (BigDecimal) n;
101: }
102:
103: if (n instanceof BigInteger) {
104: return new BigDecimal((BigInteger) n);
105: }
106:
107: return new BigDecimal(n.doubleValue());
108:
109: }
110:
111: /**
112: * Convert the given Number to a BigInteger
113: * @param n
114: * @return The number as BigInteger
115: */
116: public static BigInteger toBigInteger(Number n) {
117:
118: if (n instanceof BigInteger) {
119: return (BigInteger) n;
120: }
121:
122: return BigInteger.valueOf(n.longValue());
123:
124: }
125:
126: /**
127: * Compare the given Number to 0.
128: * @param n
129: * @return True if number is 0.
130: */
131: public static boolean isZero(Number n) {
132: if (isInteger(n)) {
133: if (n instanceof BigInteger) {
134: return ((BigInteger) n).compareTo(BigInteger.ZERO) == 0;
135: }
136: return n.doubleValue() == 0;
137: }
138: if (n instanceof Float) {
139: return n.floatValue() == 0f;
140: }
141: if (n instanceof Double) {
142: return n.doubleValue() == 0d;
143: }
144: return toBigDecimal(n).compareTo(DECIMAL_ZERO) == 0;
145: }
146:
147: /**
148: * Test, whether the given object is an integer value
149: * (Byte, Short, Integer, Long, BigInteger)
150: * @param n
151: * @return True if n is an integer.
152: */
153: public static boolean isInteger(Number n) {
154: return ints.containsKey(n.getClass());
155: }
156:
157: /**
158: * Wrap the given primitive into the given class if the value is in the
159: * range of the destination type. If not the next bigger type will be chosen.
160: * @param value
161: * @param type
162: * @return Number object representing the primitive.
163: */
164: public static Number wrapPrimitive(long value, Class type) {
165: if (type == Byte.class) {
166: if (value > Byte.MAX_VALUE || value < Byte.MIN_VALUE) {
167: type = Short.class;
168: } else {
169: // TODO: JDK 1.4+ -> valueOf()
170: return new Byte((byte) value);
171: }
172: }
173: if (type == Short.class) {
174: if (value > Short.MAX_VALUE || value < Short.MIN_VALUE) {
175: type = Integer.class;
176: } else {
177: // TODO: JDK 1.4+ -> valueOf()
178: return new Short((short) value);
179: }
180: }
181: if (type == Integer.class) {
182: if (value > Integer.MAX_VALUE || value < Integer.MIN_VALUE) {
183: type = Long.class;
184: } else {
185: // TODO: JDK 1.4+ -> valueOf()
186: return new Integer((int) value);
187: }
188: }
189: if (type == Long.class) {
190: // TODO: JDK 1.4+ -> valueOf()
191: return new Long(value);
192: }
193: return BigInteger.valueOf(value);
194: }
195:
196: /**
197: * Wrap the result in the object of the bigger type.
198: *
199: * @param value result of operation (as a long) - used to check size
200: * @param op1 first operand of binary operation
201: * @param op2 second operand of binary operation
202: * @return Number object of appropriate size to fit the value and operators
203: */
204: private static Number wrapPrimitive(long value, Number op1,
205: Number op2) {
206: if (typesBySize.indexOf(op1.getClass()) > typesBySize
207: .indexOf(op2.getClass())) {
208: return wrapPrimitive(value, op1.getClass());
209: }
210: return wrapPrimitive(value, op2.getClass());
211: }
212:
213: /**
214: * Find the common Number-type to be used in calculations.
215: *
216: * @param op1 first operand of binary operation
217: * @param op2 second operand of binary operation
218: * @return constant indicating type of Number to use in calculations
219: */
220: private static int findCalculationBase(Number op1, Number op2) {
221:
222: boolean op1Int = isInteger(op1);
223: boolean op2Int = isInteger(op2);
224:
225: if ((op1 instanceof BigDecimal || op2 instanceof BigDecimal)
226: || ((!op1Int || !op2Int) && (op1 instanceof BigInteger || op2 instanceof BigInteger))) {
227: return BASE_BIGDECIMAL;
228: }
229:
230: if (op1Int && op2Int) {
231: if (op1 instanceof BigInteger || op2 instanceof BigInteger) {
232: return BASE_BIGINTEGER;
233: }
234: return BASE_LONG;
235: }
236:
237: if ((op1 instanceof Double) || (op2 instanceof Double)) {
238: return BASE_DOUBLE;
239: }
240: return BASE_FLOAT;
241: }
242:
243: /**
244: * Add two numbers and return the correct value / type.
245: * Overflow detection is done for integer values (byte, short, int, long) only!
246: * @param op1
247: * @param op2
248: * @return Addition result.
249: */
250: public static Number add(Number op1, Number op2) {
251:
252: int calcBase = findCalculationBase(op1, op2);
253: switch (calcBase) {
254: case BASE_BIGINTEGER:
255: return toBigInteger(op1).add(toBigInteger(op2));
256: case BASE_LONG:
257: long l1 = op1.longValue();
258: long l2 = op2.longValue();
259: long result = l1 + l2;
260:
261: // Overflow check
262: if ((result ^ l1) < 0 && (result ^ l2) < 0) {
263: return toBigInteger(op1).add(toBigInteger(op2));
264: }
265: return wrapPrimitive(result, op1, op2);
266: case BASE_FLOAT:
267: return new Float(op1.floatValue() + op2.floatValue());
268: case BASE_DOUBLE:
269: return new Double(op1.doubleValue() + op2.doubleValue());
270:
271: // Default is BigDecimal operation
272: default:
273: return toBigDecimal(op1).add(toBigDecimal(op2));
274: }
275: }
276:
277: /**
278: * Subtract two numbers and return the correct value / type.
279: * Overflow detection is done for integer values (byte, short, int, long) only!
280: * @param op1
281: * @param op2
282: * @return Subtraction result.
283: */
284: public static Number subtract(Number op1, Number op2) {
285:
286: int calcBase = findCalculationBase(op1, op2);
287: switch (calcBase) {
288: case BASE_BIGINTEGER:
289: return toBigInteger(op1).subtract(toBigInteger(op2));
290: case BASE_LONG:
291: long l1 = op1.longValue();
292: long l2 = op2.longValue();
293: long result = l1 - l2;
294:
295: // Overflow check
296: if ((result ^ l1) < 0 && (result ^ ~l2) < 0) {
297: return toBigInteger(op1).subtract(toBigInteger(op2));
298: }
299: return wrapPrimitive(result, op1, op2);
300: case BASE_FLOAT:
301: return new Float(op1.floatValue() - op2.floatValue());
302: case BASE_DOUBLE:
303: return new Double(op1.doubleValue() - op2.doubleValue());
304:
305: // Default is BigDecimal operation
306: default:
307: return toBigDecimal(op1).subtract(toBigDecimal(op2));
308: }
309: }
310:
311: /**
312: * Multiply two numbers and return the correct value / type.
313: * Overflow detection is done for integer values (byte, short, int, long) only!
314: * @param op1
315: * @param op2
316: * @return Multiplication result.
317: */
318: public static Number multiply(Number op1, Number op2) {
319:
320: int calcBase = findCalculationBase(op1, op2);
321: switch (calcBase) {
322: case BASE_BIGINTEGER:
323: return toBigInteger(op1).multiply(toBigInteger(op2));
324: case BASE_LONG:
325: long l1 = op1.longValue();
326: long l2 = op2.longValue();
327: long result = l1 * l2;
328:
329: // Overflow detection
330: if ((l2 != 0) && (result / l2 != l1)) {
331: return toBigInteger(op1).multiply(toBigInteger(op2));
332: }
333: return wrapPrimitive(result, op1, op2);
334: case BASE_FLOAT:
335: return new Float(op1.floatValue() * op2.floatValue());
336: case BASE_DOUBLE:
337: return new Double(op1.doubleValue() * op2.doubleValue());
338:
339: // Default is BigDecimal operation
340: default:
341: return toBigDecimal(op1).multiply(toBigDecimal(op2));
342: }
343: }
344:
345: /**
346: * Divide two numbers. The result will be returned as Integer-type if and only if
347: * both sides of the division operator are Integer-types. Otherwise a Float, Double,
348: * or BigDecimal will be returned.
349: * @param op1
350: * @param op2
351: * @return Division result.
352: */
353: public static Number divide(Number op1, Number op2) {
354:
355: int calcBase = findCalculationBase(op1, op2);
356: switch (calcBase) {
357: case BASE_BIGINTEGER:
358: BigInteger b1 = toBigInteger(op1);
359: BigInteger b2 = toBigInteger(op2);
360: return b1.divide(b2);
361:
362: case BASE_LONG:
363: long l1 = op1.longValue();
364: long l2 = op2.longValue();
365: return wrapPrimitive(l1 / l2, op1, op2);
366:
367: case BASE_FLOAT:
368: return new Float(op1.floatValue() / op2.floatValue());
369: case BASE_DOUBLE:
370: return new Double(op1.doubleValue() / op2.doubleValue());
371:
372: // Default is BigDecimal operation
373: default:
374: return toBigDecimal(op1).divide(toBigDecimal(op2),
375: BigDecimal.ROUND_HALF_DOWN);
376: }
377: }
378:
379: /**
380: * Modulo two numbers.
381: * @param op1
382: * @param op2
383: * @return Modulo result.
384: *
385: * @throws ArithmeticException If at least one parameter is a BigDecimal
386: */
387: public static Number modulo(Number op1, Number op2)
388: throws ArithmeticException {
389:
390: int calcBase = findCalculationBase(op1, op2);
391: switch (calcBase) {
392: case BASE_BIGINTEGER:
393: return toBigInteger(op1).mod(toBigInteger(op2));
394: case BASE_LONG:
395: return wrapPrimitive(op1.longValue() % op2.longValue(),
396: op1, op2);
397: case BASE_FLOAT:
398: return new Float(op1.floatValue() % op2.floatValue());
399: case BASE_DOUBLE:
400: return new Double(op1.doubleValue() % op2.doubleValue());
401:
402: // Default is BigDecimal operation
403: default:
404: throw new ArithmeticException(
405: "Cannot calculate the modulo of BigDecimals.");
406: }
407: }
408:
409: /**
410: * Compare two numbers.
411: * @param op1
412: * @param op2
413: * @return 1 if n1 > n2, -1 if n1 < n2 and 0 if equal.
414: */
415: public static int compare(Number op1, Number op2) {
416:
417: int calcBase = findCalculationBase(op1, op2);
418: switch (calcBase) {
419: case BASE_BIGINTEGER:
420: return toBigInteger(op1).compareTo(toBigInteger(op2));
421: case BASE_LONG:
422: long l1 = op1.longValue();
423: long l2 = op2.longValue();
424: if (l1 < l2) {
425: return -1;
426: }
427: if (l1 > l2) {
428: return 1;
429: }
430: return 0;
431: case BASE_FLOAT:
432: float f1 = op1.floatValue();
433: float f2 = op2.floatValue();
434: if (f1 < f2) {
435: return -1;
436: }
437: if (f1 > f2) {
438: return 1;
439: }
440: return 0;
441: case BASE_DOUBLE:
442: double d1 = op1.doubleValue();
443: double d2 = op2.doubleValue();
444: if (d1 < d2) {
445: return -1;
446: }
447: if (d1 > d2) {
448: return 1;
449: }
450: return 0;
451:
452: // Default is BigDecimal operation
453: default:
454: return toBigDecimal(op1).compareTo(toBigDecimal(op2));
455: }
456: }
457: }
|