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  &lt; right; +1 if left &gt > right; 0 if left == right
+     * @return -1 if left &lt; right; +1 if left &gt > 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 &lt; 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 &gt; 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 &lt;= 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 &gt;= 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);
+    }
 }