You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by di...@apache.org on 2007/11/04 14:58:44 UTC

svn commit: r591784 - in /commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl: ExpressionFactory.java ExpressionImpl.java Interpreter.java ScriptFactory.java ScriptImpl.java

Author: dion
Date: Sun Nov  4 05:58:44 2007
New Revision: 591784

URL: http://svn.apache.org/viewvc?rev=591784&view=rev
Log:
use pluggable arithmetic operators

Modified:
    commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/ExpressionFactory.java
    commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/ExpressionImpl.java
    commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/Interpreter.java
    commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/ScriptFactory.java
    commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/ScriptImpl.java

Modified: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/ExpressionFactory.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/ExpressionFactory.java?rev=591784&r1=591783&r2=591784&view=diff
==============================================================================
--- commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/ExpressionFactory.java (original)
+++ commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/ExpressionFactory.java Sun Nov  4 05:58:44 2007
@@ -142,7 +142,10 @@
         // throw an exception.
         SimpleNode node = (SimpleNode) tree.jjtGetChild(0);
 
-        Interpreter interpreter = new Interpreter();
+        Interpreter interpreter = new Interpreter(
+            null,
+            Introspector.getUberspect(), 
+            new JexlArithmetic());
         // TODO: remove this from the parser.
         // interpreter.setUberspect(parser.getUberspect());
         return new ExpressionImpl(expression, node, interpreter);

Modified: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/ExpressionImpl.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/ExpressionImpl.java?rev=591784&r1=591783&r2=591784&view=diff
==============================================================================
--- commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/ExpressionImpl.java (original)
+++ commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/ExpressionImpl.java Sun Nov  4 05:58:44 2007
@@ -49,9 +49,7 @@
      */
     protected SimpleNode node;
 
