You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by ra...@apache.org on 2009/08/01 02:36:52 UTC

svn commit: r799779 [1/3] - in /commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl: ./ context/ parser/ scripting/ util/ util/introspection/

Author: rahul
Date: Sat Aug  1 00:36:51 2009
New Revision: 799779

URL: http://svn.apache.org/viewvc?rev=799779&view=rev
Log:
JEXL-60
Step 2 of 4

Refactor two packages:
 - org.apache.commons.jexl.util
 - org.apache.commons.jexl.util.introspection

Changes include:
 * A more complete set of specialized executors for property, map, list, duck setters and getters etc.
 * Support for the new() function which allows calling constructors
 * Added two missing package.html files
 * New DebugInfo interface for use with Uberspects
 * Separated out MethodKey class
 * Type safety and Javadoc changes

Based on patch by Henri Biestro <hbiestro at gmail dot com> with minor changes.

Added:
    commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/parser/JexlNode.java   (with props)
    commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/parser/SimpleNode.java   (with props)
    commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/BooleanGetExecutor.java   (with props)
    commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/DuckGetExecutor.java   (with props)
    commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/DuckSetExecutor.java   (with props)
    commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/ListGetExecutor.java   (with props)
    commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/ListSetExecutor.java   (with props)
    commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/MapSetExecutor.java   (with props)
    commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/MethodExecutor.java   (with props)
    commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/PropertyGetExecutor.java   (with props)
    commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/PropertySetExecutor.java   (with props)
    commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/DebugInfo.java   (with props)
    commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/MethodKey.java   (with props)
    commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/package.html   (with props)
    commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/package.html   (with props)
Modified:
    commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/Debugger.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/JexlArithmetic.java
    commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/JexlContext.java
    commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/JexlEngine.java
    commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/JexlException.java
    commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/JexlHelper.java
    commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/UnifiedJEXL.java
    commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/context/HashMapContext.java
    commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/parser/Parser.jjt
    commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/parser/VisitorAdapter.java
    commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/scripting/JexlScriptEngine.java
    commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/AbstractExecutor.java
    commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/ArrayIterator.java
    commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/ArrayListWrapper.java
    commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/EnumerationIterator.java
    commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/Introspector.java
    commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/MapGetExecutor.java
    commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/ClassMap.java
    commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/Info.java
    commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/Introspector.java
    commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/IntrospectorBase.java
    commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/MethodMap.java
    commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/Uberspect.java
    commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/UberspectImpl.java

Modified: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/Debugger.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/Debugger.java?rev=799779&r1=799778&r2=799779&view=diff
==============================================================================
--- commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/Debugger.java (original)
+++ commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/Debugger.java Sat Aug  1 00:36:51 2009
@@ -25,6 +25,7 @@
 import org.apache.commons.jexl.parser.ASTBitwiseOrNode;
 import org.apache.commons.jexl.parser.ASTBitwiseXorNode;
 import org.apache.commons.jexl.parser.ASTBlock;
+import org.apache.commons.jexl.parser.ASTConstructorNode;
 import org.apache.commons.jexl.parser.ASTDivNode;
 import org.apache.commons.jexl.parser.ASTEQNode;
 import org.apache.commons.jexl.parser.ASTEmptyFunction;
@@ -62,7 +63,7 @@
 import org.apache.commons.jexl.parser.ASTTrueNode;
 import org.apache.commons.jexl.parser.ASTUnaryMinusNode;
 import org.apache.commons.jexl.parser.ASTWhileStatement;
-import org.apache.commons.jexl.parser.Node;
+import org.apache.commons.jexl.parser.JexlNode;
 import org.apache.commons.jexl.parser.SimpleNode;
 
 import org.apache.commons.jexl.parser.ParserVisitor;
@@ -78,7 +79,7 @@
     /** The builder to compose messages. */
     private StringBuilder builder;
     /** The cause of the issue to debug. */
-    private Node cause;
+    private JexlNode cause;
     /** The starting character location offset of the cause in the builder. */
     private int start;
     /** The ending character location offset of the cause in the builder. */
@@ -99,14 +100,14 @@
      * @param node the node to debug
      * @return true if the cause was located, false otherwise
      */
