You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by he...@apache.org on 2011/05/21 16:50:56 UTC
svn commit: r1125719 - in
/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2:
JexlArithmetic.java JexlThreadedArithmetic.java
Author: henrib
Date: Sat May 21 14:50:56 2011
New Revision: 1125719
URL: http://svn.apache.org/viewvc?rev=1125719&view=rev
Log:
Revisited BigDecimal handling by adding a scale used for coercion / comparisons
Modified:
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/JexlArithmetic.java
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/JexlThreadedArithmetic.java
Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/JexlArithmetic.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/JexlArithmetic.java?rev=1125719&r1=1125718&r2=1125719&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/JexlArithmetic.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/JexlArithmetic.java Sat May 21 14:50:56 2011
@@ -51,17 +51,21 @@ public class JexlArithmetic {
protected static final BigInteger BIGI_LONG_MAX_VALUE = BigInteger.valueOf(Long.MAX_VALUE);
/** Long.MIN_VALUE as BigInteger. */
protected static final BigInteger BIGI_LONG_MIN_VALUE = BigInteger.valueOf(Long.MIN_VALUE);
+ /** Default BigDecimal scale. */
+ protected static final int BIGD_SCALE = 5;
/** Whether this JexlArithmetic instance behaves in strict or lenient mode. */
protected final boolean strict;
/** The big decimal math context. */
protected final MathContext mathContext;
+ /** The big decimal scale. */
+ protected final int mathScale;
/**
* Creates a JexlArithmetic.
* @param lenient whether this arithmetic is lenient or strict
*/
public JexlArithmetic(boolean lenient) {
- this(lenient, MathContext.DECIMAL128);
+ this(lenient, MathContext.DECIMAL128, BIGD_SCALE);
}
/**
@@ -69,9 +73,10 @@ public class JexlArithmetic {
* @param lenient whether this arithmetic is lenient or strict
* @param bigdContext the math context instance to use for +,-,/,*,% operations on big decimals.
*/
- public JexlArithmetic(boolean lenient, MathContext bigdContext) {
+ public JexlArithmetic(boolean lenient, MathContext bigdContext, int bigdScale) {
this.strict = !lenient;
this.mathContext = bigdContext;
+ this.mathScale = bigdScale;
}
/**
@@ -90,6 +95,23 @@ public class JexlArithmetic {
public MathContext getMathContext() {
return mathContext;
}
+
+ /**
+ * The BigDecimal scale used for comparison and coericion operations.
+ * @return the scale
+ */
+ public int getMathScale() {
+ return mathScale;
+ }
+
+ /**
+ * Ensure a big decimal is rounded by this arithmetic scale and rounding mode.
+ * @param number the big decimal to round
+ * @return the rounded big decimal
+ */
+ public BigDecimal roundBigDecimal(final BigDecimal number) {
+ return number.setScale(mathScale, mathContext.getRoundingMode());
+ }
/**
* The result of +,/,-,*,% when both operands are null.
@@ -159,10 +181,10 @@ public class JexlArithmetic {
*/
protected boolean isNumberable(final Object o) {
return o instanceof Integer
- || o instanceof Long
- || o instanceof Byte
- || o instanceof Short
- || o instanceof Character;
+ || o instanceof Long
+ || o instanceof Byte
+ || o instanceof Short
+ || o instanceof Character;
}
/**
@@ -181,14 +203,14 @@ public class JexlArithmetic {
protected Number narrowBigInteger(Object lhs, Object rhs, BigInteger bigi) {
//coerce to long if possible
if (!(lhs instanceof BigInteger || rhs instanceof BigInteger)
- && bigi.compareTo(BIGI_LONG_MAX_VALUE) <= 0
- && bigi.compareTo(BIGI_LONG_MIN_VALUE) >= 0) {
+ && bigi.compareTo(BIGI_LONG_MAX_VALUE) <= 0
+ && bigi.compareTo(BIGI_LONG_MIN_VALUE) >= 0) {
// coerce to int if possible
long l = bigi.longValue();
// coerce to int when possible (int being so often used in method parms)
if (!(lhs instanceof Long || rhs instanceof Long)
- && l <= Integer.MAX_VALUE
- && l >= Integer.MIN_VALUE) {
+ && l <= Integer.MAX_VALUE
+ && l >= Integer.MIN_VALUE) {
return Integer.valueOf((int) l);
}
return Long.valueOf(l);
@@ -197,6 +219,32 @@ public class JexlArithmetic {
}
/**
+ * Given a BigDecimal, attempt to narrow it to an Integer or Long if it fits if
+ * one of the arguments is a numberable.
+ * </p>
+ * @param lhs the left hand side operand that lead to the bigd result
+ * @param rhs the right hand side operand that lead to the bigd result
+ * @param bigi the BigInteger to narrow
+ * @return an Integer or Long if narrowing is possible, the original BigInteger otherwise
+ */
+ protected Number narrowBigDecimal(Object lhs, Object rhs, BigDecimal bigd) {
+ if (isNumberable(lhs) || isNumberable(rhs)) {
+ try {
+ long l = bigd.longValueExact();
+ // coerce to int when possible (int being so often used in method parms)
+ if (l <= Integer.MAX_VALUE && l >= Integer.MIN_VALUE) {
+ return Integer.valueOf((int) l);
+ } else {
+ return Long.valueOf(l);
+ }
+ } catch(ArithmeticException xa) {
+ // ignore, no exact value possible
+ }
+ }
+ return bigd;
+ }
+
+ /**
* Given an array of objects, attempt to type it more strictly.
* <ul>
* <li>If all objects are of the same type, the array returned will be an array of that same type</li>
@@ -232,7 +280,7 @@ public class JexlArithmetic {
commonClass = Object.class;
break;
}
- } while(!commonClass.isAssignableFrom(eclass));
+ } while (!commonClass.isAssignableFrom(eclass));
}
}
} else {
@@ -252,7 +300,7 @@ public class JexlArithmetic {
}
// allocate and fill up the typed array
Object typed = Array.newInstance(commonClass, size);
- for(int i = 0; i < size; ++i) {
+ for (int i = 0; i < size; ++i) {
Array.set(typed, i, untyped[i]);
}
return typed;
@@ -296,7 +344,7 @@ public class JexlArithmetic {
if (left == null && right == null) {
return controlNullNullOperands();
}
-
+
try {
// if either are floating point (double or float) use double
if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
@@ -304,21 +352,15 @@ public class JexlArithmetic {
double r = toDouble(right);
return new Double(l + r);
}
-
- // if both are bigintegers use that type
- if (left instanceof BigInteger && right instanceof BigInteger) {
- BigInteger l = toBigInteger(left);
- BigInteger r = toBigInteger(right);
- return l.add(r);
- }
-
+
// if either are bigdecimal use that type
if (left instanceof BigDecimal || right instanceof BigDecimal) {
BigDecimal l = toBigDecimal(left);
BigDecimal r = toBigDecimal(right);
- return l.add(r, mathContext);
+ BigDecimal result = l.add(r, mathContext);
+ return narrowBigDecimal(left, right, result);
}
-
+
// otherwise treat as integers
BigInteger l = toBigInteger(left);
BigInteger r = toBigInteger(right);
@@ -352,19 +394,12 @@ public class JexlArithmetic {
return new Double(l / r);
}
- // if both are bigintegers use that type
- if (left instanceof BigInteger && right instanceof BigInteger) {
- BigInteger l = toBigInteger(left);
- BigInteger r = toBigInteger(right);
- return l.divide(r);
- }
-
// if either are bigdecimal use that type
if (left instanceof BigDecimal || right instanceof BigDecimal) {
BigDecimal l = toBigDecimal(left);
BigDecimal r = toBigDecimal(right);
- BigDecimal d = l.divide(r, mathContext);
- return d;
+ BigDecimal result = l.divide(r, mathContext);
+ return narrowBigDecimal(left, right, result);
}
// otherwise treat as integers
@@ -373,7 +408,7 @@ public class JexlArithmetic {
BigInteger result = l.divide(r);
return narrowBigInteger(left, right, result);
}
-
+
/**
* left value mod right.
* @param left first value
@@ -396,19 +431,12 @@ public class JexlArithmetic {
return new Double(l % r);
}
- // if both are bigintegers use that type
- if (left instanceof BigInteger && right instanceof BigInteger) {
- BigInteger l = toBigInteger(left);
- BigInteger r = toBigInteger(right);
- return l.mod(r);
- }
-
// if either are bigdecimal use that type
if (left instanceof BigDecimal || right instanceof BigDecimal) {
BigDecimal l = toBigDecimal(left);
BigDecimal r = toBigDecimal(right);
BigDecimal remainder = l.remainder(r, mathContext);
- return remainder;
+ return narrowBigDecimal(left, right, remainder);
}
// otherwise treat as integers
@@ -417,7 +445,7 @@ public class JexlArithmetic {
BigInteger result = l.mod(r);
return narrowBigInteger(left, right, result);
}
-
+
/**
* Multiply the left value by the right.
* @param left first value
@@ -435,19 +463,13 @@ public class JexlArithmetic {
double r = toDouble(right);
return new Double(l * r);
}
-
- // if both are bigintegers use that type
- if (left instanceof BigInteger && right instanceof BigInteger) {
- BigInteger l = toBigInteger(left);
- BigInteger r = toBigInteger(right);
- return l.multiply(r);
- }
-
+
// if either are bigdecimal use that type
if (left instanceof BigDecimal || right instanceof BigDecimal) {
BigDecimal l = toBigDecimal(left);
BigDecimal r = toBigDecimal(right);
- return l.multiply(r, mathContext);
+ BigDecimal result = l.multiply(r, mathContext);
+ return narrowBigDecimal(left, right, result);
}
// otherwise treat as integers
@@ -456,7 +478,7 @@ public class JexlArithmetic {
BigInteger result = l.multiply(r);
return narrowBigInteger(left, right, result);
}
-
+
/**
* Subtract the right value from the left.
* @param left first value
@@ -474,19 +496,13 @@ public class JexlArithmetic {
double r = toDouble(right);
return new Double(l - r);
}
-
- // if both are bigintegers use that type
- if (left instanceof BigInteger && right instanceof BigInteger) {
- BigInteger l = toBigInteger(left);
- BigInteger r = toBigInteger(right);
- return l.subtract(r);
- }
-
+
// if either are bigdecimal use that type
if (left instanceof BigDecimal || right instanceof BigDecimal) {
BigDecimal l = toBigDecimal(left);
BigDecimal r = toBigDecimal(right);
- return l.subtract(r, mathContext);
+ BigDecimal result = l.subtract(r, mathContext);
+ return narrowBigDecimal(left, right, result);
}
// otherwise treat as integers
@@ -538,14 +554,14 @@ public class JexlArithmetic {
* we know both aren't null, therefore L != R
*/
return false;
- } else if (left.getClass().equals(right.getClass())) {
- return left.equals(right);
} else if (left instanceof BigDecimal || right instanceof BigDecimal) {
return toBigDecimal(left).compareTo(toBigDecimal(right)) == 0;
+ } else if (left.getClass().equals(right.getClass())) {
+ return left.equals(right);
} else if (isFloatingPointType(left, right)) {
return toDouble(left) == toDouble(right);
} else if (left instanceof Number || right instanceof Number || left instanceof Character
- || right instanceof Character) {
+ || right instanceof Character) {
return toLong(left) == toLong(right);
} else if (left instanceof Boolean || right instanceof Boolean) {
return toBoolean(left) == toBoolean(right);
@@ -556,7 +572,6 @@ public class JexlArithmetic {
return left.equals(right);
}
-
/**
* Test if left < right.
*
@@ -572,8 +587,8 @@ public class JexlArithmetic {
double rightDouble = toDouble(right);
return leftDouble < rightDouble;
} else if (left instanceof BigDecimal || right instanceof BigDecimal) {
- BigDecimal l = toBigDecimal(left);
- BigDecimal r = toBigDecimal(right);
+ BigDecimal l = toBigDecimal(left);
+ BigDecimal r = toBigDecimal(right);
return l.compareTo(r) < 0;
} else if (isNumberable(left) || isNumberable(right)) {
long leftLong = toLong(left);
@@ -594,7 +609,7 @@ public class JexlArithmetic {
}
throw new IllegalArgumentException("Invalid comparison : comparing cardinality for left: " + left
- + " and right: " + right);
+ + " and right: " + right);
}
@@ -633,7 +648,7 @@ public class JexlArithmetic {
public boolean greaterThanOrEqual(Object left, Object right) {
return equals(left, right) || greaterThan(left, right);
}
-
+
/**
* Coerce to a boolean (not a java.lang.Boolean).
*
@@ -680,7 +695,6 @@ public class JexlArithmetic {
+ val.getClass().getName());
}
-
/**
* Coerce to a long (not a java.lang.Long).
*
@@ -732,11 +746,11 @@ public class JexlArithmetic {
int i = ((Character) val).charValue();
return BigInteger.valueOf(i);
}
-
+
throw new IllegalArgumentException("BigInteger coercion exception. Can't coerce type: "
+ val.getClass().getName());
}
-
+
/**
* Get a BigDecimal from the object passed.
* Null and empty string maps to zero.
@@ -746,7 +760,7 @@ public class JexlArithmetic {
*/
public BigDecimal toBigDecimal(Object val) {
if (val instanceof BigDecimal) {
- return (BigDecimal) val;
+ return roundBigDecimal((BigDecimal) val);
} else if (val == null) {
controlNullOperand();
return BigDecimal.ZERO;
@@ -755,18 +769,18 @@ public class JexlArithmetic {
if ("".equals(string)) {
return BigDecimal.valueOf(0);
}
- return new BigDecimal(string, mathContext);
+ return roundBigDecimal(new BigDecimal(string, mathContext));
} else if (val instanceof Number) {
- return new BigDecimal(val.toString(), mathContext);
+ return roundBigDecimal(new BigDecimal(val.toString(), mathContext));
} else if (val instanceof Character) {
int i = ((Character) val).charValue();
return new BigDecimal(i);
}
-
+
throw new IllegalArgumentException("BigDecimal coercion exception. Can't coerce type: "
+ val.getClass().getName());
}
-
+
/**
* Coerce to a double.
*
@@ -803,7 +817,6 @@ public class JexlArithmetic {
+ val.getClass().getName());
}
-
/**
* Coerce to a string.
*
@@ -852,7 +865,7 @@ public class JexlArithmetic {
BigInteger bigi = (BigInteger) original;
// if it's bigger than a Long it can't be narrowed
if (bigi.compareTo(BIGI_LONG_MAX_VALUE) > 0
- || bigi.compareTo(BIGI_LONG_MIN_VALUE) < 0) {
+ || bigi.compareTo(BIGI_LONG_MIN_VALUE) < 0) {
return original;
}
}
@@ -869,5 +882,4 @@ public class JexlArithmetic {
}
return result;
}
-
}
\ No newline at end of file
Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/JexlThreadedArithmetic.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/JexlThreadedArithmetic.java?rev=1125719&r1=1125718&r2=1125719&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/JexlThreadedArithmetic.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/JexlThreadedArithmetic.java Sat May 21 14:50:56 2011
@@ -16,13 +16,25 @@
*/
package org.apache.commons.jexl2;
+import java.math.MathContext;
+
/**
* A derived arithmetic that allows different threads to operate with
- * different strict/lenient modes using the same JexlEngine.
+ * different strict/lenient/math modes using the same JexlEngine.
*/
public class JexlThreadedArithmetic extends JexlArithmetic {
- /** Whether this JexlArithmetic instance behaves in strict or lenient mode for this thread. */
- protected static final ThreadLocal<Boolean> lenient = new ThreadLocal<Boolean>();
+
+ /** Holds the threaded version of some arithmetic features. */
+ static class Features {
+ Features() {
+ }
+ /** Whether this JexlArithmetic instance behaves in strict or lenient mode. */
+ Boolean lenient = null;
+ /** The big decimal math context. */
+ MathContext mathContext = null;
+ /** The big decimal scale. */
+ Integer mathScale = null;
+ }
/**
* Standard ctor.
@@ -31,6 +43,24 @@ public class JexlThreadedArithmetic exte
public JexlThreadedArithmetic(boolean lenient) {
super(lenient);
}
+
+ /**
+ * Creates a JexlThreadedArithmetic.
+ * @param lenient whether this arithmetic is lenient or strict
+ * @param bigdContext the math context instance to use for +,-,/,*,% operations on big decimals.
+ */
+ public JexlThreadedArithmetic(boolean lenient, MathContext bigdContext, int bigdScale) {
+ super(lenient, bigdContext, bigdScale);
+ }
+
+ /** Whether this JexlArithmetic instance behaves in strict or lenient mode for this thread. */
+ static final ThreadLocal<Features> features = new ThreadLocal<Features>() {
+ @Override
+ protected synchronized Features initialValue() {
+ return new Features();
+ }
+ };
+
/**
* Overrides the default behavior and sets whether this JexlArithmetic instance triggers errors
@@ -43,13 +73,43 @@ public class JexlThreadedArithmetic exte
* @param flag true means no JexlException will occur, false allows them, null reverts to default behavior
*/
public static void setLenient(Boolean flag) {
- lenient.set(flag);
+ features.get().lenient = flag == null? null : flag;
+ }
+
+ /**
+ * Sets the math scale.
+ * <p>The goal and constraints are the same than for setLenient.</p>
+ * @param scale the scale
+ */
+ public static void setMathScale(Integer scale) {
+ features.get().mathScale = scale;
+ }
+
+ /**
+ * Sets the math context.
+ * <p>The goal and constraints are the same than for setLenient.</p>
+ * @param mc the math context
+ */
+ public static void setMathContext(MathContext mc) {
+ features.get().mathContext = mc;
}
/** {@inheritDoc} */
@Override
public boolean isLenient() {
- Boolean tl = lenient.get();
- return tl == null? super.isLenient() : tl.booleanValue();
+ Boolean lenient = features.get().lenient;
+ return lenient == null ? super.isLenient() : lenient.booleanValue();
+ }
+
+ @Override
+ public int getMathScale() {
+ Integer scale = features.get().mathScale;
+ return scale == null ? super.getMathScale() : scale.intValue();
+ }
+
+ @Override
+ public MathContext getMathContext() {
+ MathContext mc = features.get().mathContext;
+ return mc == null? super.getMathContext() : mc;
}
}