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 2012/05/11 18:36:21 UTC
svn commit: r1337271 - in /commons/proper/jexl/branches/2.0: ./
src/main/java/org/apache/commons/jexl2/ src/site/xdoc/
src/test/java/org/apache/commons/jexl2/
Author: henrib
Date: Fri May 11 16:36:20 2012
New Revision: 1337271
URL: http://svn.apache.org/viewvc?rev=1337271&view=rev
Log:
Backported JexlArithmetic from JEXL3: essentially, consider BigDecimal before Double, logical code regrouping and clearer javadoc;
Added test/example based on JEXL-132;
Modified pom.xml to use toolchains (to help ensure this is compiled against Java 1.5); not sure how to enforce this otherwise;
Updated release notes and changes.xml
Modified:
commons/proper/jexl/branches/2.0/RELEASE-NOTES.txt
commons/proper/jexl/branches/2.0/pom.xml
commons/proper/jexl/branches/2.0/src/main/java/org/apache/commons/jexl2/Interpreter.java
commons/proper/jexl/branches/2.0/src/main/java/org/apache/commons/jexl2/JexlArithmetic.java
commons/proper/jexl/branches/2.0/src/main/java/org/apache/commons/jexl2/JexlException.java
commons/proper/jexl/branches/2.0/src/site/xdoc/changes.xml
commons/proper/jexl/branches/2.0/src/test/java/org/apache/commons/jexl2/IssuesTest.java
Modified: commons/proper/jexl/branches/2.0/RELEASE-NOTES.txt
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/RELEASE-NOTES.txt?rev=1337271&r1=1337270&r2=1337271&view=diff
==============================================================================
--- commons/proper/jexl/branches/2.0/RELEASE-NOTES.txt (original)
+++ commons/proper/jexl/branches/2.0/RELEASE-NOTES.txt Fri May 11 16:36:20 2012
@@ -17,7 +17,7 @@
$Id$
Commons JEXL Package
- Version 2.1.1
+ Version 2.1.2
Release Notes
@@ -35,13 +35,36 @@ Its goal is to expose scripting features
http://commons.apache.org/jexl/
-What's new in 2.1.1
-===================
-Version 2.1.1 is a bugfix release to address a regression in 2.1:
-* JEXL-124: Array parameters to methods don't work anymore
+========================================================================================================================
+Release 2.1.2:
+========================================================================================================================
-There are no other changes
+Version 2.1.2 is a micro release to fix bugs reported since 2.1.1:
+
+* JEXL-131: UnifiedJexl parsing may fail with NPE
+* JEXL-130: Ternary Conditional fails for Object values
+* JEXL-126: Decimal numbers literals should be 'double' by default (instead of 'float')
+
+There are no other changes.
+
+
+========================================================================================================================
+Release 2.1.1
+========================================================================================================================
+
+Version 2.1.2 is a micro release to fix a regression detected in 2.1:
+
+* JEXL-124: Array parameters to methods don't work anymore (Jexl 2.1.1)
+
+There are no other changes.
+
+
+========================================================================================================================
+Release 2.1
+========================================================================================================================
+
+Version 2.1 is a minor release adding new features and fixing known issues detected in 2.0.
Compatibility with previous releases
====================================
@@ -76,6 +99,7 @@ New features in 2.1:
Bugs Fixed in 2.1:
==================
+* JEXL-124: Array parameters to methods don't work anymore (Jexl 2.1.1)
* JEXL-83: Make JexlArithmetic immutable (and threadsafe)
* JEXL-24: Support Long for integer literal instead of Integers
* JEXL-107: literals and parenthesized expressions can not be used as references
@@ -83,7 +107,7 @@ Bugs Fixed in 2.1:
* JEXL-101: Vararg methods where the first argument is no vararg can not be called with only the fixed parameters given
* JEXL-105: Array literals are considered constant even when they are not
* JEXL-104: NPE in JexlArithmetic when an Array-Expression containing a null is used.
-* JEXL-112: Cannot parse Integer.MIN_VALUE
+* JEXL-112: Cannot parse Integer.MIN_VALUE
* JEXL-111: expression execute error
Bugs fixed in 2.0.1:
@@ -95,9 +119,9 @@ Bugs fixed in 2.0.1:
-Previous Releases:
-==================
-
+========================================================================================================================
+Release 2.0:
+========================================================================================================================
Bugs fixed in 2.0:
==================
@@ -169,7 +193,7 @@ Other Changes:
o Add @since 2.0 tags to code so we can track API additions via Javadoc
-Upgrading from JEXL 1.x
+Upgrading from JEXL 1.x
=======================
JEXL now requires Java 1.5 or later.
Modified: commons/proper/jexl/branches/2.0/pom.xml
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/pom.xml?rev=1337271&r1=1337270&r2=1337271&view=diff
==============================================================================
--- commons/proper/jexl/branches/2.0/pom.xml (original)
+++ commons/proper/jexl/branches/2.0/pom.xml Fri May 11 16:36:20 2012
@@ -136,6 +136,19 @@
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-toolchains-plugin</artifactId>
+ <version>1.0</version>
+ <configuration>
+ <toolchains>
+ <jdk>
+ <version>1.5</version>
+ <vendor>sun</vendor>
+ </jdk>
+ </toolchains>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
@@ -177,15 +190,15 @@
</executions>
</plugin>
<plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-jar-plugin</artifactId>
- <executions>
- <execution>
- <goals>
- <goal>test-jar</goal>
- </goals>
- </execution>
- </executions>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <executions>
+ <execution>
+ <goals>
+ <goal>test-jar</goal>
+ </goals>
+ </execution>
+ </executions>
</plugin>
</plugins>
</build>
@@ -275,8 +288,8 @@
<!-- version is defined in commons-parent -->
<configuration>
<excludes>
- <exclude>org/apache/commons/jexl2/parser/**</exclude>
- <exclude>org/apache/commons/jexl2/internal/**</exclude>
+ <exclude>org/apache/commons/jexl2/parser/**</exclude>
+ <exclude>org/apache/commons/jexl2/internal/**</exclude>
</excludes>
</configuration>
</plugin>
Modified: commons/proper/jexl/branches/2.0/src/main/java/org/apache/commons/jexl2/Interpreter.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/main/java/org/apache/commons/jexl2/Interpreter.java?rev=1337271&r1=1337270&r2=1337271&view=diff
==============================================================================
--- commons/proper/jexl/branches/2.0/src/main/java/org/apache/commons/jexl2/Interpreter.java (original)
+++ commons/proper/jexl/branches/2.0/src/main/java/org/apache/commons/jexl2/Interpreter.java Fri May 11 16:36:20 2012
@@ -880,7 +880,10 @@ public class Interpreter implements Pars
}
/**
- * @deprecated Do not use
+ * @deprecated. Do not use.
+ * @param node that can no longer be produced by the grammar
+ * @param data that will not be used
+ * @return throws UnsupportedOperationException
*/
@Deprecated
public Object visit(ASTFloatLiteral node, Object data) {
@@ -888,7 +891,10 @@ public class Interpreter implements Pars
}
/**
- * @deprecated Do not use
+ * @deprecated. Do not use.
+ * @param node that can no longer be produced by the grammar
+ * @param data that will not be used
+ * @return throws UnsupportedOperationException
*/
@Deprecated
public Object visit(ASTIntegerLiteral node, Object data) {
Modified: commons/proper/jexl/branches/2.0/src/main/java/org/apache/commons/jexl2/JexlArithmetic.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/main/java/org/apache/commons/jexl2/JexlArithmetic.java?rev=1337271&r1=1337270&r2=1337271&view=diff
==============================================================================
--- commons/proper/jexl/branches/2.0/src/main/java/org/apache/commons/jexl2/JexlArithmetic.java (original)
+++ commons/proper/jexl/branches/2.0/src/main/java/org/apache/commons/jexl2/JexlArithmetic.java Fri May 11 16:36:20 2012
@@ -228,6 +228,123 @@ public class JexlArithmetic {
}
/**
+ * Given a Number, return back the value using the smallest type the result
+ * will fit into. This works hand in hand with parameter 'widening' in java
+ * method calls, e.g. a call to substring(int,int) with an int and a long
+ * will fail, but a call to substring(int,int) with an int and a short will
+ * succeed.
+ *
+ * @param original the original number.
+ * @return a value of the smallest type the original number will fit into.
+ */
+ public Number narrow(Number original) {
+ return narrowNumber(original, null);
+ }
+
+ /**
+ * Replace all numbers in an arguments array with the smallest type that will fit.
+ * @param args the argument array
+ * @return true if some arguments were narrowed and args array is modified,
+ * false if no narrowing occured and args array has not been modified
+ */
+ protected boolean narrowArguments(Object[] args) {
+ boolean narrowed = false;
+ for (int a = 0; a < args.length; ++a) {
+ Object arg = args[a];
+ if (arg instanceof Number) {
+ Object narg = narrow((Number) arg);
+ if (narg != arg) {
+ narrowed = true;
+ }
+ args[a] = narg;
+ }
+ }
+ return narrowed;
+ }
+
+
+ /**
+ * Whether we consider the narrow class as a potential candidate for narrowing the source.
+ * @param narrow the target narrow class
+ * @param source the orginal source class
+ * @return true if attempt to narrow source to target is accepted
+ * @since 2.1
+ */
+ protected boolean narrowAccept(Class<?> narrow, Class<?> source) {
+ return narrow == null || narrow.equals(source);
+ }
+
+ /**
+ * Given a Number, return back the value attempting to narrow it to a target class.
+ * @param original the original number
+ * @param narrow the attempted target class
+ * @return the narrowed number or the source if no narrowing was possible
+ * @since 2.1
+ */
+ protected Number narrowNumber(Number original, Class<?> narrow) {
+ if (original == null) {
+ return original;
+ }
+ Number result = original;
+ if (original instanceof BigDecimal) {
+ BigDecimal bigd = (BigDecimal) original;
+ // if it's bigger than a double it can't be narrowed
+ if (bigd.compareTo(BIGD_DOUBLE_MAX_VALUE) > 0) {
+ return original;
+ } else {
+ try {
+ long l = bigd.longValueExact();
+ // coerce to int when possible (int being so often used in method parms)
+ if (narrowAccept(narrow, Integer.class)
+ && l <= Integer.MAX_VALUE
+ && l >= Integer.MIN_VALUE) {
+ return Integer.valueOf((int) l);
+ } else if (narrowAccept(narrow, Long.class)) {
+ return Long.valueOf(l);
+ }
+ } catch (ArithmeticException xa) {
+ // ignore, no exact value narrowing possible
+ }
+ }
+ }
+ if (original instanceof Double || original instanceof Float || original instanceof BigDecimal) {
+ double value = original.doubleValue();
+ if (narrowAccept(narrow, Float.class)
+ && value <= Float.MAX_VALUE
+ && value >= Float.MIN_VALUE) {
+ result = Float.valueOf(result.floatValue());
+ }
+ // else it fits in a double only
+ } else {
+ if (original instanceof BigInteger) {
+ 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) {
+ return original;
+ }
+ }
+ long value = original.longValue();
+ if (narrowAccept(narrow, Byte.class)
+ && value <= Byte.MAX_VALUE
+ && value >= Byte.MIN_VALUE) {
+ // it will fit in a byte
+ result = Byte.valueOf((byte) value);
+ } else if (narrowAccept(narrow, Short.class)
+ && value <= Short.MAX_VALUE
+ && value >= Short.MIN_VALUE) {
+ result = Short.valueOf((short) value);
+ } else if (narrowAccept(narrow, Integer.class)
+ && value <= Integer.MAX_VALUE
+ && value >= Integer.MIN_VALUE) {
+ result = Integer.valueOf((int) value);
+ }
+ // else it fits in a long
+ }
+ return result;
+ }
+
+ /**
* Given a BigInteger, narrow it to an Integer or Long if it fits and the arguments
* class allow it.
* <p>
@@ -279,7 +396,7 @@ public class JexlArithmetic {
return Long.valueOf(l);
}
} catch (ArithmeticException xa) {
- // ignore, no exact value possible
+ // ignore, no exact value narrowing possible
}
}
return bigd;
@@ -336,7 +453,7 @@ public class JexlArithmetic {
final Field type = commonClass.getField("TYPE");
commonClass = (Class<?>) type.get(null);
} catch (Exception xany) {
- // ignore
+ // ignore, no accessible field TYPE in common class, not a primitive class
}
}
// allocate and fill up the typed array
@@ -351,33 +468,12 @@ public class JexlArithmetic {
}
/**
- * Replace all numbers in an arguments array with the smallest type that will fit.
- * @param args the argument array
- * @return true if some arguments were narrowed and args array is modified,
- * false if no narrowing occured and args array has not been modified
- */
- protected boolean narrowArguments(Object[] args) {
- boolean narrowed = false;
- for (int a = 0; a < args.length; ++a) {
- Object arg = args[a];
- if (arg instanceof Number) {
- Object narg = narrow((Number) arg);
- if (narg != arg) {
- narrowed = true;
- }
- args[a] = narg;
- }
- }
- return narrowed;
- }
-
- /**
* Add two values together.
* <p>
* If any numeric add fails on coercion to the appropriate type,
* treat as Strings and do concatenation.
* </p>
- * @param left first value
+ * @param left first value
* @param right second value
* @return left + right.
*/
@@ -385,15 +481,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)) {
- double l = toDouble(left);
- double r = toDouble(right);
- return new Double(l + r);
- }
-
// if either are bigdecimal use that type
if (left instanceof BigDecimal || right instanceof BigDecimal) {
BigDecimal l = toBigDecimal(left);
@@ -401,7 +489,12 @@ public class JexlArithmetic {
BigDecimal result = l.add(r, getMathContext());
return narrowBigDecimal(left, right, result);
}
-
+ // if either are floating point (double or float) use double
+ if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
+ double l = toDouble(left);
+ double r = toDouble(right);
+ return new Double(l + r);
+ }
// otherwise treat as integers
BigInteger l = toBigInteger(left);
BigInteger r = toBigInteger(right);
@@ -409,13 +502,16 @@ public class JexlArithmetic {
return narrowBigInteger(left, right, result);
} catch (java.lang.NumberFormatException nfe) {
// Well, use strings!
+ if (left == null || right == null) {
+ controlNullOperand();
+ }
return toString(left).concat(toString(right));
}
}
/**
* Divide the left value by the right.
- * @param left first value
+ * @param left first value
* @param right second value
* @return left / right
* @throws ArithmeticException if right == 0
@@ -424,41 +520,38 @@ public class JexlArithmetic {
if (left == null && right == null) {
return controlNullNullOperands();
}
-
- // if either are floating point (double or float) use double
- if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
- double l = toDouble(left);
- double r = toDouble(right);
- if (r == 0.0) {
- throw new ArithmeticException("/");
- }
- return new Double(l / r);
- }
-
// if either are bigdecimal use that type
if (left instanceof BigDecimal || right instanceof BigDecimal) {
BigDecimal l = toBigDecimal(left);
BigDecimal r = toBigDecimal(right);
if (BigDecimal.ZERO.equals(r)) {
throw new ArithmeticException("/");
- }
+ }
BigDecimal result = l.divide(r, getMathContext());
return narrowBigDecimal(left, right, result);
}
-
+ // if either are floating point (double or float) use double
+ if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
+ double l = toDouble(left);
+ double r = toDouble(right);
+ if (r == 0.0) {
+ throw new ArithmeticException("/");
+ }
+ return new Double(l / r);
+ }
// otherwise treat as integers
BigInteger l = toBigInteger(left);
BigInteger r = toBigInteger(right);
if (BigInteger.ZERO.equals(r)) {
throw new ArithmeticException("/");
- }
+ }
BigInteger result = l.divide(r);
return narrowBigInteger(left, right, result);
}
/**
* left value mod right.
- * @param left first value
+ * @param left first value
* @param right second value
* @return left mod right
* @throws ArithmeticException if right == 0.0
@@ -467,41 +560,38 @@ public class JexlArithmetic {
if (left == null && right == null) {
return controlNullNullOperands();
}
-
- // if either are floating point (double or float) use double
- if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
- double l = toDouble(left);
- double r = toDouble(right);
- if (r == 0.0) {
- throw new ArithmeticException("%");
- }
- return new Double(l % r);
- }
-
// if either are bigdecimal use that type
if (left instanceof BigDecimal || right instanceof BigDecimal) {
BigDecimal l = toBigDecimal(left);
BigDecimal r = toBigDecimal(right);
if (BigDecimal.ZERO.equals(r)) {
throw new ArithmeticException("%");
- }
+ }
BigDecimal remainder = l.remainder(r, getMathContext());
return narrowBigDecimal(left, right, remainder);
}
-
+ // if either are floating point (double or float) use double
+ if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
+ double l = toDouble(left);
+ double r = toDouble(right);
+ if (r == 0.0) {
+ throw new ArithmeticException("%");
+ }
+ return new Double(l % r);
+ }
// otherwise treat as integers
BigInteger l = toBigInteger(left);
BigInteger r = toBigInteger(right);
BigInteger result = l.mod(r);
if (BigInteger.ZERO.equals(r)) {
throw new ArithmeticException("%");
- }
+ }
return narrowBigInteger(left, right, result);
}
/**
* Multiply the left value by the right.
- * @param left first value
+ * @param left first value
* @param right second value
* @return left * right.
*/
@@ -509,14 +599,6 @@ public class JexlArithmetic {
if (left == null && right == null) {
return controlNullNullOperands();
}
-
- // if either are floating point (double or float) use double
- if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
- double l = toDouble(left);
- double r = toDouble(right);
- return new Double(l * r);
- }
-
// if either are bigdecimal use that type
if (left instanceof BigDecimal || right instanceof BigDecimal) {
BigDecimal l = toBigDecimal(left);
@@ -524,7 +606,12 @@ public class JexlArithmetic {
BigDecimal result = l.multiply(r, getMathContext());
return narrowBigDecimal(left, right, result);
}
-
+ // if either are floating point (double or float) use double
+ if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
+ double l = toDouble(left);
+ double r = toDouble(right);
+ return new Double(l * r);
+ }
// otherwise treat as integers
BigInteger l = toBigInteger(left);
BigInteger r = toBigInteger(right);
@@ -534,7 +621,7 @@ public class JexlArithmetic {
/**
* Subtract the right value from the left.
- * @param left first value
+ * @param left first value
* @param right second value
* @return left - right.
*/
@@ -542,14 +629,6 @@ public class JexlArithmetic {
if (left == null && right == null) {
return controlNullNullOperands();
}
-
- // if either are floating point (double or float) use double
- if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
- double l = toDouble(left);
- double r = toDouble(right);
- return new Double(l - r);
- }
-
// if either are bigdecimal use that type
if (left instanceof BigDecimal || right instanceof BigDecimal) {
BigDecimal l = toBigDecimal(left);
@@ -557,7 +636,12 @@ public class JexlArithmetic {
BigDecimal result = l.subtract(r, getMathContext());
return narrowBigDecimal(left, right, result);
}
-
+ // if either are floating point (double or float) use double
+ if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
+ double l = toDouble(left);
+ double r = toDouble(right);
+ return new Double(l - r);
+ }
// otherwise treat as integers
BigInteger l = toBigInteger(left);
BigInteger r = toBigInteger(right);
@@ -569,7 +653,6 @@ public class JexlArithmetic {
* Negates a value (unary minus for numbers).
* @param val the value to negate
* @return the negated value
- * @since 2.1
*/
public Object negate(Object val) {
if (val instanceof Integer) {
@@ -605,10 +688,9 @@ public class JexlArithmetic {
/**
* Test if left regexp matches right.
*
- * @param left first value
+ * @param left first value
* @param right second value
* @return test result.
- * @since 2.1
*/
public boolean matches(Object left, Object right) {
if (left == null && right == null) {
@@ -629,10 +711,9 @@ public class JexlArithmetic {
/**
* Performs a bitwise and.
- * @param left the left operand
+ * @param left the left operand
* @param right the right operator
* @return left & right
- * @since 2.1
*/
public Object bitwiseAnd(Object left, Object right) {
long l = toLong(left);
@@ -642,10 +723,9 @@ public class JexlArithmetic {
/**
* Performs a bitwise or.
- * @param left the left operand
+ * @param left the left operand
* @param right the right operator
* @return left | right
- * @since 2.1
*/
public Object bitwiseOr(Object left, Object right) {
long l = toLong(left);
@@ -655,10 +735,9 @@ public class JexlArithmetic {
/**
* Performs a bitwise xor.
- * @param left the left operand
+ * @param left the left operand
* @param right the right operator
- * @return left right
- * @since 2.1
+ * @return left right
*/
public Object bitwiseXor(Object left, Object right) {
long l = toLong(left);
@@ -670,7 +749,6 @@ public class JexlArithmetic {
* Performs a bitwise complement.
* @param val the operand
* @return ~val
- * @since 2.1
*/
public Object bitwiseComplement(Object val) {
long l = toLong(val);
@@ -679,12 +757,11 @@ public class JexlArithmetic {
/**
* Performs a comparison.
- * @param left the left operand
- * @param right the right operator
+ * @param left the left operand
+ * @param right the right operator
* @param operator the operator
- * @return -1 if left < right; +1 if left > > right; 0 if left == right
+ * @return -1 if left < right; +1 if left > > right; 0 if left == right
* @throws ArithmeticException if either left or right is null
- * @since 2.1
*/
protected int compare(Object left, Object right, String operator) {
if (left != null && right != null) {
@@ -745,7 +822,7 @@ public class JexlArithmetic {
/**
* Test if left and right are equal.
*
- * @param left first value
+ * @param left first value
* @param right second value
* @return test result.
*/
@@ -762,9 +839,9 @@ public class JexlArithmetic {
}
/**
- * Test if left < right.
+ * Test if left < right.
*
- * @param left first value
+ * @param left first value
* @param right second value
* @return test result.
*/
@@ -778,9 +855,9 @@ public class JexlArithmetic {
}
/**
- * Test if left > right.
+ * Test if left > right.
*
- * @param left first value
+ * @param left first value
* @param right second value
* @return test result.
*/
@@ -793,9 +870,9 @@ public class JexlArithmetic {
}
/**
- * Test if left <= right.
+ * Test if left <= right.
*
- * @param left first value
+ * @param left first value
* @param right second value
* @return test result.
*/
@@ -810,9 +887,9 @@ public class JexlArithmetic {
}
/**
- * Test if left >= right.
+ * Test if left >= right.
*
- * @param left first value
+ * @param left first value
* @param right second value
* @return test result.
*/
@@ -827,10 +904,11 @@ public class JexlArithmetic {
}
/**
- * Coerce to a boolean (not a java.lang.Boolean).
+ * Coerce to a primitive boolean.
+ * <p>Double.NaN, null, "false" and empty string coerce to false.</p>
*
* @param val Object to be coerced.
- * @return the coerced value if coercion is possible, true if no available coercion and val is not null
+ * @return the boolean value if coercion is possible, true if value was not null.
*/
public boolean toBoolean(Object val) {
if (val == null) {
@@ -844,26 +922,31 @@ public class JexlArithmetic {
} else if (val instanceof String) {
String strval = val.toString();
return strval.length() > 0 && !"false".equals(strval);
+ } else {
+ // non null value is true
+ return true;
}
- // non null value is true
- return true;
}
/**
- * Coerce to a int.
+ * Coerce to a primitive int.
+ * <p>Double.NaN, null and empty string coerce to zero.</p>
+ * <p>Boolean false is 0, true is 1.</p>
*
* @param val Object to be coerced.
* @return The int coerced value.
+ * @throws ArithmeticException if val is null and mode is strict or if coercion is not possible.
*/
public int toInteger(Object val) {
if (val == null) {
controlNullOperand();
return 0;
} else if (val instanceof Double) {
- if (!Double.isNaN(((Double) val).doubleValue())) {
+ Double dval = (Double) val;
+ if (Double.isNaN(dval.doubleValue())) {
return 0;
} else {
- return ((Double) val).intValue();
+ return dval.intValue();
}
} else if (val instanceof Number) {
return ((Number) val).intValue();
@@ -883,26 +966,30 @@ public class JexlArithmetic {
}
/**
- * Coerce to a long (not a java.lang.Long).
+ * Coerce to a primitive long.
+ * <p>Double.NaN, null and empty string coerce to zero.</p>
+ * <p>Boolean false is 0, true is 1.</p>
*
* @param val Object to be coerced.
* @return The long coerced value.
+ * @throws ArithmeticException if val is null and mode is strict or if coercion is not possible.
*/
public long toLong(Object val) {
if (val == null) {
controlNullOperand();
return 0L;
} else if (val instanceof Double) {
- if (!Double.isNaN(((Double) val).doubleValue())) {
- return 0;
+ Double dval = (Double) val;
+ if (Double.isNaN(dval.doubleValue())) {
+ return 0L;
} else {
- return ((Double) val).longValue();
+ return dval.longValue();
}
} else if (val instanceof Number) {
return ((Number) val).longValue();
} else if (val instanceof String) {
if ("".equals(val)) {
- return 0;
+ return 0L;
} else {
return Long.parseLong((String) val);
}
@@ -917,11 +1004,13 @@ public class JexlArithmetic {
}
/**
- * Get a BigInteger from the object passed.
- * Null and empty string maps to zero.
+ * Coerce to a BigInteger.
+ * <p>Double.NaN, null and empty string coerce to zero.</p>
+ * <p>Boolean false is 0, true is 1.</p>
+ *
* @param val the object to be coerced.
* @return a BigDecimal.
- * @throws NullPointerException if val is null and mode is strict.
+ * @throws ArithmeticException if val is null and mode is strict or if coercion is not possible.
*/
public BigInteger toBigInteger(Object val) {
if (val == null) {
@@ -930,10 +1019,11 @@ public class JexlArithmetic {
} else if (val instanceof BigInteger) {
return (BigInteger) val;
} else if (val instanceof Double) {
- if (!Double.isNaN(((Double) val).doubleValue())) {
- return new BigInteger(val.toString());
- } else {
+ Double dval = (Double) val;
+ if (Double.isNaN(dval.doubleValue())) {
return BigInteger.ZERO;
+ } else {
+ return new BigInteger(dval.toString());
}
} else if (val instanceof Number) {
return new BigInteger(val.toString());
@@ -954,11 +1044,13 @@ public class JexlArithmetic {
}
/**
- * Get a BigDecimal from the object passed.
- * Null and empty string maps to zero.
+ * Coerce to a BigDecimal.
+ * <p>Double.NaN, null and empty string coerce to zero.</p>
+ * <p>Boolean false is 0, true is 1.</p>
+ *
* @param val the object to be coerced.
* @return a BigDecimal.
- * @throws NullPointerException if val is null and mode is strict.
+ * @throws ArithmeticException if val is null and mode is strict or if coercion is not possible.
*/
public BigDecimal toBigDecimal(Object val) {
if (val instanceof BigDecimal) {
@@ -973,10 +1065,10 @@ public class JexlArithmetic {
}
return roundBigDecimal(new BigDecimal(string, getMathContext()));
} else if (val instanceof Double) {
- if (!Double.isNaN(((Double) val).doubleValue())) {
- return roundBigDecimal(new BigDecimal(val.toString(), getMathContext()));
- } else {
+ if (Double.isNaN(((Double) val).doubleValue())) {
return BigDecimal.ZERO;
+ } else {
+ return roundBigDecimal(new BigDecimal(val.toString(), getMathContext()));
}
} else if (val instanceof Number) {
return roundBigDecimal(new BigDecimal(val.toString(), getMathContext()));
@@ -984,17 +1076,17 @@ public class JexlArithmetic {
int i = ((Character) val).charValue();
return new BigDecimal(i);
}
-
throw new ArithmeticException("BigDecimal coercion: "
+ val.getClass().getName() + ":(" + val + ")");
}
/**
- * Coerce to a double.
- *
+ * Coerce to a primitive double.
+ * <p>Double.NaN, null and empty string coerce to zero.</p>
+ * <p>Boolean false is 0, true is 1.</p>
* @param val Object to be coerced.
* @return The double coerced value.
- * @throws NullPointerException if val is null and mode is strict.
+ * @throws ArithmeticException if val is null and mode is strict or if coercion is not possible.
*/
public double toDouble(Object val) {
if (val == null) {
@@ -1020,17 +1112,17 @@ public class JexlArithmetic {
int i = ((Character) val).charValue();
return i;
}
-
throw new ArithmeticException("Double coercion: "
+ val.getClass().getName() + ":(" + val + ")");
}
/**
* Coerce to a string.
+ * <p>Double.NaN coerce to the empty string.</p>
*
* @param val Object to be coerced.
* @return The String coerced value.
- * @throws NullPointerException if val is null and mode is strict.
+ * @throws ArithmeticException if val is null and mode is strict or if coercion is not possible.
*/
public String toString(Object val) {
if (val == null) {
@@ -1048,98 +1140,4 @@ public class JexlArithmetic {
}
}
- /**
- * Given a Number, return back the value using the smallest type the result
- * will fit into. This works hand in hand with parameter 'widening' in java
- * method calls, e.g. a call to substring(int,int) with an int and a long
- * will fail, but a call to substring(int,int) with an int and a short will
- * succeed.
- *
- * @param original the original number.
- * @return a value of the smallest type the original number will fit into.
- */
- public Number narrow(Number original) {
- return narrowNumber(original, null);
- }
-
- /**
- * Whether we consider the narrow class as a potential candidate for narrowing the source.
- * @param narrow the target narrow class
- * @param source the orginal source class
- * @return true if attempt to narrow source to target is accepted
- * @since 2.1
- */
- protected boolean narrowAccept(Class<?> narrow, Class<?> source) {
- return narrow == null || narrow.equals(source);
- }
-
- /**
- * Given a Number, return back the value attempting to narrow it to a target class.
- * @param original the original number
- * @param narrow the attempted target class
- * @return the narrowed number or the source if no narrowing was possible
- * @since 2.1
- */
- protected Number narrowNumber(Number original, Class<?> narrow) {
- if (original == null) {
- return original;
- }
- Number result = original;
- if (original instanceof BigDecimal) {
- BigDecimal bigd = (BigDecimal) original;
- // if it's bigger than a double it can't be narrowed
- if (bigd.compareTo(BIGD_DOUBLE_MAX_VALUE) > 0) {
- return original;
- } else {
- try {
- long l = bigd.longValueExact();
- // coerce to int when possible (int being so often used in method parms)
- if (narrowAccept(narrow, Integer.class)
- && l <= Integer.MAX_VALUE
- && l >= Integer.MIN_VALUE) {
- return Integer.valueOf((int) l);
- } else if (narrowAccept(narrow, Long.class)) {
- return Long.valueOf(l);
- }
- } catch (ArithmeticException xa) {
- // ignore, no exact value possible
- }
- }
- }
- if (original instanceof Double || original instanceof Float || original instanceof BigDecimal) {
- double value = original.doubleValue();
- if (narrowAccept(narrow, Float.class)
- && value <= Float.MAX_VALUE
- && value >= Float.MIN_VALUE) {
- result = Float.valueOf(result.floatValue());
- }
- // else it fits in a double only
- } else {
- if (original instanceof BigInteger) {
- 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) {
- return original;
- }
- }
- long value = original.longValue();
- if (narrowAccept(narrow, Byte.class)
- && value <= Byte.MAX_VALUE
- && value >= Byte.MIN_VALUE) {
- // it will fit in a byte
- result = Byte.valueOf((byte) value);
- } else if (narrowAccept(narrow, Short.class)
- && value <= Short.MAX_VALUE
- && value >= Short.MIN_VALUE) {
- result = Short.valueOf((short) value);
- } else if (narrowAccept(narrow, Integer.class)
- && value <= Integer.MAX_VALUE
- && value >= Integer.MIN_VALUE) {
- result = Integer.valueOf((int) value);
- }
- // else it fits in a long
- }
- return result;
- }
}
\ No newline at end of file
Modified: commons/proper/jexl/branches/2.0/src/main/java/org/apache/commons/jexl2/JexlException.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/main/java/org/apache/commons/jexl2/JexlException.java?rev=1337271&r1=1337270&r2=1337271&view=diff
==============================================================================
--- commons/proper/jexl/branches/2.0/src/main/java/org/apache/commons/jexl2/JexlException.java (original)
+++ commons/proper/jexl/branches/2.0/src/main/java/org/apache/commons/jexl2/JexlException.java Fri May 11 16:36:20 2012
@@ -259,6 +259,7 @@ public class JexlException extends Runti
* Creates a new Property exception instance.
* @param node the offending ASTnode
* @param var the unknown variable
+ * @param cause the exception causing the error
*/
public Property(JexlNode node, String var, Throwable cause) {
super(node, var, cause);
@@ -286,6 +287,7 @@ public class JexlException extends Runti
* Creates a new Method exception instance.
* @param node the offending ASTnode
* @param name the unknown method
+ * @param cause the exception causing the error
*/
public Method(JexlNode node, String name, Throwable cause) {
super(node, name, cause);
Modified: commons/proper/jexl/branches/2.0/src/site/xdoc/changes.xml
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/site/xdoc/changes.xml?rev=1337271&r1=1337270&r2=1337271&view=diff
==============================================================================
--- commons/proper/jexl/branches/2.0/src/site/xdoc/changes.xml (original)
+++ commons/proper/jexl/branches/2.0/src/site/xdoc/changes.xml Fri May 11 16:36:20 2012
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!--
+<!--
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
@@ -8,9 +8,9 @@
* 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.
@@ -25,7 +25,18 @@
<author email="dev@commons.apache.org">Commons Developers</author>
</properties>
<body>
- <release version="2.1.1" date="2011-12-??">
+ <release version="2.1.2" date="2012-05-30">
+ <action dev="henrib" type="fix" issue="JEXL-131" due-to="Clay Bruce">
+ UnifiedJexl (2.1.x) parsing may fail with NPE
+ </action>
+ <action dev="henrib" type="fix" issue="JEXL-130" due-to="William Bakker">
+ Ternary Conditional fails for Object values
+ </action>
+ <action dev="henrib" type="add" issue="JEXL-126" due-to="Grace">
+ Decimal numbers literals fix be 'double' by default (instead of 'float')
+ </action>
+ </release>
+ <release version="2.1.1" date="2011-12-24">
<action dev="henrib" type="fix" issue="JEXL-124">
Array parameters to methods don't work anymore (regression)
</action>
Modified: commons/proper/jexl/branches/2.0/src/test/java/org/apache/commons/jexl2/IssuesTest.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/test/java/org/apache/commons/jexl2/IssuesTest.java?rev=1337271&r1=1337270&r2=1337271&view=diff
==============================================================================
--- commons/proper/jexl/branches/2.0/src/test/java/org/apache/commons/jexl2/IssuesTest.java (original)
+++ commons/proper/jexl/branches/2.0/src/test/java/org/apache/commons/jexl2/IssuesTest.java Fri May 11 16:36:20 2012
@@ -841,7 +841,6 @@ public class IssuesTest extends JexlTest
private final JexlEngine jexl;
private final Foo125 object;
- @Override
public Object resolveNamespace(String name) {
if (name == null) {
return object;
@@ -922,4 +921,134 @@ public class IssuesTest extends JexlTest
Object myObjectWithTernaryConditional = myJexlEngine.createScript(myName + "?:null").execute(myMapContext);
assertEquals(myValue, myObjectWithTernaryConditional);
}
+
+ public static class Arithmetic132 extends JexlArithmetic {
+ public Arithmetic132() {
+ super(false);
+ }
+
+ protected double divideZero(BigDecimal x) {
+ int ls = x.signum();
+ if (ls < 0) {
+ return Double.NEGATIVE_INFINITY;
+ } else if (ls > 0) {
+ return Double.POSITIVE_INFINITY;
+ } else {
+ return Double.NaN;
+ }
+ }
+
+ protected double divideZero(BigInteger x) {
+ int ls = x.signum();
+ if (ls < 0) {
+ return Double.NEGATIVE_INFINITY;
+ } else if (ls > 0) {
+ return Double.POSITIVE_INFINITY;
+ } else {
+ return Double.NaN;
+ }
+ }
+
+ @Override
+ public Object divide(Object left, Object right) {
+ if (left == null && right == null) {
+ return controlNullNullOperands();
+ }
+ // if either are bigdecimal use that type
+ if (left instanceof BigDecimal || right instanceof BigDecimal) {
+ BigDecimal l = toBigDecimal(left);
+ BigDecimal r = toBigDecimal(right);
+ if (BigDecimal.ZERO.equals(r)) {
+ return divideZero(l);
+ }
+ BigDecimal result = l.divide(r, getMathContext());
+ return narrowBigDecimal(left, right, result);
+ }
+ // if either are floating point (double or float) use double
+ if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
+ double l = toDouble(left);
+ double r = toDouble(right);
+ return new Double(l / r);
+ }
+ // otherwise treat as integers
+ BigInteger l = toBigInteger(left);
+ BigInteger r = toBigInteger(right);
+ if (BigInteger.ZERO.equals(r)) {
+ return divideZero(l);
+ }
+ BigInteger result = l.divide(r);
+ return narrowBigInteger(left, right, result);
+ }
+
+ @Override
+ public Object mod(Object left, Object right) {
+ if (left == null && right == null) {
+ return controlNullNullOperands();
+ }
+ // if either are bigdecimal use that type
+ if (left instanceof BigDecimal || right instanceof BigDecimal) {
+ BigDecimal l = toBigDecimal(left);
+ BigDecimal r = toBigDecimal(right);
+ if (BigDecimal.ZERO.equals(r)) {
+ return divideZero(l);
+ }
+ BigDecimal remainder = l.remainder(r, getMathContext());
+ return narrowBigDecimal(left, right, remainder);
+ }
+ // if either are floating point (double or float) use double
+ if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
+ double l = toDouble(left);
+ double r = toDouble(right);
+ return new Double(l % r);
+ }
+ // otherwise treat as integers
+ BigInteger l = toBigInteger(left);
+ BigInteger r = toBigInteger(right);
+ BigInteger result = l.mod(r);
+ if (BigInteger.ZERO.equals(r)) {
+ return divideZero(l);
+ }
+ return narrowBigInteger(left, right, result);
+ }
+ }
+
+ JexlEngine createJexl132() {
+ Map<String, Object> ns = new HashMap<String, Object>();
+ ns.put("math", Math.class);
+ return new JexlEngine(null, new Arithmetic132(), ns, null);
+
+ }
+
+ public void test132a() throws Exception {
+ JexlEngine jexl = createJexl132();
+ String expr = "1/0";
+
+ Object evaluate = jexl.createExpression(expr).evaluate(null);
+ assertTrue(Double.isInfinite((Double) evaluate));
+
+ expr = "-1/0";
+ evaluate = jexl.createExpression(expr).evaluate(null);
+ assertTrue(Double.isInfinite((Double) evaluate));
+ }
+
+ public void test132b() throws Exception {
+ JexlEngine jexl = createJexl132();
+ String expr = "1.0/0.0";
+
+ Object evaluate = jexl.createExpression(expr).evaluate(null);
+ assertTrue(Double.isInfinite((Double) evaluate));
+
+ expr = "-1.0/0";
+ evaluate = jexl.createExpression(expr).evaluate(null);
+ assertTrue(Double.isInfinite((Double) evaluate));
+ }
+
+ public void test132c() throws Exception {
+ JexlEngine jexl = createJexl132();
+
+ String expr = "math:abs(-42)";
+
+ Object evaluate = jexl.createExpression(expr).evaluate(null);
+ assertEquals(42, evaluate);
+ }
}