| package org.apache.velocity.runtime.parser.node; |
| |
| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| import java.math.BigDecimal; |
| import java.math.BigInteger; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| /** |
| * Utility-class for all arithmetic-operations.<br><br> |
| * |
| * All operations (+ - / *) return a Number which type is the type of the bigger argument.<br> |
| * Example:<br> |
| * <code>add ( new Integer(10), new Integer(1))</code> will return an <code>Integer</code>-Object with the value 11<br> |
| * <code>add ( new Long(10), new Integer(1))</code> will return an <code>Long</code>-Object with the value 11<br> |
| * <code>add ( new Integer(10), new Float(1))</code> will return an <code>Float</code>-Object with the value 11<br><br> |
| * |
| * Overflow checking:<br> |
| * For integral values (byte, short, int) there is an implicit overflow correction (the next "bigger" |
| * type will be returned). For example, if you call <code>add (new Integer (Integer.MAX_VALUE), 1)</code> a |
| * <code>Long</code>-object will be returned with the correct value of <code>Integer.MAX_VALUE+1</code>.<br> |
| * In addition to that the methods <code>multiply</code>,<code>add</code> and <code>substract</code> implement overflow |
| * checks for <code>long</code>-values. That means that if an overflow occurs while working with long values a BigInteger |
| * will be returned.<br> |
| * For all other operations and types (such as Float and Double) there is no overflow checking. |
| * |
| * @author <a href="mailto:pero@antaramusic.de">Peter Romianowski</a> |
| * @since 1.5 |
| */ |
| public abstract class MathUtils |
| { |
| |
| /** |
| * A BigDecimal representing the number 0 |
| */ |
| protected static final BigDecimal DECIMAL_ZERO = new BigDecimal ( BigInteger.ZERO ); |
| |
| /** |
| * The constants are used to determine in which context we have to calculate. |
| */ |
| protected static final int BASE_LONG = 0; |
| protected static final int BASE_FLOAT = 1; |
| protected static final int BASE_DOUBLE = 2; |
| protected static final int BASE_BIGINTEGER = 3; |
| protected static final int BASE_BIGDECIMAL = 4; |
| |
| /** |
| * The <code>Class</code>-object is key, the maximum-value is the value |
| */ |
| private static final Map ints = new HashMap(); |
| static |
| { |
| ints.put (Byte.class, BigDecimal.valueOf (Byte.MAX_VALUE)); |
| ints.put (Short.class, BigDecimal.valueOf (Short.MAX_VALUE)); |
| ints.put (Integer.class, BigDecimal.valueOf (Integer.MAX_VALUE)); |
| ints.put (Long.class, BigDecimal.valueOf (Long.MAX_VALUE)); |
| ints.put (BigInteger.class, BigDecimal.valueOf (-1)); |
| } |
| |
| /** |
| * The "size" of the number-types - ascending. |
| */ |
| private static final List typesBySize = new ArrayList(); |
| static |
| { |
| typesBySize.add (Byte.class); |
| typesBySize.add (Short.class); |
| typesBySize.add (Integer.class); |
| typesBySize.add (Long.class); |
| typesBySize.add (Float.class); |
| typesBySize.add (Double.class); |
| } |
| |
| /** |
| * Convert the given Number to a BigDecimal |
| * @param n |
| * @return The number as BigDecimal |
| */ |
| public static BigDecimal toBigDecimal (Number n) |
| { |
| |
| if (n instanceof BigDecimal) |
| { |
| return (BigDecimal)n; |
| } |
| |
| if (n instanceof BigInteger) |
| { |
| return new BigDecimal ( (BigInteger)n ); |
| } |
| |
| return new BigDecimal (n.doubleValue()); |
| |
| } |
| |
| /** |
| * Convert the given Number to a BigInteger |
| * @param n |
| * @return The number as BigInteger |
| */ |
| public static BigInteger toBigInteger (Number n) |
| { |
| |
| if (n instanceof BigInteger) |
| { |
| return (BigInteger)n; |
| } |
| |
| return BigInteger.valueOf (n.longValue()); |
| |
| } |
| |
| /** |
| * Compare the given Number to 0. |
| * @param n |
| * @return True if number is 0. |
| */ |
| public static boolean isZero (Number n) |
| { |
| if (isInteger( n ) ) |
| { |
| if (n instanceof BigInteger) |
| { |
| return ((BigInteger)n).compareTo (BigInteger.ZERO) == 0; |
| } |
| return n.doubleValue() == 0; |
| } |
| if (n instanceof Float) |
| { |
| return n.floatValue() == 0f; |
| } |
| if (n instanceof Double) |
| { |
| return n.doubleValue() == 0d; |
| } |
| return toBigDecimal( n ).compareTo( DECIMAL_ZERO) == 0; |
| } |
| |
| /** |
| * Test, whether the given object is an integer value |
| * (Byte, Short, Integer, Long, BigInteger) |
| * @param n |
| * @return True if n is an integer. |
| */ |
| public static boolean isInteger (Number n) |
| { |
| return ints.containsKey (n.getClass()); |
| } |
| |
| /** |
| * Wrap the given primitive into the given class if the value is in the |
| * range of the destination type. If not the next bigger type will be chosen. |
| * @param value |
| * @param type |
| * @return Number object representing the primitive. |
| */ |
| public static Number wrapPrimitive (long value, Class type) |
| { |
| if (type == Byte.class) |
| { |
| if (value > Byte.MAX_VALUE || value < Byte.MIN_VALUE) |
| { |
| type = Short.class; |
| } |
| else |
| { |
| return (byte) value; |
| } |
| } |
| if (type == Short.class) |
| { |
| if (value > Short.MAX_VALUE || value < Short.MIN_VALUE) |
| { |
| type = Integer.class; |
| } |
| else |
| { |
| return (short) value; |
| } |
| } |
| if (type == Integer.class) |
| { |
| if (value > Integer.MAX_VALUE || value < Integer.MIN_VALUE) |
| { |
| type = Long.class; |
| } |
| else |
| { |
| return (int) value; |
| } |
| } |
| if (type == Long.class) |
| { |
| return value; |
| } |
| return BigInteger.valueOf(value); |
| } |
| |
| /** |
| * Wrap the result in the object of the bigger type. |
| * |
| * @param value result of operation (as a long) - used to check size |
| * @param op1 first operand of binary operation |
| * @param op2 second operand of binary operation |
| * @return Number object of appropriate size to fit the value and operators |
| */ |
| private static Number wrapPrimitive (long value, Number op1, Number op2) |
| { |
| if ( typesBySize.indexOf( op1.getClass()) > typesBySize.indexOf( op2.getClass())) |
| { |
| return wrapPrimitive(value, op1.getClass()); |
| } |
| return wrapPrimitive(value, op2.getClass()); |
| } |
| |
| /** |
| * Find the common Number-type to be used in calculations. |
| * |
| * @param op1 first operand of binary operation |
| * @param op2 second operand of binary operation |
| * @return constant indicating type of Number to use in calculations |
| */ |
| private static int findCalculationBase (Number op1, Number op2) |
| { |
| |
| boolean op1Int = isInteger(op1); |
| boolean op2Int = isInteger(op2); |
| |
| if ( (op1 instanceof BigDecimal || op2 instanceof BigDecimal) || |
| ( (!op1Int || !op2Int) && (op1 instanceof BigInteger || op2 instanceof BigInteger)) ) |
| { |
| return BASE_BIGDECIMAL; |
| } |
| |
| if (op1Int && op2Int) { |
| if (op1 instanceof BigInteger || op2 instanceof BigInteger) |
| { |
| return BASE_BIGINTEGER; |
| } |
| return BASE_LONG; |
| } |
| |
| if ((op1 instanceof Double) || (op2 instanceof Double)) |
| { |
| return BASE_DOUBLE; |
| } |
| return BASE_FLOAT; |
| } |
| |
| /** |
| * Find the Number-type to be used for a single number |
| * |
| * @param op operand |
| * @return constant indicating type of Number to use in calculations |
| */ |
| public static int findCalculationBase(Number op) |
| { |
| if (isInteger(op)) |
| { |
| if (op instanceof BigInteger) |
| { |
| return BASE_BIGINTEGER; |
| } |
| return BASE_LONG; |
| } else if (op instanceof BigDecimal) |
| { |
| return BASE_BIGDECIMAL; |
| } else if (op instanceof Double) |
| { |
| return BASE_DOUBLE; |
| } |
| return BASE_FLOAT; |
| } |
| |
| /** |
| * Add two numbers and return the correct value / type. |
| * Overflow detection is done for integer values (byte, short, int, long) only! |
| * @param op1 |
| * @param op2 |
| * @return Addition result. |
| */ |
| public static Number add (Number op1, Number op2) |
| { |
| |
| int calcBase = findCalculationBase( op1, op2); |
| switch (calcBase) |
| { |
| case BASE_BIGINTEGER: |
| return toBigInteger( op1 ).add( toBigInteger( op2 )); |
| case BASE_LONG: |
| long l1 = op1.longValue(); |
| long l2 = op2.longValue(); |
| long result = l1+l2; |
| |
| // Overflow check |
| if ((result ^ l1) < 0 && (result ^ l2) < 0) |
| { |
| return toBigInteger( op1).add( toBigInteger( op2)); |
| } |
| return wrapPrimitive( result, op1, op2); |
| case BASE_FLOAT: |
| return op1.floatValue() + op2.floatValue(); |
| case BASE_DOUBLE: |
| return op1.doubleValue() + op2.doubleValue(); |
| |
| // Default is BigDecimal operation |
| default: |
| return toBigDecimal( op1 ).add( toBigDecimal( op2 )); |
| } |
| } |
| |
| /** |
| * Subtract two numbers and return the correct value / type. |
| * Overflow detection is done for integer values (byte, short, int, long) only! |
| * @param op1 |
| * @param op2 |
| * @return Subtraction result. |
| */ |
| public static Number subtract (Number op1, Number op2) { |
| |
| int calcBase = findCalculationBase( op1, op2); |
| switch (calcBase) { |
| case BASE_BIGINTEGER: |
| return toBigInteger( op1 ).subtract( toBigInteger( op2 )); |
| case BASE_LONG: |
| long l1 = op1.longValue(); |
| long l2 = op2.longValue(); |
| long result = l1-l2; |
| |
| // Overflow check |
| if ((result ^ l1) < 0 && (result ^ ~l2) < 0) { |
| return toBigInteger( op1).subtract( toBigInteger( op2)); |
| } |
| return wrapPrimitive( result, op1, op2); |
| case BASE_FLOAT: |
| return op1.floatValue() - op2.floatValue(); |
| case BASE_DOUBLE: |
| return op1.doubleValue() - op2.doubleValue(); |
| |
| // Default is BigDecimal operation |
| default: |
| return toBigDecimal( op1 ).subtract( toBigDecimal( op2 )); |
| } |
| } |
| |
| /** |
| * Multiply two numbers and return the correct value / type. |
| * Overflow detection is done for integer values (byte, short, int, long) only! |
| * @param op1 |
| * @param op2 |
| * @return Multiplication result. |
| */ |
| public static Number multiply (Number op1, Number op2) { |
| |
| int calcBase = findCalculationBase( op1, op2); |
| switch (calcBase) { |
| case BASE_BIGINTEGER: |
| return toBigInteger( op1 ).multiply( toBigInteger( op2 )); |
| case BASE_LONG: |
| long l1 = op1.longValue(); |
| long l2 = op2.longValue(); |
| long result = l1*l2; |
| |
| // Overflow detection |
| if ((l2 != 0) && (result / l2 != l1)) { |
| return toBigInteger( op1).multiply( toBigInteger( op2)); |
| } |
| return wrapPrimitive( result, op1, op2); |
| case BASE_FLOAT: |
| return op1.floatValue() * op2.floatValue(); |
| case BASE_DOUBLE: |
| return op1.doubleValue() * op2.doubleValue(); |
| |
| // Default is BigDecimal operation |
| default: |
| return toBigDecimal( op1 ).multiply( toBigDecimal( op2 )); |
| } |
| } |
| |
| /** |
| * Divide two numbers. The result will be returned as Integer-type if and only if |
| * both sides of the division operator are Integer-types. Otherwise a Float, Double, |
| * or BigDecimal will be returned. |
| * @param op1 |
| * @param op2 |
| * @return Division result. |
| */ |
| public static Number divide (Number op1, Number op2) { |
| |
| int calcBase = findCalculationBase( op1, op2); |
| switch (calcBase) { |
| case BASE_BIGINTEGER: |
| BigInteger b1 = toBigInteger( op1 ); |
| BigInteger b2 = toBigInteger( op2 ); |
| return b1.divide( b2); |
| |
| case BASE_LONG: |
| long l1 = op1.longValue(); |
| long l2 = op2.longValue(); |
| return wrapPrimitive( l1 / l2, op1, op2); |
| |
| case BASE_FLOAT: |
| return op1.floatValue() / op2.floatValue(); |
| case BASE_DOUBLE: |
| return op1.doubleValue() / op2.doubleValue(); |
| |
| // Default is BigDecimal operation |
| default: |
| return toBigDecimal( op1 ).divide( toBigDecimal( op2 ), BigDecimal.ROUND_HALF_DOWN); |
| } |
| } |
| |
| /** |
| * Modulo two numbers. |
| * @param op1 |
| * @param op2 |
| * @return Modulo result. |
| * |
| * @throws ArithmeticException If at least one parameter is a BigDecimal |
| */ |
| public static Number modulo (Number op1, Number op2) throws ArithmeticException { |
| |
| int calcBase = findCalculationBase( op1, op2); |
| switch (calcBase) { |
| case BASE_BIGINTEGER: |
| return toBigInteger( op1 ).mod( toBigInteger( op2 )); |
| case BASE_LONG: |
| return wrapPrimitive( op1.longValue() % op2.longValue(), op1, op2); |
| case BASE_FLOAT: |
| return op1.floatValue() % op2.floatValue(); |
| case BASE_DOUBLE: |
| return op1.doubleValue() % op2.doubleValue(); |
| |
| // Default is BigDecimal operation |
| default: |
| throw new ArithmeticException( "Cannot calculate the modulo of BigDecimals."); |
| } |
| } |
| |
| /** |
| * Compare two numbers. |
| * @param op1 |
| * @param op2 |
| * @return 1 if n1 > n2, -1 if n1 < n2 and 0 if equal. |
| */ |
| public static int compare (Number op1, Number op2) { |
| |
| int calcBase = findCalculationBase( op1, op2); |
| switch (calcBase) { |
| case BASE_BIGINTEGER: |
| return toBigInteger( op1 ).compareTo( toBigInteger( op2 )); |
| case BASE_LONG: |
| long l1 = op1.longValue(); |
| long l2 = op2.longValue(); |
| if (l1 < l2) { |
| return -1; |
| } |
| if (l1 > l2) { |
| return 1; |
| } |
| return 0; |
| case BASE_FLOAT: |
| float f1 = op1.floatValue(); |
| float f2 = op2.floatValue(); |
| if (f1 < f2) { |
| return -1; |
| } |
| if (f1 > f2) { |
| return 1; |
| } |
| return 0; |
| case BASE_DOUBLE: |
| double d1 = op1.doubleValue(); |
| double d2 = op2.doubleValue(); |
| if (d1 < d2) { |
| return -1; |
| } |
| if (d1 > d2) { |
| return 1; |
| } |
| return 0; |
| |
| // Default is BigDecimal operation |
| default: |
| return toBigDecimal( op1 ).compareTo( toBigDecimal ( op2 )); |
| } |
| } |
| |
| /** |
| * Negate a number |
| * @param op n |
| * @return -n (unary negation of n) |
| */ |
| public static Number negate(Number op) |
| { |
| int calcBase = findCalculationBase( op); |
| switch (calcBase) { |
| case BASE_BIGINTEGER: |
| return toBigInteger(op).negate(); |
| case BASE_LONG: |
| long l = op.longValue(); |
| /* overflow check */ |
| if (l == Long.MIN_VALUE) |
| { |
| return toBigInteger(l).negate(); |
| } |
| return wrapPrimitive(-l, op.getClass()); |
| case BASE_FLOAT: |
| float f = op.floatValue(); |
| return -f; |
| case BASE_DOUBLE: |
| double d = op.doubleValue(); |
| return -d; |
| // Default is BigDecimal operation |
| default: |
| return toBigDecimal(op).negate(); |
| } |
| } |
| } |