-    /**
-     * The interpreter of the expression.
-     */
+    /** The interpreter of the expression. */
     protected Interpreter interpreter;
 
     /**

Modified: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/Interpreter.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/Interpreter.java?rev=591784&r1=591783&r2=591784&view=diff
==============================================================================
--- commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/Interpreter.java (original)
+++ commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/Interpreter.java Sun Nov  4 05:58:44 2007
@@ -75,7 +75,6 @@
 import org.apache.commons.jexl.parser.SimpleNode;
 import org.apache.commons.jexl.parser.VisitorAdapter;
 import org.apache.commons.jexl.util.Coercion;
-import org.apache.commons.jexl.util.Introspector;
 import org.apache.commons.jexl.util.introspection.Info;
 import org.apache.commons.jexl.util.introspection.Uberspect;
 import org.apache.commons.jexl.util.introspection.VelMethod;
@@ -83,53 +82,48 @@
 
 /**
  * An interpreter of JEXL syntax.
- * 
+ *
  * @since 2.0
  */
 class Interpreter extends VisitorAdapter {
 
     /** The uberspect. */
     private Uberspect uberspect;
-
     /** The context to store/retrieve variables. */
     private JexlContext context;
+    /** the arithmetic handler. */
+    private Arithmetic arithmetic;
 
     /** dummy velocity info. */
     private static final Info DUMMY = new Info("", 1, 1);
 
     /**
-     * Create the interpreter with the default settings.
-     */
-    public Interpreter() {
-        super();
-        setUberspect(Introspector.getUberspect());
+     * Create the interpreter.
+     * @param ctx the context to retrieve variables from.
+     * @param uber the helper to perform introspection,
+     * @param arith the arithmetic handler
+     */
+    public Interpreter(JexlContext ctx, Uberspect uber, Arithmetic arith) {
+        uberspect = uber;
+        context = ctx;
+        arithmetic = arith;
     }
 
     /**
      * Interpret the given script/expression.
-     * 
+     *
      * @param node the script or expression to interpret.
      * @param aContext the context to interpret against.
      * @return the result of the interpretation.
      */
     public Object interpret(SimpleNode node, JexlContext aContext) {
-        setContext(aContext);
+        context = aContext;
         return node.jjtAccept(this, null);
     }
 
     /**
-     * TODO: Does this need to be a setter. sets the uberspect to use for
-     * divining bean properties etc.
-     * 
-     * @param anUberspect the uberspect.
-     */
-    public void setUberspect(Uberspect anUberspect) {
-        uberspect = anUberspect;
-    }
-
-    /**
      * Gets the uberspect.
-     * 
+     *
      * @return an {@link Uberspect}
      */
     protected Uberspect getUberspect() {
@@ -138,7 +132,7 @@
 
     /**
      * Sets the context that contain variables.
-     * 
+     *
      * @param aContext a {link JexlContext}
      */
     public void setContext(JexlContext aContext) {
@@ -150,49 +144,7 @@
     public Object visit(ASTAddNode node, Object data) {
         Object left = node.jjtGetChild(0).jjtAccept(this, data);
         Object right = node.jjtGetChild(1).jjtAccept(this, data);
-
-        // the spec says 'and'
-        if (left == null && right == null) {
-            return new Long(0);
-        }
-
-        if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
-
-            // in the event that either is null and not both, then just make the
-            // null a 0
-            try {
-                double l = Coercion.coercedouble(left);
-                double r = Coercion.coercedouble(right);
-                return new Double(l + r);
-            } catch (java.lang.NumberFormatException nfe) {
-                // Well, use strings!
-                return left.toString().concat(right.toString());
-            }
-        }
-        
-        if (left instanceof BigDecimal || right instanceof BigDecimal) {
-            // coerce both to big decimal and add
-            BigDecimal l = Coercion.coerceBigDecimal(left);
-            BigDecimal r = Coercion.coerceBigDecimal(right);
-            return l.add(r);
-        }
-
-        if (left instanceof BigInteger || right instanceof BigInteger) {
-            // coerce both to big decimal and add
-            BigInteger l = Coercion.coerceBigInteger(left);
-            BigInteger r = Coercion.coerceBigInteger(right);
-            return l.add(r);
-        }
-
-        // attempt to use Longs
-        try {
-            long l = Coercion.coercelong(left);
-            long r = Coercion.coercelong(right);
-            return new Long(l + r);
-        } catch (java.lang.NumberFormatException nfe) {
-            // Well, use strings!
-            return left.toString().concat(right.toString());
-        }
+        return arithmetic.add(left, right);
     }
 
     /** {@inheritDoc} */
@@ -293,35 +245,8 @@
 
         Object left = node.jjtGetChild(0).jjtAccept(this, data);
         Object right = node.jjtGetChild(1).jjtAccept(this, data);
-
-        // the spec says 'and', I think 'or'
-        if (left == null && right == null) {
-            return new Byte((byte) 0);
-        }
-
-        if (left instanceof BigDecimal || right instanceof BigDecimal) {
-            // coerce both to big decimal and add
-            BigDecimal l = Coercion.coerceBigDecimal(left);
-            BigDecimal r = Coercion.coerceBigDecimal(right);
-            return l.divide(r, BigDecimal.ROUND_HALF_UP);
-        }
-
-        if (left instanceof BigInteger || right instanceof BigInteger) {
-            // coerce both to big decimal and add
-            BigInteger l = Coercion.coerceBigInteger(left);
-            BigInteger r = Coercion.coerceBigInteger(right);
-            return l.divide(r);
-        }
-
-        Double l = Coercion.coerceDouble(left);
-        Double r = Coercion.coerceDouble(right);
-
-        // catch div/0
-        if (r.doubleValue() == 0.0) {
-            return r;
-        }
-
-        return new Double(l.doubleValue() / r.doubleValue());
+        
+        return arithmetic.divide(left, right);
     }
 
     /** {@inheritDoc} */
@@ -503,7 +428,7 @@
     /** {@inheritDoc} */
     public Object visit(ASTMapEntry node, Object data) {
         Object key = node.jjtGetChild(0).jjtAccept(this, data);
-        Object value = node.jjtGetChild(1).jjtAccept(this, data); 
+        Object value = node.jjtGetChild(1).jjtAccept(this, data);
         return new Object[] {key, value};
     }
 
@@ -576,95 +501,15 @@
         Object left = node.jjtGetChild(0).jjtAccept(this, data);
         Object right = node.jjtGetChild(1).jjtAccept(this, data);
 
-        // the spec says 'and', I think 'or'
-        if (left == null && right == null) {
-            return new Byte((byte) 0);
-        }
-
-        // if anything is float, double or string with ( "." | "E" | "e") coerce
-        // all to doubles and do it
-        if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
-            Double l = Coercion.coerceDouble(left);
-            Double r = Coercion.coerceDouble(right);
-
-            // catch div/0
-            if (r.doubleValue() == 0.0) {
-                return r;
-            }
-
-            return new Double(l.doubleValue() % r.doubleValue());
-        }
-
-        if (left instanceof BigDecimal || right instanceof BigDecimal) {
-            // coerce both to big decimal and add
-            BigDecimal l = Coercion.coerceBigDecimal(left);
-            BigDecimal r = Coercion.coerceBigDecimal(right);
-            BigInteger intDiv = l.divide(r, BigDecimal.ROUND_HALF_UP).toBigInteger();
-            BigInteger intValue = (r.multiply(new BigDecimal(intDiv))).toBigInteger();
-            BigDecimal remainder = new BigDecimal(l.subtract(new BigDecimal(intValue)).toBigInteger());
-            return remainder;
-        }
-
-        if (left instanceof BigInteger || right instanceof BigInteger) {
-            // coerce both to big decimal and add
-            BigInteger l = Coercion.coerceBigInteger(left);
-            BigInteger r = Coercion.coerceBigInteger(right);
-            return l.mod(r);
-        }
-
-        // otherwise to longs with thee!
-
-        long l = Coercion.coercelong(left);
-        long r = Coercion.coercelong(right);
-
-        // catch the div/0
-        if (r == 0) {
-            return new Long(0);
-        }
-
-        return new Long(l % r);
+        return arithmetic.mod(left, right);
     }
 
     /** {@inheritDoc} */
     public Object visit(ASTMulNode node, Object data) {
-
         Object left = node.jjtGetChild(0).jjtAccept(this, data);
         Object right = node.jjtGetChild(1).jjtAccept(this, data);
-
-        // the spec says 'and', I think 'or'
-        if (left == null && right == null) {
-            return new Byte((byte) 0);
-        }
-
-        // if anything is float, double or string with ( "." | "E" | "e") coerce
-        // all to doubles and do it
-        if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
-            Double l = Coercion.coerceDouble(left);
-            Double r = Coercion.coerceDouble(right);
-
-            return new Double(l.doubleValue() * r.doubleValue());
-        }
-
-        if (left instanceof BigDecimal || right instanceof BigDecimal) {
-            // coerce both to big decimal and add
-            BigDecimal l = Coercion.coerceBigDecimal(left);
-            BigDecimal r = Coercion.coerceBigDecimal(right);
-            return l.multiply(r);
-        }
-
-        if (left instanceof BigInteger || right instanceof BigInteger) {
-            // coerce both to big decimal and add
-            BigInteger l = Coercion.coerceBigInteger(left);
-            BigInteger r = Coercion.coerceBigInteger(right);
-            return l.multiply(r);
-        }
-
-        // otherwise to longs with thee!
-
-        long l = Coercion.coercelong(left);
-        long r = Coercion.coercelong(right);
-
-        return new Long(l * r);
+        
+        return arithmetic.multiply(left, right);
     }
 
     /** {@inheritDoc} */