-    public boolean debug(Node node) {
+    public boolean debug(JexlNode node) {
         start = 0;
         end = 0;
         if (node != null) {
             builder = new StringBuilder();
             this.cause = node;
             // make arg cause become the root cause
-            Node root = node;
+            JexlNode root = node;
             while (root.jjtGetParent() != null) {
                 root = root.jjtGetParent();
             }
@@ -143,7 +144,7 @@
      * @param data visitor pattern argument
      * @return visitor pattern value
      */
-    private Object accept(Node node, Object data) {
+    private Object accept(JexlNode node, Object data) {
         if (node == cause) {
             start = builder.length();
         }
@@ -162,7 +163,7 @@
      * @param data visitor pattern argument
      * @return visitor pattern value
      */
-    private Object check(Node node, String image, Object data) {
+    private Object check(JexlNode node, String image, Object data) {
         if (node == cause) {
             start = builder.length();
         }
@@ -185,7 +186,7 @@
      * @param data visitor pattern argument
      * @return visitor pattern value
      */
-    private Object infixChildren(Node node, String infix, Object data) {
+    private Object infixChildren(JexlNode node, String infix, Object data) {
         int num = node.jjtGetNumChildren();
         for (int i = 0; i < num; ++i) {
             if (i > 0) {
@@ -389,6 +390,22 @@
     }
 
     /** {@inheritDoc} */
+    public Object visit(ASTConstructorNode node, Object data) {
+        int num = node.jjtGetNumChildren();
+        builder.append("new ");
+        accept(node.jjtGetChild(0), data);
+        builder.append("(");
+        for (int i = 1; i < num; ++i) {
+            if (i > 1) {
+                builder.append(", ");
+            }
+            accept(node.jjtGetChild(i), data);
+        }
+        builder.append(")");
+        return data;
+    }
+
+    /** {@inheritDoc} */
     public Object visit(ASTFunctionNode node, Object data) {
         int num = node.jjtGetNumChildren();
         accept(node.jjtGetChild(0), data);
@@ -553,10 +570,6 @@
 
     /** {@inheritDoc} */
     public Object visit(SimpleNode node, Object data) {
-        int num = node.jjtGetNumChildren();
-        for (int i = 0; i < num; ++i) {
-            accept(node.jjtGetChild(i), data);
-        }
-        return data;
+        throw new UnsupportedOperationException("unexpected type of node");
     }
 }
\ No newline at end of file

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=799779&r1=799778&r2=799779&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 Sat Aug  1 00:36:51 2009
@@ -17,7 +17,7 @@
 
 package org.apache.commons.jexl;
 
-import org.apache.commons.jexl.parser.SimpleNode;
+import org.apache.commons.jexl.parser.JexlNode;
 
 /**
  * Instances of ExpressionImpl are created by the {@link JexlEngine},
@@ -39,7 +39,7 @@
     /**
      * The resulting AST we can interpret.
      */
-    protected final SimpleNode node;
+    protected final JexlNode node;
 
 
     /**
@@ -49,7 +49,7 @@
      * @param expr the expression.
      * @param ref the parsed expression.
      */
-    ExpressionImpl(JexlEngine engine, String expr, SimpleNode ref) {
+    ExpressionImpl(JexlEngine engine, String expr, JexlNode ref) {
         expression = expr;
         node = ref;
         jexl = engine;

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=799779&r1=799778&r2=799779&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 Sat Aug  1 00:36:51 2009
@@ -16,6 +16,7 @@
  */
 package org.apache.commons.jexl;
 
+import java.lang.reflect.Constructor;
 import java.lang.reflect.Array;
 import java.lang.reflect.InvocationTargetException;
 import java.math.BigDecimal;
@@ -23,11 +24,12 @@
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Iterator;
-import java.util.List;
 import java.util.Map;
 
+import org.apache.commons.jexl.parser.SimpleNode;
 import org.apache.commons.logging.Log;
 
+import org.apache.commons.jexl.parser.JexlNode;
 import org.apache.commons.jexl.parser.ASTAddNode;
 import org.apache.commons.jexl.parser.ASTAndNode;
 import org.apache.commons.jexl.parser.ASTArrayAccess;
@@ -37,6 +39,7 @@
 import org.apache.commons.jexl.parser.ASTBitwiseOrNode;
 import org.apache.commons.jexl.parser.ASTBitwiseXorNode;
 import org.apache.commons.jexl.parser.ASTBlock;
+import org.apache.commons.jexl.parser.ASTConstructorNode;
 import org.apache.commons.jexl.parser.ASTDivNode;
 import org.apache.commons.jexl.parser.ASTEQNode;
 import org.apache.commons.jexl.parser.ASTEmptyFunction;
@@ -75,10 +78,10 @@
 import org.apache.commons.jexl.parser.ASTUnaryMinusNode;
 import org.apache.commons.jexl.parser.ASTWhileStatement;
 import org.apache.commons.jexl.parser.Node;
-import org.apache.commons.jexl.parser.SimpleNode;
 import org.apache.commons.jexl.parser.ParserVisitor;
 
-import org.apache.commons.jexl.util.introspection.Info;
+import org.apache.commons.jexl.util.AbstractExecutor;
+import org.apache.commons.jexl.util.introspection.DebugInfo;
 import org.apache.commons.jexl.util.introspection.Uberspect;
 import org.apache.commons.jexl.util.introspection.VelMethod;
 import org.apache.commons.jexl.util.introspection.VelPropertyGet;
@@ -95,7 +98,7 @@
     /** The uberspect. */
     protected final Uberspect uberspect;
     /** The arithmetic handler. */
-    protected final Arithmetic arithmetic;
+    protected final JexlArithmetic arithmetic;
     /** The map of registered functions. */
     protected final Map<String, Object> functions;
     /** The context to store/retrieve variables. */
@@ -104,10 +107,10 @@
     protected final boolean strict;
     /** Silent intepreter flag. */
     protected boolean silent;
+    /** Cache executors. */
+    protected final boolean  cache;
     /** Registers made of 2 pairs of {register-name, value}. */
     protected Object[] registers = null;
-    /** Dummy velocity info. */
-    protected static final Info DUMMY = new Info("", 1, 1);
     /** Empty parameters for method matching. */
     protected static final Object[] EMPTY_PARAMS = new Object[0];
 
@@ -123,6 +126,7 @@
         this.functions = jexl.functions;
         this.strict = !this.arithmetic.isLenient();
         this.silent = jexl.silent;
+        this.cache = jexl.cache != null;
         this.context = aContext;
     }
 
@@ -152,7 +156,7 @@
      * @return the result of the interpretation.
      * @throws JexlException if any error occurs during interpretation.
      */
-    public Object interpret(SimpleNode node) {
+    public Object interpret(JexlNode node) {
         try {
             return node.jjtAccept(this, null);
         } catch (JexlException xjexl) {
@@ -188,7 +192,7 @@
      * @param right the right argument
      * @return the left, right or parent node
      */
-    protected Node findNullOperand(RuntimeException xrt, Node node, Object left, Object right) {
+    protected JexlNode findNullOperand(RuntimeException xrt, JexlNode node, Object left, Object right) {
         if (xrt instanceof NullPointerException
                 && JexlException.NULL_OPERAND == xrt.getMessage()) {
             if (left == null) {
@@ -202,16 +206,6 @@
     }
 
     /** {@inheritDoc} */
-    public Object visit(SimpleNode node, Object data) {
-        int numChildren = node.jjtGetNumChildren();
-        Object result = null;
-        for (int i = 0; i < numChildren; i++) {
-            result = node.jjtGetChild(i).jjtAccept(this, data);
-        }
-        return result;
-    }
-
-    /** {@inheritDoc} */
     public Object visit(ASTAddNode node, Object data) {
         /**
          * The pattern for exception mgmt is to let the child*.jjtAccept
@@ -225,7 +219,7 @@
         try {
             return arithmetic.add(left, right);
         } catch (RuntimeException xrt) {
-            Node xnode = findNullOperand(xrt, node, left, right);
+            JexlNode xnode = findNullOperand(xrt, node, left, right);
             throw new JexlException(xnode, "add error", xrt);
         }
     }
@@ -261,7 +255,7 @@
         // reference
         int numChildren = node.jjtGetNumChildren();
         for (int i = 1; i < numChildren; i++) {
-            Node nindex = node.jjtGetChild(i);
+            JexlNode nindex = node.jjtGetChild(i);
             Object index = nindex.jjtAccept(this, null);
             object = getAttribute(object, index, nindex);
         }
@@ -272,7 +266,7 @@
     /** {@inheritDoc} */
     public Object visit(ASTAssignment node, Object data) {
         // left contains the reference to assign to
-        Node left = node.jjtGetChild(0);
+        JexlNode left = node.jjtGetChild(0);
         if (!(left instanceof ASTReference)) {
             throw new JexlException(left, "illegal assignment form");
         }
@@ -280,9 +274,9 @@
         Object right = node.jjtGetChild(1).jjtAccept(this, data);
 
         // determine initial object & property:
-        Node objectNode = null;
+        JexlNode objectNode = null;
         Object object = null;
-        Node propertyNode = null;
+        JexlNode propertyNode = null;
         Object property = null;
         boolean isVariable = true;
         StringBuilder variableName = null;
@@ -327,7 +321,7 @@
                     variableName.append(property);
                     property = variableName.toString();
                 }
-                context.getVars().put(property, right);
+                context.getVars().put(String.valueOf(property), right);
                 return right;
             }
         } else if (propertyNode instanceof ASTArrayAccess) {
@@ -375,7 +369,7 @@
             long l = arithmetic.toLong(left);
             n = 1;
             long r = arithmetic.toLong(right);
-            return new Long(l & r);
+            return l & r;
         } catch (RuntimeException xrt) {
             throw new JexlException(node.jjtGetChild(n), "long coercion error", xrt);
         }
@@ -386,7 +380,7 @@
         Object left = node.jjtGetChild(0).jjtAccept(this, data);
         try {
             long l = arithmetic.toLong(left);
-            return new Long(~l);
+            return ~l;
         } catch (RuntimeException xrt) {
             throw new JexlException(node.jjtGetChild(0), "long coercion error", xrt);
         }
@@ -402,7 +396,7 @@
             long l = arithmetic.toLong(left);
             n = 1;
             long r = arithmetic.toLong(right);
-            return new Long(l | r);
+            return l | r;
         } catch (RuntimeException xrt) {
             throw new JexlException(node.jjtGetChild(n), "long coercion error", xrt);
         }
@@ -418,7 +412,7 @@
             long l = arithmetic.toLong(left);
             n = 1;
             long r = arithmetic.toLong(right);
-            return new Long(l ^ r);
+            return l ^ r;
         } catch (RuntimeException xrt) {
             throw new JexlException(node.jjtGetChild(n), "long coercion error", xrt);
         }
@@ -444,7 +438,7 @@
             if (!strict && xrt instanceof ArithmeticException) {
                 return 0.0;
             }
-            Node xnode = findNullOperand(xrt, node, left, right);
+            JexlNode xnode = findNullOperand(xrt, node, left, right);
             throw new JexlException(xnode, "divide error", xrt);
         }
     }
@@ -494,14 +488,17 @@
 
     /** {@inheritDoc} */
     public Object visit(ASTFalseNode node, Object data) {
-
         return Boolean.FALSE;
     }
 
     /** {@inheritDoc} */
     public Object visit(ASTFloatLiteral node, Object data) {
-
-        return Float.valueOf(node.image);
+        Float value = (Float) node.jjtGetValue();
+        if (value == null) {
+            value = Float.valueOf(node.image);
+            node.jjtSetValue(value);
+        }
+        return value;
     }
 
     /** {@inheritDoc} */
@@ -515,10 +512,10 @@
         // make sure there is a value to iterate on and a statement to execute
         if (iterableValue != null && node.jjtGetNumChildren() >= 3) {
             /* third objectNode is the statement to execute */
-            SimpleNode statement = (SimpleNode) node.jjtGetChild(2);
+            JexlNode statement = (JexlNode) node.jjtGetChild(2);
             // get an iterator for the collection/array etc via the
             // introspector.
-            Iterator<?> itemsIterator = getUberspect().getIterator(iterableValue, DUMMY);
+            Iterator<?> itemsIterator = getUberspect().getIterator(iterableValue, node);
             while (itemsIterator.hasNext()) {
                 // set loopVariable to value of iterator
                 Object value = itemsIterator.next();
@@ -611,8 +608,16 @@
 
     /** {@inheritDoc} */
     public Object visit(ASTIntegerLiteral node, Object data) {
-        Integer value = Integer.valueOf(node.image);
-        return (data == null) ? value : getAttribute(data, value);
+        if (data != null) {
+            Integer value = Integer.valueOf(node.image);
+            return getAttribute(data, value, node);
+        }
+        Integer value = (Integer) node.jjtGetValue();
+        if (value == null) {
+            value = Integer.valueOf(node.image);
+            node.jjtSetValue(value);
+        }
+        return value;
     }
 
     /** {@inheritDoc} */
@@ -620,7 +625,7 @@
         int numChildren = node.jjtGetNumChildren();
         Object result = null;
         for (int i = 0; i < numChildren; i++) {
-            Node child = node.jjtGetChild(i);
+            JexlNode child = node.jjtGetChild(i);
             result = child.jjtAccept(this, data);
         }
         return result;
@@ -668,6 +673,19 @@
         return map;
     }
 
+    /**
+     * Replace all numbers in an arguments array with the smallest type that will fit.
+     * @param args the argument array
+     */
+    protected final void narrowArguments(Object[] args) {
+        for (int a = 0; a < args.length; a++) {
+            Object arg = args[a];
+            if (arg instanceof Number) {
+                args[a] = arithmetic.narrow((Number) arg);
+            }
+        }
+    }
+
     /** {@inheritDoc} */
     public Object visit(ASTMethodNode node, Object data) {
         // the object to invoke the method on should be in the data argument
@@ -686,34 +704,42 @@
         // objectNode 0 is the identifier (method name), the others are parameters.
         String methodName = ((ASTIdentifier) node.jjtGetChild(0)).image;
 
-        // get our params
-        int paramCount = node.jjtGetNumChildren() - 1;
-        Object[] params = new Object[paramCount];
-        for (int i = 0; i < paramCount; i++) {
-            params[i] = node.jjtGetChild(i + 1).jjtAccept(this, null);
+        // get our arguments
+        int argc = node.jjtGetNumChildren() - 1;
+        Object[] argv = new Object[argc];
+        for (int i = 0; i < argc; i++) {
+            argv[i] = node.jjtGetChild(i + 1).jjtAccept(this, null);
         }
 
         JexlException xjexl = null;
         try {
-            VelMethod vm = getUberspect().getMethod(data, methodName, params, DUMMY);
-            // DG: If we can't find an exact match, narrow the parameters and
-            // try again!
-            if (vm == null) {
-
-                // replace all numbers with the smallest type that will fit
-                for (int i = 0; i < params.length; i++) {
-                    Object param = params[i];
-                    if (param instanceof Number) {
-                        params[i] = arithmetic.narrow((Number) param);
+            // attempt to reuse last executor cached in volatile JexlNode.value
+            if (node != null && cache) {
+                Object cached = node.jjtGetValue();
+                if (cached instanceof AbstractExecutor.Method) {
+                    AbstractExecutor.Method me = (AbstractExecutor.Method) cached;
+                    Object eval = me.tryExecute(methodName, data, argv);
+                    if (eval != AbstractExecutor.TRY_FAILED) {
+                        return eval;
                     }
                 }
-                vm = getUberspect().getMethod(data, methodName, params, DUMMY);
+            }
+            VelMethod vm = uberspect.getMethod(data, methodName, argv, node);
+            // DG: If we can't find an exact match, narrow the parameters and try again!
+            if (vm == null) {
+                narrowArguments(argv);
+                vm = uberspect.getMethod(data, methodName, argv, node);
                 if (vm == null) {
-                    xjexl = new JexlException(node, "unknown method", null);
+                    xjexl = new JexlException(node, "unknown or ambiguous method", null);
                 }
             }
             if (xjexl == null) {
-                return vm.invoke(data, params);
+                Object eval = vm.invoke(data, argv);
+                // cache executor in volatile JexlNode.value
+                if (node != null && cache) {
+                    node.jjtSetValue(vm);
+                }
+                return eval;
             }
         } catch (InvocationTargetException e) {
             Throwable t = e.getTargetException();
@@ -734,44 +760,99 @@
     }
 
     /** {@inheritDoc} */
+    public Object visit(ASTConstructorNode node, Object data) {
+        // first child is class or class name
+        Object cobject = node.jjtGetChild(0).jjtAccept(this, data);
+        // get the ctor args
+        int argc = node.jjtGetNumChildren() - 1;
+        Object[] argv = new Object[argc];
+        for (int i = 0; i < argc; i++) {
+            argv[i] = node.jjtGetChild(i + 1).jjtAccept(this, null);
+        }
+
+        JexlException xjexl = null;
+        try {
+            Constructor<?> ctor = uberspect.getConstructor(cobject, argv, node);
+            // DG: If we can't find an exact match, narrow the parameters and
+            // try again!
+            if (ctor == null) {
+                // replace all numbers with the smallest type that will fit
+                narrowArguments(argv);
+                ctor = uberspect.getConstructor(cobject, argv, node);
+                if (ctor == null) {
+                    xjexl = new JexlException(node, "unknown constructor", null);
+                }
+            }
+            if (xjexl == null) {
+                return ctor.newInstance(argv);
+            }
+        } catch (InvocationTargetException e) {
+            Throwable t = e.getTargetException();
+            if (!(t instanceof Exception)) {
+                t = e;
+            }
+            xjexl = new JexlException(node, "constructor invocation error", t);
+        } catch (Exception e) {
+            xjexl = new JexlException(node, "constructor error", e);
+        }
+        if (xjexl != null) {
+            if (strict) {
+                throw xjexl;
+            }
+            logger.warn(xjexl.getMessage(), xjexl.getCause());
+        }
+        return null;
+    }
+
+    /** {@inheritDoc} */
     public Object visit(ASTFunctionNode node, Object data) {
         // objectNode 0 is the prefix
         String prefix = ((ASTIdentifier) node.jjtGetChild(0)).image;
-        Object functor = functions.get(prefix);
-        if (functor == null) {
+        Object namespace = functions.get(prefix);
+        if (namespace == null) {
             throw new JexlException(node, "no such function namespace " + prefix);
         }
         // objectNode 1 is the identifier , the others are parameters.
-        String methodName = ((ASTIdentifier) node.jjtGetChild(1)).image;
+        String function = ((ASTIdentifier) node.jjtGetChild(1)).image;
 
-        // get our params
-        int paramCount = node.jjtGetNumChildren() - 2;
-        Object[] params = new Object[paramCount];
-        for (int i = 0; i < paramCount; i++) {
-            params[i] = node.jjtGetChild(i + 2).jjtAccept(this, null);
+        // get our args
+        int argc = node.jjtGetNumChildren() - 2;
+        Object[] argv = new Object[argc];
+        for (int i = 0; i < argc; i++) {
+            argv[i] = node.jjtGetChild(i + 2).jjtAccept(this, null);
         }
 
         JexlException xjexl = null;
         try {
-            VelMethod vm = getUberspect().getMethod(functor, methodName, params, DUMMY);
+            // attempt to reuse last executor cached in volatile JexlNode.value
+            if (node != null && cache) {
+                Object cached = node.jjtGetValue();
+                if (cached instanceof AbstractExecutor.Method) {
+                    AbstractExecutor.Method me = (AbstractExecutor.Method) cached;
+                    Object eval = me.tryExecute(function, namespace, argv);
+                    if (eval != AbstractExecutor.TRY_FAILED) {
+                        return eval;
+                    }
+                }
+            }
+            VelMethod vm = uberspect.getMethod(namespace, function, argv, node);
             // DG: If we can't find an exact match, narrow the parameters and
             // try again!
             if (vm == null) {
-
                 // replace all numbers with the smallest type that will fit
-                for (int i = 0; i < params.length; i++) {
-                    Object param = params[i];
-                    if (param instanceof Number) {
-                        params[i] = arithmetic.narrow((Number) param);
-                    }
-                }
-                vm = getUberspect().getMethod(functor, methodName, params, DUMMY);
+                narrowArguments(argv);
+                vm = uberspect.getMethod(namespace, function, argv, node);
                 if (vm == null) {
                     xjexl = new JexlException(node, "unknown function", null);
                 }
             }
             if (xjexl == null) {
-                return vm.invoke(functor, params);
+                Object eval = vm.invoke(namespace, argv);
+                // cache executor in volatile JexlNode.value
+                if (node != null && cache) {
+                    node.jjtSetValue(vm);
+                }
+                return eval;
             }
         } catch (InvocationTargetException e) {
             Throwable t = e.getTargetException();
@@ -801,7 +882,7 @@
             if (!strict && xrt instanceof ArithmeticException) {
                 return 0.0;
             }
-            Node xnode = findNullOperand(xrt, node, left, right);
+            JexlNode xnode = findNullOperand(xrt, node, left, right);
             throw new JexlException(xnode, "% error", xrt);
         }
     }
@@ -813,7 +894,7 @@
         try {
             return arithmetic.multiply(left, right);
         } catch (RuntimeException xrt) {
-            Node xnode = findNullOperand(xrt, node, left, right);
+            JexlNode xnode = findNullOperand(xrt, node, left, right);
             throw new JexlException(xnode, "* error", xrt);
         }
     }
@@ -825,7 +906,7 @@
         try {
             return arithmetic.equals(left, right) ? Boolean.FALSE : Boolean.TRUE;
         } catch (RuntimeException xrt) {
-            Node xnode = findNullOperand(xrt, node, left, right);
+            JexlNode xnode = findNullOperand(xrt, node, left, right);
             throw new JexlException(xnode, "!= error", xrt);
         }
     }
@@ -875,10 +956,10 @@
         // pass first piece of data in and loop through children
         Object result = null;
         StringBuilder variableName = null;
-        Map<?, ?> vars = context.getVars();
+        Map<String, ?> vars = context.getVars();
         boolean isVariable = true;
         for (int i = 0; i < numChildren; i++) {
-            Node theNode = node.jjtGetChild(i);
+            JexlNode theNode = node.jjtGetChild(i);
             isVariable &= (theNode instanceof ASTIdentifier);
             result = theNode.jjtAccept(this, result);
             // if we get null back a result, check for an ant variable
@@ -920,12 +1001,12 @@
             throw new JexlException(node, "size() : argument is null", null);
         }
 
-        return new Integer(sizeOf(node, val));
+        return sizeOf(node, val);
     }
 
     /** {@inheritDoc} */
     public Object visit(ASTSizeMethod node, Object data) {
-        return new Integer(sizeOf(node, data));
+        return sizeOf(node, data);
     }
 
     /** {@inheritDoc} */
@@ -945,7 +1026,7 @@
         try {
             return arithmetic.subtract(left, right);
         } catch (RuntimeException xrt) {
-            Node xnode = findNullOperand(xrt, node, left, right);
+            JexlNode xnode = findNullOperand(xrt, node, left, right);
             throw new JexlException(xnode, "- error", xrt);
         }
     }
@@ -974,7 +1055,7 @@
 
     /** {@inheritDoc} */
     public Object visit(ASTUnaryMinusNode node, Object data) {
-        Node valNode = node.jjtGetChild(0);
+        JexlNode valNode = node.jjtGetChild(0);
         Object val = valNode.jjtAccept(this, data);
         if (val instanceof Byte) {
             byte valueAsByte = ((Byte) val).byteValue();
@@ -984,16 +1065,16 @@
             return Short.valueOf((short) -valueAsShort);
         } else if (val instanceof Integer) {
             int valueAsInt = ((Integer) val).intValue();
-            return new Integer(-valueAsInt);
+            return Integer.valueOf(-valueAsInt);
         } else if (val instanceof Long) {
             long valueAsLong = ((Long) val).longValue();
-            return new Long(-valueAsLong);
+            return Long.valueOf(-valueAsLong);
         } else if (val instanceof Float) {
             float valueAsFloat = ((Float) val).floatValue();
-            return new Float(-valueAsFloat);
+            return Float.valueOf(-valueAsFloat);
         } else if (val instanceof Double) {
             double valueAsDouble = ((Double) val).doubleValue();
-            return new Double(-valueAsDouble);
+            return Double.valueOf(-valueAsDouble);
         } else if (val instanceof BigDecimal) {
             BigDecimal valueAsBigD = (BigDecimal) val;
             return valueAsBigD.negate();
@@ -1024,7 +1105,7 @@
      * @param val the object to get the size of.
      * @return the size of val
      */
-    private int sizeOf(Node node, Object val) {
+    private int sizeOf(JexlNode node, Object val) {
         if (val instanceof Collection<?>) {
             return ((Collection<?>) val).size();
         } else if (val.getClass().isArray()) {
@@ -1037,7 +1118,7 @@
             // check if there is a size method on the object that returns an
             // integer and if so, just use it
             Object[] params = new Object[0];
-            VelMethod vm = uberspect.getMethod(val, "size", EMPTY_PARAMS, DUMMY);
+            VelMethod vm = uberspect.getMethod(val, "size", EMPTY_PARAMS, (DebugInfo) node);
             if (vm != null && vm.getReturnType() == Integer.TYPE) {
                 Integer result;
                 try {
@@ -1072,42 +1153,30 @@
      * @param node the node that evaluated as the object
      * @return the attribute value
      */
-    protected Object getAttribute(Object object, Object attribute, Node node) {
+    protected Object getAttribute(Object object, Object attribute, JexlNode node) {
         if (object == null) {
             throw new JexlException(node, "object is null");
         }
-        // maps do accept null keys; check attribute null status after trying
-        if (object instanceof Map<?, ?>) {
-            try {
-                return ((Map<Object, Object>) object).get(attribute);
-            } catch (RuntimeException xrt) {
-                throw node == null ? xrt : new JexlException(node, "get map element error", xrt);
-            }
-        }
-        if (attribute == null) {
-            throw new JexlException(node, "object property is null");
-        }
-        if (object instanceof List<?>) {
-            try {
-                int idx = arithmetic.toInteger(attribute);
-                return ((List<?>) object).get(idx);
-            } catch (RuntimeException xrt) {
-                throw node == null ? xrt : new JexlException(node, "get list element error", xrt);
-            }
-        }
-        if (object.getClass().isArray()) {
-            try {
-                int idx = arithmetic.toInteger(attribute);
-                return Array.get(object, idx);
-            } catch (RuntimeException xrt) {
-                throw node == null ? xrt : new JexlException(node, "get array element error", xrt);
+        // attempt to reuse last executor cached in volatile JexlNode.value
+        if (node != null && cache) {
+            Object cached = node.jjtGetValue();
+            if (cached instanceof AbstractExecutor.Get) {
+                AbstractExecutor.Get vg = (AbstractExecutor.Get) cached;
+                Object value = vg.tryExecute(object, attribute);
+                if (value != AbstractExecutor.TRY_FAILED) {
+                    return value;
+                }
             }
         }
-        // look up bean property of data and return
-        VelPropertyGet vg = getUberspect().getPropertyGet(object, attribute.toString(), DUMMY);
+        VelPropertyGet vg = uberspect.getPropertyGet(object, attribute.toString(), node);
         if (vg != null) {
             try {
-                return vg.invoke(object);
+                Object value = vg.invoke(object);
+                // cache executor in volatile JexlNode.value
+                if (node != null && cache) {
+                    node.jjtSetValue(vg);
+                }
+                return value;
             } catch (Exception xany) {
                 if (node == null) {
                     throw new RuntimeException(xany);
@@ -1141,50 +1210,27 @@
      * @param value the value to assign to the object's attribute
      * @param node the node that evaluated as the object
      */
-    protected void setAttribute(Object object, Object attribute, Object value, Node node) {
-        if (object instanceof JexlContext) {
-            ((JexlContext) object).getVars().put(attribute, value);
-            return;
-        }
-
-        if (object instanceof Map<?, ?>) {
-            try {
-                ((Map<Object, Object>) object).put(attribute, value);
-                return;
-            } catch (RuntimeException xrt) {
-                throw node == null ? xrt : new JexlException(node, "set map element error", xrt);
-            }
-        }
-        if (object instanceof List<?>) {
-            try {
-                int idx = arithmetic.toInteger(attribute);
-                ((List<Object>) object).set(idx, value);
-                return;
-            } catch (RuntimeException xrt) {
-                throw node == null ? xrt : new JexlException(node, "set list element error", xrt);
-            }
-        }
-
-        if (object == null) {
-            throw new JexlException(node, "object is null");
-        }
-
-        if (object.getClass().isArray()) {
-            try {
-                int idx = arithmetic.toInteger(attribute);
-                Array.set(object, idx, value);
-                return;
-            } catch (RuntimeException xrt) {
-                throw node == null ? xrt : new JexlException(node, "set array element error", xrt);
+    protected void setAttribute(Object object, Object attribute, Object value, JexlNode node) {
+        String s = attribute.toString();
+        // attempt to reuse last executor cached in volatile JexlNode.value
+        if (node != null && cache) {
+            Object cached = node.jjtGetValue();
+            if (cached instanceof AbstractExecutor.Set) {
+                AbstractExecutor.Set setter = (AbstractExecutor.Set) cached;
+                Object eval = setter.tryExecute(object, attribute, value);
+                if (eval != AbstractExecutor.TRY_FAILED) {
+                    return;
+                }
             }
         }
-
-        // "Otherwise (a JavaBean object)..." huh? :)
-        String s = attribute.toString();
-        VelPropertySet vs = getUberspect().getPropertySet(object, s, value, DUMMY);
+        VelPropertySet vs = uberspect.getPropertySet(object, s, value, node);
         if (vs != null) {
             try {
+                // cache executor in volatile JexlNode.value
                 vs.invoke(object, value);
+                if (node != null && cache) {
+                    node.jjtSetValue(vs);
+                }
             } catch (RuntimeException xrt) {
                 throw node == null ? xrt : new JexlException(node, "set object property error", xrt);
             } catch (Exception xany) {
@@ -1196,10 +1242,22 @@
             }
             return;
         }
+        String error = "unable to set object property"
+                       + ", class: " + object.getClass().getName()
+                       + ", property: " + attribute;
         if (node == null) {
-            throw new UnsupportedOperationException("unable to set object property, "
-                            + "object:" + object + ", property: " + attribute);
+            throw new UnsupportedOperationException(error);
         }
-        throw new JexlException(node, "unable to set bean property", null);
+        throw new JexlException(node, error, null);
+    }
+
+    /**
+     * Unused, satisfy PArserVisitor interface.
+     * @param node a node
+     * @param data the date
+     * @return does not return,
+     */
+    public Object visit(SimpleNode node, Object data) {
+        throw new UnsupportedOperationException("Not supported yet.");
     }
 }
\ No newline at end of file

Modified: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/JexlArithmetic.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/JexlArithmetic.java?rev=799779&r1=799778&r2=799779&view=diff
==============================================================================
--- commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/JexlArithmetic.java (original)
+++ commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/JexlArithmetic.java Sat Aug  1 00:36:51 2009
@@ -23,27 +23,39 @@
  * Perform arithmetic.
  * @since 2.0
  */
-class JexlArithmetic implements Arithmetic {
-    /** Whether this Arithmetic instance behaves in strict or lenient mode. */
+public class JexlArithmetic {
+    /** Integer.MAX_VALUE as BigDecimal. */
+    private static final BigDecimal BIGD_DOUBLE_MAX_VALUE = BigDecimal.valueOf(Double.MAX_VALUE);
+    /** Integer.MIN_VALUE as BigDecimal. */
+    private static final BigDecimal BIGD_DOUBLE_MIN_VALUE = BigDecimal.valueOf(-Double.MAX_VALUE);
+    /** Long.MAX_VALUE as BigInteger. */
+    private static final BigInteger BIGI_LONG_MAX_VALUE = BigInteger.valueOf(Long.MAX_VALUE);
+    /** Long.MIN_VALUE as BigInteger. */
+    private static final BigInteger BIGI_LONG_MIN_VALUE = BigInteger.valueOf(Long.MIN_VALUE);
+    /** Whether this JexlArithmetic instance behaves in strict or lenient mode. */
     protected boolean strict;
 
     /**
      * Creates a JexlArithmetic.
      * @param lenient whether this arithmetic is lenient or strict
      */
-    JexlArithmetic(boolean lenient) {
+    public JexlArithmetic(boolean lenient) {
         this.strict = !lenient;
     }
 
     /**
-     * {@inheritDoc}
+     * Sets whether this JexlArithmetic instance triggers errors during evaluation when
+     * null is used as an operand.
+     * @param lenient true means no JexlException will occur, false allows them
      */
     public void setLenient(boolean lenient) {
         this.strict = !lenient;
     }
 
     /**
-     * {@inheritDoc}
+     * Checks whether this JexlArithmetic instance triggers errors during evaluation
+     * when null is used as an operand.
+     * @return true if lenient, false if strict
      */
     public boolean isLenient() {
         return !this.strict;
@@ -89,7 +101,7 @@
             if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
                 double l = toDouble(left);
                 double r = toDouble(right);
-                return new Double(l + r);
+                return l + r;
             }
         
             // if both are bigintegers use that type
@@ -110,9 +122,9 @@
             BigInteger l = toBigInteger(left);
             BigInteger r = toBigInteger(right);
             BigInteger result = l.add(r);
-            BigInteger maxLong = BigInteger.valueOf(Long.MAX_VALUE); 
-            if (result.compareTo(maxLong) <= 0) {
-                return new Long(result.longValue());
+            if (result.compareTo(BIGI_LONG_MAX_VALUE) <= 0
+                && result.compareTo(BIGI_LONG_MIN_VALUE) >= 0) {
+                return result.longValue();
             }
             return result;
         } catch (java.lang.NumberFormatException nfe) {
@@ -204,9 +216,9 @@
         BigInteger l = toBigInteger(left);
         BigInteger r = toBigInteger(right);
         BigInteger result = l.mod(r);
-        BigInteger maxLong = BigInteger.valueOf(Long.MAX_VALUE); 
-        if (result.compareTo(maxLong) <= 0) {
-            return new Long(result.longValue());
+        if (result.compareTo(BIGI_LONG_MAX_VALUE) <= 0
+            && result.compareTo(BIGI_LONG_MIN_VALUE) >= 0) {
+            return result.longValue();
         }
         return result;
     }
@@ -233,7 +245,7 @@
         if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
             double l = toDouble(left);
             double r = toDouble(right);
-            return new Double(l * r);
+            return l * r;
         }
         
         // if both are bigintegers use that type
@@ -254,9 +266,9 @@
         BigInteger l = toBigInteger(left);
         BigInteger r = toBigInteger(right);
         BigInteger result = l.multiply(r);
-        BigInteger maxLong = BigInteger.valueOf(Long.MAX_VALUE); 
-        if (result.compareTo(maxLong) <= 0) {
-            return new Long(result.longValue());
+        if (result.compareTo(BIGI_LONG_MAX_VALUE) <= 0
+            && result.compareTo(BIGI_LONG_MIN_VALUE) >= 0) {
+            return result.longValue();
         }
         return result;
     }
@@ -283,7 +295,7 @@
         if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
             double l = toDouble(left);
             double r = toDouble(right);
-            return new Double(l - r);
+            return l - r;
         }
         
         // if both are bigintegers use that type
@@ -304,9 +316,9 @@
         BigInteger l = toBigInteger(left);
         BigInteger r = toBigInteger(right);
         BigInteger result = l.subtract(r);
-        BigInteger maxLong = BigInteger.valueOf(Long.MAX_VALUE); 
-        if (result.compareTo(maxLong) <= 0) {
-            return new Long(result.longValue());
+        if (result.compareTo(BIGI_LONG_MAX_VALUE) <= 0
+            && result.compareTo(BIGI_LONG_MIN_VALUE) >= 0) {
+            return result.longValue();
         }
         return result;
     }
@@ -677,21 +689,21 @@
         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) {
+            if (bigd.compareTo(BIGD_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());
+                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(new BigInteger(String.valueOf(Long.MAX_VALUE))) > 0) {
+                if (bigi.compareTo(BIGI_LONG_MAX_VALUE) > 0) {
                     return original;
                 }
             }
@@ -702,7 +714,7 @@
             } else if (value <= Short.MAX_VALUE && value >= Short.MIN_VALUE) {
                 result = Short.valueOf((short) value);
             } else if (value <= Integer.MAX_VALUE && value >= Integer.MIN_VALUE) {
-                result = new Integer((int) value);
+                result = Integer.valueOf((int) value);
             }
             // else it fits in a long
         }

Modified: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/JexlContext.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/JexlContext.java?rev=799779&r1=799778&r2=799779&view=diff
==============================================================================
--- commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/JexlContext.java (original)
+++ commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/JexlContext.java Sat Aug  1 00:36:51 2009
@@ -35,7 +35,7 @@
      * @param vars Contents of vars will be replaced with the content 
      *      of this Map
      */
-    void setVars(Map vars);
+    void setVars(Map<String,Object> vars);
     
     /**
      * Retrives the Map of variables associated with this JexlContext.  The
@@ -44,5 +44,5 @@
      * 
      * @return A reference to the variable Map associated with this JexlContext.
      */
-    Map getVars();
+    Map<String,Object> getVars();
 }

Modified: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/JexlEngine.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/JexlEngine.java?rev=799779&r1=799778&r2=799779&view=diff
==============================================================================
--- commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/JexlEngine.java (original)
+++ commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/JexlEngine.java Sat Aug  1 00:36:51 2009
@@ -32,11 +32,12 @@
 
 import org.apache.commons.jexl.parser.ParseException;
 import org.apache.commons.jexl.parser.Parser;
-import org.apache.commons.jexl.parser.SimpleNode;
+import org.apache.commons.jexl.parser.JexlNode;
 import org.apache.commons.jexl.parser.TokenMgrError;
 import org.apache.commons.jexl.parser.ASTJexlScript;
 import org.apache.commons.jexl.util.Introspector;
 import org.apache.commons.jexl.util.introspection.Uberspect;
+import org.apache.commons.jexl.util.introspection.Info;
 
 /**
  * <p>
@@ -44,7 +45,7 @@
  * Determines the behavior of Expressions & Scripts during their evaluation with respect to:
  * <ul>
  *  <li>Introspection, see {@link Uberspect}</li>
- *  <li>Arithmetic & comparison, see {@link Arithmetic}</li>
+ *  <li>Arithmetic & comparison, see {@link JexlArithmetic}</li>
  *  <li>Error reporting</li>
  *  <li>Logging</li>
  * </ul>
@@ -87,9 +88,9 @@
      */
     protected final Uberspect uberspect;
     /**
-     * The Arithmetic instance.
+     * The JexlArithmetic instance.
      */
-    protected final Arithmetic arithmetic;
+    protected final JexlArithmetic arithmetic;
     /**
      * The Log to which all JexlEngine messages will be logged.
      */
@@ -106,13 +107,17 @@
      */
     protected boolean silent = false;
     /**
+     * Wheter error messages will carry debugging information.
+     */
+    protected boolean debug = true;
+    /**
      *  The map of 'prefix:function' to object implementing the function.
      */
     protected Map<String, Object> functions = Collections.EMPTY_MAP;
     /**
      * The expression cache.
      */
-    protected Map<String, SimpleNode> cache = null;
+    protected Map<String, JexlNode> cache = null;
     /**
      * An empty/static/non-mutable JexlContext used instead of null context.
      */
@@ -139,18 +144,14 @@
     }
 
     /**
-     * Creates a JEXL engine using the provided {@link Uberspect}, (@link Arithmetic) and logger.
+     * Creates a JEXL engine using the provided {@link Uberspect}, (@link JexlArithmetic),
+     * a function map and logger.
      * @param anUberspect to allow different introspection behaviour
      * @param anArithmetic to allow different arithmetic behaviour
-     * @param theFunctions an optional map of functions (@see setFunctions)
+     * @param theFunctions an optional map of functions (@link setFunctions)
      * @param log the logger for various messages
      */
-    public JexlEngine(Uberspect anUberspect, Arithmetic anArithmetic, Map<String, Object> theFunctions, Log log) {
-        this.uberspect = anUberspect == null ? Introspector.getUberspect() : anUberspect;
-        this.arithmetic = anArithmetic == null ? new JexlArithmetic(true) : anArithmetic;
-        if (theFunctions != null) {
-            this.functions = theFunctions;
-        }
+    public JexlEngine(Uberspect anUberspect, JexlArithmetic anArithmetic, Map<String, Object> theFunctions, Log log) {
         if (log == null) {
             log = LogFactory.getLog(JexlEngine.class);
         }
@@ -158,6 +159,27 @@
             throw new NullPointerException("logger can not be null");
         }
         this.logger = log;
+        this.uberspect = anUberspect == null ? Introspector.getUberspect(log) : anUberspect;
+        this.arithmetic = anArithmetic == null ? new JexlArithmetic(true) : anArithmetic;
+        if (theFunctions != null) {
+            this.functions = theFunctions;
+        }
+    }
+
+    /**
+     * Sets whether this engine reports debugging information when error occurs.
+     * @param flag true implies debug is on, false implies debug is off.
+     */
+    public void setDebug(boolean flag) {
+        this.debug = flag;
+    }
+
+    /**
+     * Checks whether this engine is in debug mode.
+     * @return true if debug is on, false otherwise
+     */
+    public boolean isDebug() {
+        return this.debug;
     }
 
     /**
@@ -258,14 +280,30 @@
      *      expression or a reference.
      */
     public Expression createExpression(String expression)
+            throws ParseException  {
+        return createExpression(expression, null);
+    }
+
+    /**
+     * Creates an Expression from a String containing valid
+     * JEXL syntax.  This method parses the expression which
+     * must contain either a reference or an expression.
+     * @param expression A String containing valid JEXL syntax
+     * @return An Expression object which can be evaluated with a JexlContext
+     * @param info An info structure to carry debugging information if needed
+     * @throws ParseException An exception can be thrown if there is a problem
+     *      parsing this expression, or if the expression is neither an
+     *      expression or a reference.
+     */
+    public Expression createExpression(String expression, Info info)
             throws ParseException {
         // Parse the expression
-        SimpleNode tree = parse(expression);
+        JexlNode tree = parse(expression, info);
         if (tree.jjtGetNumChildren() > 1) {
             logger.warn("The JEXL Expression created will be a reference"
                      + " to the first expression from the supplied script: \"" + expression + "\" ");
         }
-        SimpleNode node = (SimpleNode) tree.jjtGetChild(0);
+        JexlNode node = tree.jjtGetChild(0);
         return new ExpressionImpl(this, expression, node);
     }
 
@@ -278,10 +316,23 @@
      * @throws ParseException if there is a problem parsing the script.
      */
     public Script createScript(String scriptText) throws ParseException {
+        return createScript(scriptText, null);
+    }
+
+    /**
+     * Creates a Script from a String containing valid JEXL syntax.
+     * This method parses the script which validates the syntax.
+     *
+     * @param scriptText A String containing valid JEXL syntax
+     * @param info An info structure to carry debugging information if needed
+     * @return A {@link Script} which can be executed using a {@link JexlContext}.
+     * @throws ParseException if there is a problem parsing the script.
+     */
+    public Script createScript(String scriptText, Info info) throws ParseException {
         if (scriptText == null) {
             throw new NullPointerException("scriptText is null");
         }
-        SimpleNode script = parse(scriptText);
+        JexlNode script = parse(scriptText, info);
         if (script instanceof ASTJexlScript) {
             return new ScriptImpl(this, scriptText, (ASTJexlScript) script);
         } else {
@@ -308,7 +359,11 @@
             throw new IOException("Can't read scriptFile (" + scriptFile.getCanonicalPath() + ")");
         }
         BufferedReader reader = new BufferedReader(new FileReader(scriptFile));
-        return createScript(readerToString(reader));
+        Info info = null;
+        if (debug) {
+            info = new Info(scriptFile.getName(), 0, 0);
+        }
+        return createScript(readerToString(reader), info);
 
     }
 
@@ -331,7 +386,11 @@
 
         BufferedReader reader = new BufferedReader(
                 new InputStreamReader(connection.getInputStream()));
-        return createScript(readerToString(reader));
+        Info info = null;
+        if (debug) {
+            info = new Info(scriptUrl.toString(), 0, 0);
+        }
+        return createScript(readerToString(reader), info);
     }
 
     /**
@@ -375,8 +434,8 @@
         }
         expr = r0 + (expr.charAt(0) == '[' ? "" : ".") + expr + ";";
         try {
-            SimpleNode tree = parse(expr);
-            SimpleNode node = (SimpleNode) tree.jjtGetChild(0).jjtGetChild(0);
+            JexlNode tree = parse(expr, null);
+            JexlNode node = tree.jjtGetChild(0).jjtGetChild(0);
             Interpreter interpreter = createInterpreter(context);
             // ensure 4 objects in register array
             Object[] r = {r0, bean, r0, bean};
@@ -442,8 +501,8 @@
         // synthetize expr
         expr = r0 + (expr.charAt(0) == '[' ? "" : ".") + expr + "=" + r1 + ";";
         try {
-            SimpleNode tree = parse(expr);
-            SimpleNode node = (SimpleNode) tree.jjtGetChild(0).jjtGetChild(0);
+            JexlNode tree = parse(expr, null);
+            JexlNode node = tree.jjtGetChild(0).jjtGetChild(0);
             Interpreter interpreter = createInterpreter(context);
             // set the registers
             Object[] r = {r0, bean, r1, value};
@@ -481,10 +540,10 @@
      * @param cacheSize the cache size, must be > 0
      * @return a Map usable as a cache bounded to the given size
      */
-    protected Map<String, SimpleNode> createCache(final int cacheSize) {
-        return new java.util.LinkedHashMap<String, SimpleNode>(cacheSize, LOAD_FACTOR, true) {
+    protected Map<String, JexlNode> createCache(final int cacheSize) {
+        return new java.util.LinkedHashMap<String, JexlNode>(cacheSize, LOAD_FACTOR, true) {
             @Override
-            protected boolean removeEldestEntry(Map.Entry<String, SimpleNode> eldest) {
+            protected boolean removeEldestEntry(Map.Entry<String, JexlNode> eldest) {
                 return size() > cacheSize;
             }
         };
@@ -493,12 +552,13 @@
     /**
      * Parses an expression.
      * @param expression the expression to parse
+     * @param info debug information structure
      * @return the parsed tree
      * @throws ParseException if any error occured during parsing
      */
-    protected SimpleNode parse(CharSequence expression) throws ParseException {
+    protected JexlNode parse(CharSequence expression, Info info) throws ParseException {
         String expr = cleanExpression(expression);
-        SimpleNode tree = null;
+        JexlNode tree = null;
         synchronized (parser) {
             logger.debug("Parsing expression: " + expression);
             if (cache != null) {
@@ -509,7 +569,29 @@
             }
             try {
                 Reader reader = expr.endsWith(";") ? new StringReader(expr) : new StringReader(expr + ";");
-                tree = parser.parse(reader);
+                // use first calling method of JexlEngine as debug info
+                if (info == null && debug) {
+                    Throwable xinfo = new Throwable();
+                    xinfo.fillInStackTrace();
+                    StackTraceElement[] stack = xinfo.getStackTrace();
+                    StackTraceElement se = null;
+                    Class<?> clazz = getClass();
+                    for(int s = 0; s < stack.length; ++s, se = null) {
+                        se = stack[s];
+                        if (!se.getClassName().equals(clazz.getName())) {
+                            // go deeper if called from UnifiedJEXL
+                            if (se.getClassName().equals(UnifiedJEXL.class.getName())) {
+                                clazz = UnifiedJEXL.class;
+                            } else {
+                                break;
+                            }
+                        }
+                    }
+                    if (se != null) {
+                        info = new Info(se.getClassName()+"."+se.getMethodName(), se.getLineNumber(), 0);
+                    }
+                }
+                tree = parser.parse(reader, info);
                 if (cache != null) {
                     cache.put(expr, tree);
                 }

Modified: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/JexlException.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/JexlException.java?rev=799779&r1=799778&r2=799779&view=diff
==============================================================================
--- commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/JexlException.java (original)
+++ commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/JexlException.java Sat Aug  1 00:36:51 2009
@@ -16,14 +16,14 @@
  */
 package org.apache.commons.jexl;
 
-import org.apache.commons.jexl.parser.Node;
+import org.apache.commons.jexl.parser.JexlNode;
 
 /**
  * Wraps any error that might occur during interpretation of a script or expression.
  */
 public class JexlException extends RuntimeException {
     /** The point of origin for this exception. */
-    protected Node mark;
+    protected JexlNode mark;
     /** A marker to use in NPEs stating a null operand error. */
     public static final String NULL_OPERAND = "jexl.null";
     /**
@@ -31,7 +31,7 @@
      * @param node the node causing the error
      * @param msg the error message
      */
-    public JexlException(Node node, String msg) {
+    public JexlException(JexlNode node, String msg) {
         super(msg);
         mark = node;
     }
@@ -41,7 +41,7 @@
      * @param msg the error message
      * @param cause the exception causing the error
      */
-    public JexlException(Node node, String msg, Throwable cause) {
+    public JexlException(JexlNode node, String msg, Throwable cause) {
         super(msg, cause);
         mark = node;
     }
@@ -70,7 +70,8 @@
     
     /**
      * Detailed info message about this error.
-     * Format is "@[begin,end]: string \n msg" where:
+     * Format is "debug![begin,end]: string \n msg" where:
+     * - debug is the debugging information if it exists (@link JexlEngine.setDebug)
      * - begin, end are character offsets in the string for the precise location of the error
      * - string is the string representation of the offending expression
      * - msg is the actual explanation message for this error
@@ -81,7 +82,8 @@
         Debugger dbg = new Debugger();
         StringBuilder msg = new StringBuilder();
         if (dbg.debug(mark)) {
-            msg.append("@[");
+            msg.append(mark.debugString());
+            msg.append("![");
             msg.append(dbg.start());
             msg.append(",");
             msg.append(dbg.end());

Modified: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/JexlHelper.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/JexlHelper.java?rev=799779&r1=799778&r2=799779&view=diff
==============================================================================
--- commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/JexlHelper.java (original)
+++ commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/JexlHelper.java Sat Aug  1 00:36:51 2009
@@ -31,11 +31,11 @@
  */
 public class JexlHelper {
     /** singleton instance. */
-    protected static JexlHelper helper = new JexlHelper();
+    protected static final JexlHelper HELPER = new JexlHelper();
 
     /** @return the single instance. */
     protected static JexlHelper getInstance() {
-        return helper;
+        return HELPER;
     }
 
     /**

Modified: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/UnifiedJEXL.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/UnifiedJEXL.java?rev=799779&r1=799778&r2=799779&view=diff
==============================================================================
--- commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/UnifiedJEXL.java (original)
+++ commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/UnifiedJEXL.java Sat Aug  1 00:36:51 2009
@@ -19,9 +19,10 @@
 import java.util.Map;
 import java.util.LinkedHashMap;
 import java.util.ArrayList;
-import org.apache.commons.jexl.parser.SimpleNode;
+import org.apache.commons.jexl.parser.JexlNode;
 import org.apache.commons.jexl.parser.ParseException;
 import org.apache.commons.jexl.parser.StringParser;
+import org.apache.commons.jexl.util.introspection.Info;
 
 /**
  * An evaluator similar to the unified EL evaluator used in JSP/JSF based on JEXL.
@@ -213,7 +214,7 @@
     /**
      * The sole type of (runtime) exception the UnifiedJEXL can throw.
      */
-    public class Exception extends RuntimeException {
+    public static class Exception extends RuntimeException {
         /**
          * Creates a UnifiedJEXL.Exception.
          * @param msg the exception message
@@ -228,7 +229,7 @@
      * The abstract base class for all expressions, immediate '${...}' and deferred '#{...}'.
      */
     public abstract class Expression {
-        /** The source of this expression (see {@link Expression#prepare}) */
+        /** The source of this expression (see {@link Expression#prepare}). */
         protected final Expression source;
         /**
          * Creates an expression.
@@ -238,6 +239,11 @@
             this.source = src != null ? src : this;
         }
 
+        /**
+         * Formats this expression, adding its source string representation in
+         * comments if available: 'expression /*= source *\/'' .
+         * @return the formatted expression string
+         */
         @Override
         public String toString() {
             StringBuilder strb = new StringBuilder();
@@ -343,7 +349,7 @@
          * Intreprets a sub-expression.
          * @param interpreter a JEXL interpreter
          * @return the result of interpretation
-         * @throws ParseException (only for nested & composite)
+         * @throws org.apache.commons.jexl.parser.ParseException (only for nested & composite)
          */
         abstract Object evaluate(Interpreter interpreter) throws ParseException;
     }
@@ -373,6 +379,7 @@
             this.value = val;
         }
 
+        /** {@inheritDoc} */
         @Override
         public String asString() {
             StringBuilder strb = new StringBuilder();
@@ -382,11 +389,13 @@
             return strb.toString();
         }
 
+        /** {@inheritDoc} */
         @Override
         ExpressionType getType() {
             return ExpressionType.CONSTANT;
         }
 
+        /** {@inheritDoc} */
         @Override
         void asString(StringBuilder strb) {
             String str = value.toString();
@@ -403,21 +412,25 @@
             }
         }
 
+        /** {@inheritDoc} */
         @Override
         public Expression prepare(JexlContext context) {
             return this;
         }
 
+        /** {@inheritDoc} */
         @Override
         Expression prepare(Interpreter interpreter) throws ParseException {
             return this;
         }
 
+        /** {@inheritDoc} */
         @Override
         public Object evaluate(JexlContext context) {
             return value;
         }
 
+        /** {@inheritDoc} */
         @Override
         Object evaluate(Interpreter interpreter) throws ParseException {
             return value;
@@ -430,19 +443,20 @@
         /** The JEXL string for this expression. */
         protected final CharSequence expr;
         /** The JEXL node for this expression. */
-        protected final SimpleNode node;
+        protected final JexlNode node;
         /**
          * Creates a JEXL interpretable expression.
-         * @param expr the expression as a string
-         * @param node the expression as an AST
-         * @param source the source expression if any
+         * @param theExpr the expression as a string
+         * @param theNode the expression as an AST
+         * @param theSource the source expression if any
          */
-        protected JexlBasedExpression(CharSequence expr, SimpleNode node, Expression source) {
-            super(source);
-            this.expr = expr;
-            this.node = node;
+        protected JexlBasedExpression(CharSequence theExpr, JexlNode theNode, Expression theSource) {
+            super(theSource);
+            this.expr = theExpr;
+            this.node = theNode;
         }
 
+        /** {@inheritDoc} */
         @Override
         public String toString() {
             StringBuilder strb = new StringBuilder(expr.length() + 3);
@@ -460,6 +474,7 @@
             return strb.toString();
         }
 
+        /** {@inheritDoc} */
         @Override
         public void asString(StringBuilder strb) {
             strb.append(isImmediate() ? '$' : '#');
@@ -468,21 +483,25 @@
             strb.append("}");
         }
 
+        /** {@inheritDoc} */
         @Override
         public Expression prepare(JexlContext context) {
             return this;
         }
 
+        /** {@inheritDoc} */
         @Override
         Expression prepare(Interpreter interpreter) throws ParseException {
             return this;
         }
 
+        /** {@inheritDoc} */
         @Override
         public Object evaluate(JexlContext context) {
             return UnifiedJEXL.this.evaluate(context, this);
         }
 
+        /** {@inheritDoc} */
         @Override
         Object evaluate(Interpreter interpreter) throws ParseException {
             return interpreter.interpret(node);
@@ -498,15 +517,17 @@
          * @param node the expression as an AST
          * @param source the source expression if any
          */
-        ImmediateExpression(CharSequence expr, SimpleNode node, Expression source) {
+        ImmediateExpression(CharSequence expr, JexlNode node, Expression source) {
             super(expr, node, source);
         }
 
+        /** {@inheritDoc} */
         @Override
         ExpressionType getType() {
             return ExpressionType.IMMEDIATE;
         }
 
+        /** {@inheritDoc} */
         @Override
         public boolean isImmediate() {
             return true;
@@ -521,15 +542,17 @@
          * @param node the expression as an AST
          * @param source the source expression if any
          */
-        DeferredExpression(CharSequence expr, SimpleNode node, Expression source) {
+        DeferredExpression(CharSequence expr, JexlNode node, Expression source) {
             super(expr, node, source);
         }
 
+        /** {@inheritDoc} */
         @Override
         ExpressionType getType() {
             return ExpressionType.DEFERRED;
         }
 
+        /** {@inheritDoc} */
         @Override
         public boolean isImmediate() {
             return false;
@@ -548,35 +571,40 @@
          * @param node the expression as an AST
          * @param source the source expression if any
          */
-        NestedExpression(CharSequence expr, SimpleNode node, Expression source) {
+        NestedExpression(CharSequence expr, JexlNode node, Expression source) {
             super(expr, node, source);
             if (this.source != this) {
                 throw new IllegalArgumentException("Nested expression can not have a source");
             }
         }
 
+        /** {@inheritDoc} */
         @Override
         ExpressionType getType() {
             return ExpressionType.NESTED;
         }
 
+        /** {@inheritDoc} */
         @Override
         public String toString() {
             return expr.toString();
         }
 
+        /** {@inheritDoc} */
         @Override
         public Expression prepare(JexlContext context) {
             return UnifiedJEXL.this.prepare(context, this);
         }
 
+        /** {@inheritDoc} */
         @Override
         public Expression prepare(Interpreter interpreter) throws ParseException {
             String value = interpreter.interpret(node).toString();
-            SimpleNode dnode = toNode(value);
+            JexlNode dnode = toNode(value, jexl.isDebug()? node.getInfo() : null);
             return new DeferredExpression(value, dnode, this);
         }
 
+        /** {@inheritDoc} */
         @Override
         public Object evaluate(Interpreter interpreter) throws ParseException {
             return prepare(interpreter).evaluate(interpreter);
@@ -592,7 +620,7 @@
         private final Expression[] exprs;
         /**
          * Creates a composite expression.
-         * @param counter counters of expression per type
+         * @param counters counters of expression per type
          * @param list the sub-expressions
          * @param src the source for this expresion if any
          */
@@ -603,17 +631,20 @@
                       | (counters[ExpressionType.IMMEDIATE.index] > 0 ? 1 : 0);
         }
 
+        /** {@inheritDoc} */
         @Override
         ExpressionType getType() {
             return ExpressionType.COMPOSITE;
         }
 
+        /** {@inheritDoc} */
         @Override
         public boolean isImmediate() {
             // immediate if no deferred
             return (meta & 2) == 0;
         }
 
+        /** {@inheritDoc} */
         @Override
         void asString(StringBuilder strb) {
             for (Expression e : exprs) {
@@ -621,11 +652,13 @@
             }
         }
 
+        /** {@inheritDoc} */
         @Override
         public Expression prepare(JexlContext context) {
             return UnifiedJEXL.this.prepare(context, this);
         }
 
+        /** {@inheritDoc} */
         @Override
         Expression prepare(Interpreter interpreter) throws ParseException {
             // if this composite is not its own source, it is already prepared
@@ -658,11 +691,13 @@
             return ready;
         }
 
+        /** {@inheritDoc} */
         @Override
         public Object evaluate(JexlContext context) {
             return UnifiedJEXL.this.evaluate(context, this);
         }
 
+        /** {@inheritDoc} */
         @Override
         Object evaluate(Interpreter interpreter) throws ParseException {
             final int size = exprs.length;
@@ -783,8 +818,18 @@
      * @param expression the expression to parse
      * @return the AST
      */
-    private SimpleNode toNode(CharSequence expression) throws ParseException {
-        return (SimpleNode) jexl.parse(expression).jjtGetChild(0);
+    private JexlNode toNode(CharSequence expression) throws ParseException {
+        return jexl.parse(expression, null).jjtGetChild(0);
+    }
+    
+    /**
+     * Use the JEXL parser to create the AST for an expression.
+     * @param expression the expression to parse
+     * @param info debug information
+     * @return the AST
+     */
+    private JexlNode toNode(CharSequence expression, Info info) throws ParseException {
+        return jexl.parse(expression, info).jjtGetChild(0);
     }
 
     /**
@@ -832,7 +877,7 @@
      * Parses a unified expression.
      * @param expr the string expression
      * @return the expression instance
-     * @throws Exception
+     * @throws org.apache.commons.jexl.parser.ParseException if an error occur during parsing
      */
     private Expression parseExpression(String expr) throws ParseException {
         final int size = expr.length();

Modified: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/context/HashMapContext.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/context/HashMapContext.java?rev=799779&r1=799778&r2=799779&view=diff
==============================================================================
--- commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/context/HashMapContext.java (original)
+++ commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/context/HashMapContext.java Sat Aug  1 00:36:51 2009
@@ -29,13 +29,13 @@
  *  @author <a href="mailto:geirm@apache.org">Geir Magnusson Jr.</a>
  *  @version $Id$
  */
-public class HashMapContext extends HashMap implements JexlContext {
+public class HashMapContext extends HashMap<String,Object> implements JexlContext {
     /** serialization version id jdk13 generated. */
     static final long serialVersionUID = 5715964743204418854L;
     /**
      * {@inheritDoc}
      */
-    public void setVars(Map vars) {
+    public void setVars(Map<String,Object> vars) {
         clear();
         putAll(vars);
     }
@@ -43,7 +43,7 @@
     /**
      * {@inheritDoc}
      */
-    public Map getVars() {
+    public Map<String,Object> getVars() {
         return this;
     }
 }

Added: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/parser/JexlNode.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/parser/JexlNode.java?rev=799779&view=auto
==============================================================================
--- commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/parser/JexlNode.java (added)
+++ commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/parser/JexlNode.java Sat Aug  1 00:36:51 2009
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl.parser;
+
+import org.apache.commons.jexl.util.introspection.Info;
+import org.apache.commons.jexl.util.introspection.DebugInfo;
+
+/**
+ * Base class for parser nodes - holds an 'image' of the token for later use.
+ *
+ * @since 2.0
+ */
+public abstract class JexlNode extends SimpleNode implements DebugInfo {
+    /** token value. */
+    public String image;
+
+    public JexlNode(int id) {
+        super(id);
+    }
+
+    public JexlNode(Parser p, int id) {
+        super(p, id);
+    }
+
+    public Info getInfo() {
+        JexlNode node = this;
+        while (node != null) {
+            if (node.value instanceof Info) {
+                return (Info) node.value;
+            }
+            node = node.jjtGetParent();
+        }
+        return null;
+    }
+    
+    /** {@inheritDoc} */
+    public String debugString() {
+        Info info = getInfo();
+        return info != null? info.debugString() : "";
+    }
+}

Propchange: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/parser/JexlNode.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/parser/JexlNode.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Modified: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/parser/Parser.jjt
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/parser/Parser.jjt?rev=799779&r1=799778&r2=799779&view=diff
==============================================================================
--- commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/parser/Parser.jjt (original)
+++ commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/parser/Parser.jjt Sat Aug  1 00:36:51 2009
@@ -29,7 +29,7 @@
    MULTI=true;
    STATIC=false;
    VISITOR=true;
-   NODE_EXTENDS="JEXLNode";
+   NODE_CLASS="JexlNode";
    NODE_USES_PARSER=true;
    UNICODE_INPUT=true;
 }
@@ -39,22 +39,24 @@
 package org.apache.commons.jexl.parser;
 
 import java.io.Reader;
-import java.io.ByteArrayInputStream;
+import org.apache.commons.jexl.util.introspection.Info;
 
 public class Parser extends StringParser
 {
+    /** Base debug information. */
+    protected Info debug = null;
 
-    public SimpleNode parse(Reader reader)
+    public JexlNode parse(Reader reader, Info info)
         throws ParseException
     {
         ReInit(reader);
-
         /*
          *  lets do the 'Unique Init' in here to be
          *  safe - it's a pain to remember
          */
 
-        SimpleNode tree = JexlScript();
+        JexlNode tree = JexlScript();
+        tree.value = info;
         return tree;
     }
 }
@@ -85,7 +87,7 @@
  * Program structuring syntax follows.
  */
 
-SimpleNode JexlScript() :
+JexlNode JexlScript() :
 {
    String name;
 }
@@ -442,6 +444,12 @@
     LOOKAHEAD(Identifier() "(") Method()
 }
 
+void Constructor() # ConstructorNode() : {}
+{
+
+  "new" "("[ Parameter() ( "," Parameter() )* ] ")"
+}
+
 void SizeMethod() : {}
 {
     "size" "(" ")"
@@ -449,7 +457,8 @@
 
 void Reference() : {}
 {
-  (LOOKAHEAD(Identifier() "[" ( Expression() | IntegerLiteral() | Reference()) "]") ArrayAccess() |
+  (LOOKAHEAD("new") Constructor() |
+   LOOKAHEAD(Identifier() "[" ( Expression() | IntegerLiteral() | Reference()) "]") ArrayAccess() |
    LOOKAHEAD(Identifier() ":" Identifier() "(") Function() |
    LOOKAHEAD(Identifier() "(") Method() |
    Identifier() |

Added: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/parser/SimpleNode.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/parser/SimpleNode.java?rev=799779&view=auto
==============================================================================
--- commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/parser/SimpleNode.java (added)
+++ commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/parser/SimpleNode.java Sat Aug  1 00:36:51 2009
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.jexl.parser;
+
+/**
+ * A class originally generated by JJTree:
+ * /* JavaCCOptions:MULTI=true,NODE_USES_PARSER=true,VISITOR=true,TRACK_TOKENS=false,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY= *\/
+ * Worksaround issue https://javacc.dev.java.net/issues/show_bug.cgi?id=227
+ * As soon as this issue if fixed and the maven plugin uses the correct version of Javacc, this
+ * class can go away.
+ *
+ * The technical goal is to ensure every reference made in the parser was to a JexlNode; unfortunately,
+ * as in javacc 4.1, it still uses a SimpleNode reference in the generated ParserVisitor.
+ * Besides, there is no need to keep the parser around in the node.
+ *
+ * The functional goal is to a allow a <em>volatile</em> value in the node
+ * so it can serve as a last evaluation cache even in multi-threaded executions.
+ */
+public class SimpleNode implements Node {
+  protected JexlNode parent;
+  protected JexlNode[] children;
+  protected int id;
+  /** volatile value so it can be used as a last evaluation cache. */
+  protected volatile Object value;
+
+  public SimpleNode(int i) {
+    id = i;
+  }
+
+  public SimpleNode(Parser p, int i) {
+    this(i);
+  }
+
+  public void jjtOpen() {
+  }
+
+  public void jjtClose() {
+  }
+
+  public void jjtSetParent(Node n) {
+      parent = (JexlNode) n;
+  }
+  
+  public JexlNode jjtGetParent() {
+      return parent;
+  }
+
+  public void jjtAddChild(Node n, int i) {
+    if (children == null) {
+      children = new JexlNode[i + 1];
+    } else if (i >= children.length) {
+      JexlNode c[] = new JexlNode[i + 1];
+      System.arraycopy(children, 0, c, 0, children.length);
+      children = c;
+    }
+    children[i] = (JexlNode) n;
+  }
+
+  public JexlNode jjtGetChild(int i) {
+    return children[i];
+  }
+
+  public int jjtGetNumChildren() {
+    return (children == null) ? 0 : children.length;
+  }
+
+  public void jjtSetValue(Object value) {
+      this.value = value;
+  }
+
+  public Object jjtGetValue() {
+      return value;
+  }
+
+  /** Accept the visitor. **/
+  public Object jjtAccept(ParserVisitor visitor, Object data) {
+    return visitor.visit(this, data);
+  }
+
+  /** Accept the visitor. **/
+  public Object childrenAccept(ParserVisitor visitor, Object data) {
+    if (children != null) {
+      for (int i = 0; i < children.length; ++i) {
+        children[i].jjtAccept(visitor, data);
+      }
+    }
+    return data;
+  }
+
+  /* You can override these two methods in subclasses of SimpleNode to
+     customize the way the JexlNode appears when the tree is dumped.  If
+     your output uses more than one line you should override
+     toString(String), otherwise overriding toString() is probably all
+     you need to do. */
+
+  public String toString() { return ParserTreeConstants.jjtNodeName[id]; }
+  public String toString(String prefix) { return prefix + toString(); }
+
+  /* Override this method if you want to customize how the JexlNode dumps
+     out its children. */
+
+  public void dump(String prefix) {
+    System.out.println(toString(prefix));
+    if (children != null) {
+      for (int i = 0; i < children.length; ++i) {
+  SimpleNode n = (SimpleNode)children[i];
+  if (n != null) {
+    n.dump(prefix + " ");
+  }
+      }
+    }
+  }
+}
+
+/* JavaCC - OriginalChecksum=7dff880883d088a37c1e3197e4b455a0 (do not edit this line) */

Propchange: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/parser/SimpleNode.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/parser/SimpleNode.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Modified: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/parser/VisitorAdapter.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/parser/VisitorAdapter.java?rev=799779&r1=799778&r2=799779&view=diff
==============================================================================
--- commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/parser/VisitorAdapter.java (original)
+++ commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/parser/VisitorAdapter.java Sat Aug  1 00:36:51 2009
@@ -74,6 +74,12 @@
     }
 
     /** {@inheritDoc} */
+    public Object visit(ASTConstructorNode node, Object data) {
+        node.dump(" ");
+        return node.childrenAccept(this, data);
+    }
+
+    /** {@inheritDoc} */
     public Object visit(ASTBlock node, Object data) {
         node.dump(" ");
         return node.childrenAccept(this, data);
@@ -301,7 +307,19 @@
         return node.childrenAccept(this, data);
     }
 
-    /** {@inheritDoc} */
+    /** Sink, should not be used.
+     * @param node the node
+     * @param data some data
+     */
+    public Object visit(JexlNode node, Object data) {
+        node.dump(" ");
+        return node.childrenAccept(this, data);
+    }
+
+    /** Unused, required for generated interface compliance.
+     * @param node the node
+     * @param data some data
+     */
     public Object visit(SimpleNode node, Object data) {
         node.dump(" ");
         return node.childrenAccept(this, data);