View Javadoc

1   package org.apache.velocity.runtime.parser.node;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.    
20   */
21  
22  import java.math.BigDecimal;
23  import java.math.BigInteger;
24  import java.util.HashMap;
25  import java.util.Map;
26  import java.util.List;
27  import java.util.ArrayList;
28  
29  /**
30   * Utility-class for all arithmetic-operations.<br><br>
31   *
32   * All operations (+ - / *) return a Number which type is the type of the bigger argument.<br>
33   * Example:<br>
34   * <code>add ( new Integer(10), new Integer(1))</code> will return an <code>Integer</code>-Object with the value 11<br>
35   * <code>add ( new Long(10), new Integer(1))</code> will return an <code>Long</code>-Object with the value 11<br>
36   * <code>add ( new Integer(10), new Float(1))</code> will return an <code>Float</code>-Object with the value 11<br><br>
37   *
38   * Overflow checking:<br>
39   * For integral values (byte, short, int) there is an implicit overflow correction (the next "bigger"
40   * type will be returned). For example, if you call <code>add (new Integer (Integer.MAX_VALUE), 1)</code> a
41   * <code>Long</code>-object will be returned with the correct value of <code>Integer.MAX_VALUE+1</code>.<br>
42   * In addition to that the methods <code>multiply</code>,<code>add</code> and <code>substract</code> implement overflow
43   * checks for <code>long</code>-values. That means that if an overflow occurs while working with long values a BigInteger
44   * will be returned.<br>
45   * For all other operations and types (such as Float and Double) there is no overflow checking.
46   *
47   * @author <a href="mailto:pero@antaramusic.de">Peter Romianowski</a>
48   */
49  
50  public abstract class MathUtils
51  {
52  
53      /**
54       * A BigDecimal representing the number 0
55       */
56      protected static final BigDecimal DECIMAL_ZERO    = new BigDecimal ( BigInteger.ZERO );
57  
58      /**
59       * The constants are used to determine in which context we have to calculate.
60       */
61      protected static final int BASE_LONG          = 0;
62      protected static final int BASE_FLOAT         = 1;
63      protected static final int BASE_DOUBLE        = 2;
64      protected static final int BASE_BIGINTEGER    = 3;
65      protected static final int BASE_BIGDECIMAL    = 4;
66  
67      /**
68       * The <code>Class</code>-object is key, the maximum-value is the value
69       */
70      protected static final Map ints = new HashMap();
71      static
72      {
73          ints.put (Byte.class, BigDecimal.valueOf (Byte.MAX_VALUE));
74          ints.put (Short.class, BigDecimal.valueOf (Short.MAX_VALUE));
75          ints.put (Integer.class, BigDecimal.valueOf (Integer.MAX_VALUE));
76          ints.put (Long.class, BigDecimal.valueOf (Long.MAX_VALUE));
77          ints.put (BigInteger.class, BigDecimal.valueOf (-1));
78      }
79  
80      /**
81       * The "size" of the number-types - ascending.
82       */
83      protected static final List typesBySize = new ArrayList();
84      static
85      {
86          typesBySize.add (Byte.class);
87          typesBySize.add (Short.class);
88          typesBySize.add (Integer.class);
89          typesBySize.add (Long.class);
90          typesBySize.add (Float.class);
91          typesBySize.add (Double.class);
92      }
93  
94      /**
95       * Convert the given Number to a BigDecimal
96       * @param n
97       * @return The number as BigDecimal
98       */
99      public static BigDecimal toBigDecimal (Number n)
100     {
101 
102         if (n instanceof BigDecimal)
103         {
104             return (BigDecimal)n;
105         }
106 
107         if (n instanceof BigInteger)
108         {
109             return new BigDecimal ( (BigInteger)n );
110         }
111 
112         return new BigDecimal (n.doubleValue());
113 
114     }
115 
116     /**
117      * Convert the given Number to a BigInteger
118      * @param n
119      * @return The number as BigInteger
120      */
121     public static BigInteger toBigInteger (Number n)
122     {
123 
124         if (n instanceof BigInteger)
125         {
126             return (BigInteger)n;
127         }
128 
129         return BigInteger.valueOf (n.longValue());
130 
131     }
132 
133     /**
134      * Compare the given Number to 0.
135      * @param n
136      * @return True if number is 0.
137      */
138     public static boolean isZero (Number n)
139     {
140         if (isInteger( n ) )
141         {
142             if (n instanceof BigInteger)
143             {
144                 return ((BigInteger)n).compareTo (BigInteger.ZERO) == 0;
145             }
146             return n.doubleValue() == 0;
147         }
148         if (n instanceof Float)
149         {
150             return n.floatValue() == 0f;
151         }
152         if (n instanceof Double)
153         {
154             return n.doubleValue() == 0d;
155         }
156         return toBigDecimal( n ).compareTo( DECIMAL_ZERO) == 0;
157     }
158 
159     /**
160      * Test, whether the given object is an integer value
161      * (Byte, Short, Integer, Long, BigInteger)
162      * @param n
163      * @return True if n is an integer.
164      */
165     public static boolean isInteger (Number n)
166     {
167         return ints.containsKey (n.getClass());
168     }
169 
170     /**
171      * Wrap the given primitive into the given class if the value is in the
172      * range of the destination type. If not the next bigger type will be chosen.
173      * @param value
174      * @param type
175      * @return Number object representing the primitive.
176      */
177     public static Number wrapPrimitive (long value, Class type)
178     {
179         if (type == Byte.class)
180         {
181             if (value > Byte.MAX_VALUE || value < Byte.MIN_VALUE)
182             {
183                 type = Short.class;
184             }
185             else
186             {
187                 // TODO: JDK 1.4+ -> valueOf()
188                 return new Byte ((byte)value);
189             }
190         }
191         if (type == Short.class)
192         {
193             if (value > Short.MAX_VALUE || value < Short.MIN_VALUE)
194             {
195                 type = Integer.class;
196             }
197             else
198             {
199                 // TODO: JDK 1.4+ -> valueOf()
200                 return new Short((short)value);
201             }
202         }
203         if (type == Integer.class)
204         {
205             if (value > Integer.MAX_VALUE || value < Integer.MIN_VALUE)
206             {
207                 type = Long.class;
208             }
209             else
210             {
211                 // TODO: JDK 1.4+ -> valueOf()
212                 return new Integer ((int)value);
213             }
214         }
215         if (type == Long.class)
216         {
217             // TODO: JDK 1.4+ -> valueOf()
218             return new Long (value);
219         }
220         return BigInteger.valueOf( value);
221     }
222 
223     /**
224      * Wrap the result in the object of the bigger type.
225      * 
226      * @param value result of operation (as a long) - used to check size
227      * @param op1 first operand of binary operation
228      * @param op2 second operand of binary operation
229      * @return Number object of appropriate size to fit the value and operators
230      */
231     private static Number wrapPrimitive (long value, Number op1, Number op2)
232     {
233         if ( typesBySize.indexOf( op1.getClass()) > typesBySize.indexOf( op2.getClass()))
234         {
235             return wrapPrimitive( value, op1.getClass());
236         }
237         return wrapPrimitive( value, op2.getClass());
238     }
239 
240     /**
241      * Find the common Number-type to be used in calculations.
242      * 
243      * @param op1 first operand of binary operation
244      * @param op2 second operand of binary operation
245      * @return constant indicating type of Number to use in calculations
246      */
247     private static int findCalculationBase (Number op1, Number op2)
248     {
249 
250         boolean op1Int = isInteger(op1);
251         boolean op2Int = isInteger(op2);
252 
253         if ( (op1 instanceof BigDecimal || op2 instanceof BigDecimal) ||
254              ( (!op1Int || !op2Int) && (op1 instanceof BigInteger || op2 instanceof BigInteger)) )
255         {
256             return BASE_BIGDECIMAL;
257         }
258 
259         if (op1Int && op2Int) {
260             if (op1 instanceof BigInteger || op2 instanceof BigInteger)
261             {
262                 return BASE_BIGINTEGER;
263             }
264             return BASE_LONG;
265         }
266 
267         if ((op1 instanceof Double) || (op2 instanceof Double))
268         {
269             return BASE_DOUBLE;
270         }
271         return BASE_FLOAT;
272     }
273 
274     /**
275      * Add two numbers and return the correct value / type.
276      * Overflow detection is done for integer values (byte, short, int, long) only!
277      * @param op1
278      * @param op2
279      * @return Addition result.
280      */
281     public static Number add (Number op1, Number op2)
282     {
283 
284         int calcBase = findCalculationBase( op1, op2);
285         switch (calcBase)
286         {
287             case BASE_BIGINTEGER:
288                 return toBigInteger( op1 ).add( toBigInteger( op2 ));
289             case BASE_LONG:
290                 long l1 = op1.longValue();
291                 long l2 = op2.longValue();
292                 long result = l1+l2;
293 
294                 // Overflow check
295                 if ((result ^ l1) < 0 && (result ^ l2) < 0)
296                 {
297                     return toBigInteger( op1).add( 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 ).add( toBigDecimal( op2 ));
308         }
309     }
310 
311     /**
312      * Subtract 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 Subtraction result.
317      */
318     public static Number subtract (Number op1, Number op2) {
319 
320         int calcBase = findCalculationBase( op1, op2);
321         switch (calcBase) {
322             case BASE_BIGINTEGER:
323                 return toBigInteger( op1 ).subtract( toBigInteger( op2 ));
324             case BASE_LONG:
325                 long l1 = op1.longValue();
326                 long l2 = op2.longValue();
327                 long result = l1-l2;
328 
329                 // Overflow check
330                 if ((result ^ l1) < 0 && (result ^ ~l2) < 0) {
331                     return toBigInteger( op1).subtract( 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 ).subtract( toBigDecimal( op2 ));
342         }
343     }
344 
345     /**
346      * Multiply two numbers and return the correct value / type.
347      * Overflow detection is done for integer values (byte, short, int, long) only!
348      * @param op1
349      * @param op2
350      * @return Multiplication result.
351      */
352     public static Number multiply (Number op1, Number op2) {
353 
354         int calcBase = findCalculationBase( op1, op2);
355         switch (calcBase) {
356             case BASE_BIGINTEGER:
357                 return toBigInteger( op1 ).multiply( toBigInteger( op2 ));
358             case BASE_LONG:
359                 long l1 = op1.longValue();
360                 long l2 = op2.longValue();
361                 long result = l1*l2;
362 
363                 // Overflow detection
364                 if ((l2 != 0) && (result / l2 != l1)) {
365                     return toBigInteger( op1).multiply( toBigInteger( op2));
366                 }
367                 return wrapPrimitive( result, op1, op2);
368             case BASE_FLOAT:
369                 return new Float (op1.floatValue()*op2.floatValue());
370             case BASE_DOUBLE:
371                 return new Double (op1.doubleValue()*op2.doubleValue());
372 
373             // Default is BigDecimal operation
374             default:
375                 return toBigDecimal( op1 ).multiply( toBigDecimal( op2 ));
376         }
377     }
378 
379     /**
380      * Divide two numbers. The result will be returned as Integer-type if and only if
381      * both sides of the division operator are Integer-types. Otherwise a Float, Double,
382      * or BigDecimal will be returned.
383      * @param op1
384      * @param op2
385      * @return Division result.
386      */
387     public static Number divide (Number op1, Number op2) {
388 
389         int calcBase = findCalculationBase( op1, op2);
390         switch (calcBase) {
391             case BASE_BIGINTEGER:
392                 BigInteger b1 = toBigInteger( op1 );
393                 BigInteger b2 = toBigInteger( op2 );
394                 return b1.divide( b2);
395 
396             case BASE_LONG:
397                 long l1 = op1.longValue();
398                 long l2 = op2.longValue();
399                 return wrapPrimitive( l1 / l2, op1, op2);
400 
401             case BASE_FLOAT:
402                 return new Float (op1.floatValue()/op2.floatValue());
403             case BASE_DOUBLE:
404                 return new Double (op1.doubleValue()/op2.doubleValue());
405 
406             // Default is BigDecimal operation
407             default:
408                 return toBigDecimal( op1 ).divide( toBigDecimal( op2 ), BigDecimal.ROUND_HALF_DOWN);
409         }
410     }
411 
412     /**
413      * Modulo two numbers.
414      * @param op1
415      * @param op2
416      * @return Modulo result.
417      *
418      * @throws ArithmeticException If at least one parameter is a BigDecimal
419      */
420     public static Number modulo (Number op1, Number op2) throws ArithmeticException {
421 
422         int calcBase = findCalculationBase( op1, op2);
423         switch (calcBase) {
424             case BASE_BIGINTEGER:
425                 return toBigInteger( op1 ).mod( toBigInteger( op2 ));
426             case BASE_LONG:
427                 return wrapPrimitive( op1.longValue() % op2.longValue(), op1, op2);
428             case BASE_FLOAT:
429                 return new Float (op1.floatValue() % op2.floatValue());
430             case BASE_DOUBLE:
431                 return new Double (op1.doubleValue() % op2.doubleValue());
432 
433             // Default is BigDecimal operation
434             default:
435                 throw new ArithmeticException( "Cannot calculate the modulo of BigDecimals.");
436         }
437     }
438 
439     /**
440      * Compare two numbers.
441      * @param op1
442      * @param op2
443      * @return 1 if n1 > n2, -1 if n1 < n2 and 0 if equal.
444      */
445     public static int compare (Number op1, Number op2) {
446 
447         int calcBase = findCalculationBase( op1, op2);
448         switch (calcBase) {
449             case BASE_BIGINTEGER:
450                 return toBigInteger( op1 ).compareTo( toBigInteger( op2 ));
451             case BASE_LONG:
452                 long l1 = op1.longValue();
453                 long l2 = op2.longValue();
454                 if (l1 < l2) {
455                     return -1;
456                 }
457                 if (l1 > l2) {
458                     return 1;
459                 }
460                 return 0;
461             case BASE_FLOAT:
462                 float f1 = op1.floatValue();
463                 float f2 = op2.floatValue();
464                 if (f1 < f2) {
465                     return -1;
466                 }
467                 if (f1 > f2) {
468                     return 1;
469                 }
470                 return 0;
471             case BASE_DOUBLE:
472                 double d1 = op1.doubleValue();
473                 double d2 = op2.doubleValue();
474                 if (d1 < d2) {
475                     return -1;
476                 }
477                 if (d1 > d2) {
478                     return 1;
479                 }
480                 return 0;
481 
482             // Default is BigDecimal operation
483             default:
484                 return toBigDecimal( op1 ).compareTo( toBigDecimal ( op2 ));
485         }
486     }
487 }