@@ -769,46 +614,10 @@
 
     /** {@inheritDoc} */
     public Object visit(ASTSubtractNode node, Object data) {
-
         Object left = node.jjtGetChild(0).jjtAccept(this, data);
         Object right = node.jjtGetChild(1).jjtAccept(this, data);
-
-        // the spec says 'and', I think 'or'
-        if (left == null && right == null) {
-            return new Byte((byte) 0);
-        }
-
-        // if anything is float, double or string with ( "." | "E" | "e") coerce
-        // all to doubles and do it
-        if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
-
-            // in the event that either is null and not both, then just make the
-            // null a 0
-            double l = Coercion.coercedouble(left);
-            double r = Coercion.coercedouble(right);
-            return new Double(l - r);
-        }
-
-        if (left instanceof BigDecimal || right instanceof BigDecimal) {
-            // coerce both to big decimal and add
-            BigDecimal l = Coercion.coerceBigDecimal(left);
-            BigDecimal r = Coercion.coerceBigDecimal(right);
-            return l.subtract(r);
-        }
-
-        if (left instanceof BigInteger || right instanceof BigInteger) {
-            // coerce both to big decimal and add
-            BigInteger l = Coercion.coerceBigInteger(left);
-            BigInteger r = Coercion.coerceBigInteger(right);
-            return l.subtract(r);
-        }
         
-        // otherwise to longs with thee!
-
-        long l = Coercion.coercelong(left);
-        long r = Coercion.coercelong(right);
-
-        return new Long(l - r);
+        return arithmetic.subtract(left, right);
     }
 
     /** {@inheritDoc} */
@@ -870,23 +679,37 @@
      * 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.
      * @since 1.1
      */
     private Number narrow(Number original) {
-        if (original == null || original instanceof BigDecimal || original instanceof BigInteger) {
+        if (original == null) {
             return original;
         }
         Number result = original;
-        if (original instanceof Double || original instanceof Float) {
+        if (original instanceof BigDecimal) {
+            BigDecimal bigd = (BigDecimal) original;
+            // if it's bigger than a double it can't be narrowed
+            if (bigd.compareTo(new BigDecimal(Double.MAX_VALUE)) > 0) {
+                return original;
+            }
+        }
+        if (original instanceof Double || original instanceof Float || original instanceof BigDecimal) {
             double value = original.doubleValue();
             if (value <= Float.MAX_VALUE && value >= Float.MIN_VALUE) {
                 result = new Float(result.floatValue());
             }
-            // else it was already a double
+            // 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(new BigInteger(String.valueOf(Long.MAX_VALUE))) > 0) {
+                    return original;
+                }
+            }
             long value = original.longValue();
             if (value <= Byte.MAX_VALUE && value >= Byte.MIN_VALUE) {
                 // it will fit in a byte
@@ -896,7 +719,7 @@
             } else if (value <= Integer.MAX_VALUE && value >= Integer.MIN_VALUE) {
                 result = new Integer((int) value);
             }
-            // else it was already a long
+            // else it fits in a long
         }
         return result;
     }
@@ -904,7 +727,7 @@
     /**
      * Calculate the <code>size</code> of various types: Collection, Array,
      * Map, String, and anything that has a int size() method.
-     * 
+     *
      * @param val the object to get the size of.
      * @return the size of val
      */
@@ -938,26 +761,8 @@
     }
 
     /**
-     * Test if the passed value is a floating point number, i.e. a float, double
-     * or string with ( "." | "E" | "e").
-     * 
-     * @param val the object to be tested
-     * @return true if it is, false otherwise.
-     */
-    private boolean isFloatingPointNumber(Object val) {
-        if (val instanceof Float || val instanceof Double) {
-            return true;
-        }
-        if (val instanceof String) {
-            String string = (String) val;
-            return string.indexOf(".") != -1 || string.indexOf("e") != -1 || string.indexOf("E") != -1;
-        }
-        return false;
-    }
-
-    /**
      * Get an attribute of an object.
-     * 
+     *
      * @param object to retrieve value from
      * @param attribute the attribute of the object, e.g. an index (1, 0, 2) or
      *            key for a map
@@ -1007,7 +812,7 @@
 
     /**
      * Test if left and right are equal.
-     * 
+     *
      * @param left first value
      * @param right second value
      * @return test result.
@@ -1056,7 +861,7 @@
 
     /**
      * Test if left < right.
-     * 
+     *
      * @param left first value
      * @param right second value
      * @return test result.
@@ -1096,7 +901,7 @@
 
     /**
      * Test if left > right.
-     * 
+     *
      * @param left first value
      * @param right second value
      * @return test result.
@@ -1110,7 +915,7 @@
 
     /**
      * Test if left <= right.
-     * 
+     *
      * @param left first value
      * @param right second value
      * @return test result.
@@ -1121,7 +926,7 @@
 
     /**
      * Test if left >= right.
-     * 
+     *
      * @param left first value
      * @param right second value
      * @return test result.

Modified: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/ScriptFactory.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/ScriptFactory.java?rev=591784&r1=591783&r2=591784&view=diff
==============================================================================
--- commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/ScriptFactory.java (original)
+++ commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/ScriptFactory.java Sun Nov  4 05:58:44 2007
@@ -68,7 +68,7 @@
 
     /**
      * ScriptFactory is a singleton and this is the private
-     * instance fufilling that pattern.
+     * instance fulfilling that pattern.
      */
     protected static ScriptFactory factory = new ScriptFactory();
 
@@ -176,7 +176,11 @@
             }
         }
         if (script instanceof ASTJexlScript) {
-            return new ScriptImpl(cleanText, (ASTJexlScript) script);
+            Interpreter interpreter = new Interpreter(
+                null,
+                Introspector.getUberspect(), 
+                new JexlArithmetic());
+            return new ScriptImpl(cleanText, (ASTJexlScript) script, interpreter);
         } else {
             throw new IllegalStateException("Parsed script is not "
                 + "an ASTJexlScript");

Modified: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/ScriptImpl.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/ScriptImpl.java?rev=591784&r1=591783&r2=591784&view=diff
==============================================================================
--- commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/ScriptImpl.java (original)
+++ commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/ScriptImpl.java Sun Nov  4 05:58:44 2007
@@ -29,24 +29,26 @@
     private final String text;
     /** syntax tree. */
     private final ASTJexlScript parsedScript;
+    /** The interpreter of the expression. */
+    protected Interpreter interpreter;
 
     /**
      * Create a new Script from the given string and parsed syntax.
      * @param scriptText the text of the script.
      * @param scriptTree the parsed script.
+     * @param interp the interpreter to evaluate the expression
      */
-    public ScriptImpl(String scriptText, ASTJexlScript scriptTree) {
+    public ScriptImpl(String scriptText, ASTJexlScript scriptTree, Interpreter interp) {
         text = scriptText;
         parsedScript = scriptTree;
+        interpreter = interp;
     }
 
     /**
      * {@inheritDoc}
      */
     public Object execute(JexlContext context) throws Exception {
-        Interpreter interpreter = new Interpreter();
         return interpreter.interpret(parsedScript, context);
-        //return parsedScript.value(context);
     }
 
     /**