commons-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From hen...@apache.org
Subject svn commit: r1145359 - in /commons/proper/jexl/trunk/src: main/java/org/apache/commons/jexl2/ main/java/org/apache/commons/jexl2/internal/introspection/ main/java/org/apache/commons/jexl2/introspection/ main/java/org/apache/commons/jexl2/parser/ test/j...
Date Mon, 11 Jul 2011 21:17:12 GMT
Author: henrib
Date: Mon Jul 11 21:17:11 2011
New Revision: 1145359

URL: http://svn.apache.org/viewvc?rev=1145359&view=rev
Log:
JEXL-114 / JEXL-113
* Added "var" and "return" keywords to .jjt
* Added methods getParameters, getLocalVariables and getVariables to JexlEngine to allow extracting the various variable information from Scripts
* Added tests in testVar.java

Added:
    commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/NamespaceResolver.java   (with props)
    commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/parser/ASTIdentifier.java   (with props)
    commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/parser/ASTVar.java   (with props)
    commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/parser/JexlParser.java   (with props)
    commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl2/VarTest.java   (with props)
Modified:
    commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/Debugger.java
    commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/ExpressionImpl.java
    commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/Interpreter.java
    commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/JexlEngine.java
    commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/JexlException.java
    commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/internal/introspection/IntrospectorBase.java
    commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/introspection/UberspectImpl.java
    commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/parser/ASTJexlScript.java
    commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/parser/JexlNode.java
    commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/parser/Parser.jjt
    commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/parser/StringParser.java
    commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl2/JexlTestCase.java

Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/Debugger.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/Debugger.java?rev=1145359&r1=1145358&r2=1145359&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/Debugger.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/Debugger.java Mon Jul 11 21:17:11 2011
@@ -16,10 +16,14 @@
  */
 package org.apache.commons.jexl2;
 
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
 import org.apache.commons.jexl2.parser.ASTAdditiveNode;
 import org.apache.commons.jexl2.parser.ASTAdditiveOperator;
-import org.apache.commons.jexl2.parser.ASTAndNode;
 import org.apache.commons.jexl2.parser.ASTAmbiguous;
+import org.apache.commons.jexl2.parser.ASTAndNode;
 import org.apache.commons.jexl2.parser.ASTArrayAccess;
 import org.apache.commons.jexl2.parser.ASTArrayLiteral;
 import org.apache.commons.jexl2.parser.ASTAssignment;
@@ -51,22 +55,24 @@ import org.apache.commons.jexl2.parser.A
 import org.apache.commons.jexl2.parser.ASTNENode;
 import org.apache.commons.jexl2.parser.ASTNRNode;
 import org.apache.commons.jexl2.parser.ASTNotNode;
-import org.apache.commons.jexl2.parser.ASTNumberLiteral;
 import org.apache.commons.jexl2.parser.ASTNullLiteral;
+import org.apache.commons.jexl2.parser.ASTNumberLiteral;
 import org.apache.commons.jexl2.parser.ASTOrNode;
 import org.apache.commons.jexl2.parser.ASTReference;
 import org.apache.commons.jexl2.parser.ASTReferenceExpression;
+import org.apache.commons.jexl2.parser.ASTReturnStatement;
 import org.apache.commons.jexl2.parser.ASTSizeFunction;
 import org.apache.commons.jexl2.parser.ASTSizeMethod;
 import org.apache.commons.jexl2.parser.ASTStringLiteral;
 import org.apache.commons.jexl2.parser.ASTTernaryNode;
 import org.apache.commons.jexl2.parser.ASTTrueNode;
 import org.apache.commons.jexl2.parser.ASTUnaryMinusNode;
+import org.apache.commons.jexl2.parser.ASTVar;
 import org.apache.commons.jexl2.parser.ASTWhileStatement;
 import org.apache.commons.jexl2.parser.JexlNode;
-import org.apache.commons.jexl2.parser.SimpleNode;
 
 import org.apache.commons.jexl2.parser.ParserVisitor;
+import org.apache.commons.jexl2.parser.SimpleNode;
 
 /**
  * Helps pinpoint the cause of problems in expressions that fail during evaluation.
@@ -168,9 +174,9 @@ final class Debugger implements ParserVi
         Object value = accept(child, data);
         // blocks, if, for & while dont need a ';' at end
         if (child instanceof ASTBlock
-            || child instanceof ASTIfStatement
-            || child instanceof ASTForeachStatement
-            || child instanceof ASTWhileStatement) {
+                || child instanceof ASTIfStatement
+                || child instanceof ASTForeachStatement
+                || child instanceof ASTWhileStatement) {
             return value;
         }
         builder.append(";");
@@ -251,8 +257,8 @@ final class Debugger implements ParserVi
     public Object visit(ASTAdditiveNode node, Object data) {
         // need parenthesis if not in operator precedence order
         boolean paren = node.jjtGetParent() instanceof ASTMulNode
-                        || node.jjtGetParent() instanceof ASTDivNode
-                        || node.jjtGetParent() instanceof ASTModNode;
+                || node.jjtGetParent() instanceof ASTDivNode
+                || node.jjtGetParent() instanceof ASTModNode;
         int num = node.jjtGetNumChildren(); //child.jjtGetNumChildren() > 1;
         if (paren) {
             builder.append("(");
@@ -306,7 +312,7 @@ final class Debugger implements ParserVi
         builder.append(" ]");
         return data;
     }
-    
+
     /** {@inheritDoc} */
     public Object visit(ASTAssignment node, Object data) {
         return infixChildren(node, " = ", false, data);
@@ -474,7 +480,7 @@ final class Debugger implements ParserVi
     }
 
     /** {@inheritDoc} */
-     public Object visit(ASTConstructorNode node, Object data) {
+    public Object visit(ASTConstructorNode node, Object data) {
         int num = node.jjtGetNumChildren();
         builder.append("new ");
         builder.append("(");
@@ -538,7 +544,7 @@ final class Debugger implements ParserVi
     public Object visit(ASTNRNode node, Object data) {
         return infixChildren(node, " !~ ", false, data);
     }
-    
+
     /** {@inheritDoc} */
     public Object visit(ASTNotNode node, Object data) {
         builder.append("!");
@@ -553,7 +559,7 @@ final class Debugger implements ParserVi
     }
 
     /** {@inheritDoc} */
-   public Object visit(ASTOrNode node, Object data) {
+    public Object visit(ASTOrNode node, Object data) {
         // need parenthesis if not in operator precedence order
         boolean paren = node.jjtGetParent() instanceof ASTAndNode;
         return infixChildren(node, " || ", paren, data);
@@ -573,9 +579,9 @@ final class Debugger implements ParserVi
     /** {@inheritDoc} */
     public Object visit(ASTReferenceExpression node, Object data) {
         JexlNode first = node.jjtGetChild(0);
-            builder.append('(');
+        builder.append('(');
         accept(first, data);
-            builder.append(')');
+        builder.append(')');
         int num = node.jjtGetNumChildren();
         for (int i = 1; i < num; ++i) {
             builder.append("[");
@@ -586,6 +592,13 @@ final class Debugger implements ParserVi
     }
 
     /** {@inheritDoc} */
+    public Object visit(ASTReturnStatement node, Object data) {
+        builder.append("return ");
+        accept(node.jjtGetChild(0), data);
+        return data;
+    }
+
+    /** {@inheritDoc} */
     public Object visit(ASTSizeFunction node, Object data) {
         builder.append("size(");
         accept(node.jjtGetChild(0), data);
@@ -598,7 +611,7 @@ final class Debugger implements ParserVi
         check(node, "size()", data);
         return data;
     }
-    
+
     /** {@inheritDoc} */
     public Object visit(ASTStringLiteral node, Object data) {
         String img = node.image.replace("'", "\\'");
@@ -632,6 +645,12 @@ final class Debugger implements ParserVi
         return prefixChild(node, "-", data);
     }
 
+    public Object visit(ASTVar node, Object data) {
+        builder.append("var ");
+        check(node, node.image, data);
+        return data;
+    }
+
     /** {@inheritDoc} */
     public Object visit(ASTWhileStatement node, Object data) {
         builder.append("while (");

Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/ExpressionImpl.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/ExpressionImpl.java?rev=1145359&r1=1145358&r2=1145359&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/ExpressionImpl.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/ExpressionImpl.java Mon Jul 11 21:17:11 2011
@@ -59,6 +59,7 @@ public class ExpressionImpl implements E
             return null;
         }
         Interpreter interpreter = jexl.createInterpreter(context);
+        interpreter.setArguments(script.getRegisters(), script.createArguments((Object[]) null));
         return interpreter.interpret(script.jjtGetChild(0));
     }
 
@@ -77,6 +78,22 @@ public class ExpressionImpl implements E
     public String getExpression() {
         return expression;
     }
+    
+    /**
+     * Gets this script parameters.
+     * @return the parameters or null
+     */
+    public String[] getParameters() {
+        return script.getParameters();
+    }
+       
+    /**
+     * Gets this script local variables.
+     * @return the local variables or null
+     */
+    public String[] getLocalVariables() {
+        return script.getLocalVariables();
+    }
 
     /**
      * Provide a string representation of the expression.
@@ -101,7 +118,7 @@ public class ExpressionImpl implements E
      */
     public Object execute(JexlContext context) {
         Interpreter interpreter = jexl.createInterpreter(context);
-        interpreter.setArguments(script.getParameters(), null);
+        interpreter.setArguments(script.getRegisters(), script.createArguments((Object[]) null));
         return interpreter.interpret(script);
     }
     
@@ -110,7 +127,7 @@ public class ExpressionImpl implements E
      */
     public Object execute(JexlContext context, Object...args) {
         Interpreter interpreter = jexl.createInterpreter(context);
-        interpreter.setArguments(script.getParameters(), args);
+        interpreter.setArguments(script.getRegisters(), script.createArguments(args));
         return interpreter.interpret(script);
     }
 

Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/Interpreter.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/Interpreter.java?rev=1145359&r1=1145358&r2=1145359&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/Interpreter.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/Interpreter.java Mon Jul 11 21:17:11 2011
@@ -68,6 +68,7 @@ import org.apache.commons.jexl2.parser.A
 import org.apache.commons.jexl2.parser.ASTOrNode;
 import org.apache.commons.jexl2.parser.ASTReference;
 import org.apache.commons.jexl2.parser.ASTReferenceExpression;
+import org.apache.commons.jexl2.parser.ASTReturnStatement;
 import org.apache.commons.jexl2.parser.ASTSizeFunction;
 import org.apache.commons.jexl2.parser.ASTSizeMethod;
 import org.apache.commons.jexl2.parser.ASTStringLiteral;
@@ -82,6 +83,7 @@ import org.apache.commons.jexl2.introspe
 import org.apache.commons.jexl2.introspection.JexlMethod;
 import org.apache.commons.jexl2.introspection.JexlPropertyGet;
 import org.apache.commons.jexl2.introspection.JexlPropertySet;
+import org.apache.commons.jexl2.parser.ASTVar;
 
 /**
  * An interpreter of JEXL syntax.
@@ -132,6 +134,22 @@ public class Interpreter implements Pars
     }
 
     /**
+     * Copy constructor.
+     * @param base the base to copy
+     */
+    protected Interpreter(Interpreter base) {
+        this.logger = base.logger;
+        this.uberspect = base.uberspect;
+        this.arithmetic = base.arithmetic;
+        this.functions = base.functions;
+        this.strict = base.strict;
+        this.silent = base.silent;
+        this.cache = base.cache;
+        this.context = base.context;
+        this.functors = base.functors;
+    }
+
+    /**
      * Sets whether this interpreter throws JexlException during evaluation.
      * @param flag true means no JexlException will be thrown but will be logged
      *        as info through the Jexl engine logger, false allows them to be thrown.
@@ -160,12 +178,27 @@ public class Interpreter implements Pars
     public Object interpret(JexlNode node) {
         try {
             return node.jjtAccept(this, null);
+        } catch (JexlException.Return xreturn) {
+            Object value = xreturn.getValue();
+            if (value instanceof JexlException.Return) {
+                if (silent) {
+                    logger.warn(xreturn.getMessage(), xreturn.getCause());
+                    return null;
+                }
+                throw xreturn;
+            } else {
+                return value;
+            }
         } catch (JexlException xjexl) {
             if (silent) {
                 logger.warn(xjexl.getMessage(), xjexl.getCause());
                 return null;
             }
             throw xjexl;
+        } finally {
+            functors = null;
+            parameters = null;
+            registers = null;
         }
     }
 
@@ -179,9 +212,18 @@ public class Interpreter implements Pars
 
     /**
      * Sets this interpreter registers for bean access/assign expressions.
+     * <p>Use setArguments(...) instead.</p>
      * @param theRegisters the array of registers
      */
+    @Deprecated
     protected void setRegisters(Object... theRegisters) {
+        if (theRegisters != null) {
+            String[] regStrs = new String[theRegisters.length];
+            for (int r = 0; r < regStrs.length; ++r) {
+                regStrs[r] = "#" + r;
+            }
+            this.parameters = regStrs;
+        }
         this.registers = theRegisters;
     }
 
@@ -237,7 +279,7 @@ public class Interpreter implements Pars
      * @return throws JexlException if strict, null otherwise
      */
     protected Object invocationFailed(JexlException xjexl) {
-        if (strict) {
+        if (strict || xjexl instanceof JexlException.Return) {
             throw xjexl;
         }
         if (!silent) {
@@ -255,7 +297,7 @@ public class Interpreter implements Pars
      * @return the namespace instance
      */
     protected Object resolveNamespace(String prefix, JexlNode node) {
-        Object namespace;
+        Object namespace = null;
         // check whether this namespace is a functor
         if (functors != null) {
             namespace = functors.get(prefix);
@@ -263,9 +305,15 @@ public class Interpreter implements Pars
                 return namespace;
             }
         }
-        namespace = functions.get(prefix);
-        if (prefix != null && namespace == null) {
-            throw new JexlException(node, "no such function namespace " + prefix);
+        // check if namespace if a resolver
+        if (context instanceof NamespaceResolver) {
+            namespace = ((NamespaceResolver) context).resolveNamespace(prefix);
+        }
+        if (namespace == null) {
+            namespace = functions.get(prefix);
+            if (prefix != null && namespace == null) {
+                throw new JexlException(node, "no such function namespace " + prefix);
+            }
         }
         // allow namespace to be instantiated as functor with context if possible, not an error otherwise
         if (namespace instanceof Class<?>) {
@@ -388,8 +436,15 @@ public class Interpreter implements Pars
     /** {@inheritDoc} */
     public Object visit(ASTAssignment node, Object data) {
         // left contains the reference to assign to
+        int register = -1;
         JexlNode left = node.jjtGetChild(0);
-        if (!(left instanceof ASTReference)) {
+        if (left instanceof ASTIdentifier) {
+            ASTIdentifier var = (ASTIdentifier) left;
+            register = var.getRegister();
+            if (register < 0) {
+                throw new JexlException(left, "unknown variable " + left.image);
+            }
+        } else if (!(left instanceof ASTReference)) {
             throw new JexlException(left, "illegal assignment form 0");
         }
         // right is the value expression to assign
@@ -397,22 +452,26 @@ public class Interpreter implements Pars
 
         // determine initial object & property:
         JexlNode objectNode = null;
-        Object object = null;
+        Object object = register >= 0 ? registers[register] : null;
         JexlNode propertyNode = null;
         Object property = null;
         boolean isVariable = true;
         int v = 0;
         StringBuilder variableName = null;
-        // 1: follow children till penultimate
+        // 1: follow children till penultimate, resolve dot/array
         int last = left.jjtGetNumChildren() - 1;
-        for (int c = 0; c < last; ++c) {
+        // check we are not assigning a register itself
+        boolean isRegister = last < 0 && register >= 0;
+        // start at 1 if register
+        for (int c = register >= 0 ? 1 : 0; c < last; ++c) {
             objectNode = left.jjtGetChild(c);
             // evaluate the property within the object
             object = objectNode.jjtAccept(this, object);
             if (object != null) {
                 continue;
             }
-            isVariable &= objectNode instanceof ASTIdentifier;
+            isVariable &= objectNode instanceof ASTIdentifier
+                    || (objectNode instanceof ASTNumberLiteral && ((ASTNumberLiteral) objectNode).isInteger());
             // if we get null back as a result, check for an ant variable
             if (isVariable) {
                 if (v == 0) {
@@ -433,13 +492,19 @@ public class Interpreter implements Pars
             }
         }
         // 2: last objectNode will perform assignement in all cases
-        propertyNode = left.jjtGetChild(last);
+        propertyNode = isRegister ? null : left.jjtGetChild(last);
         boolean antVar = false;
         if (propertyNode instanceof ASTIdentifier) {
-            property = ((ASTIdentifier) propertyNode).image;
-            antVar = true;
-        } else if (propertyNode instanceof ASTNumberLiteral && ((ASTNumberLiteral)propertyNode).isInteger()) {
-            property = ((ASTNumberLiteral)propertyNode).getLiteral();
+            ASTIdentifier identifier = (ASTIdentifier) propertyNode;
+            register = identifier.getRegister();
+            if (register >= 0) {
+                isRegister = true;
+            } else {
+                property = identifier.image;
+                antVar = true;
+            }
+        } else if (propertyNode instanceof ASTNumberLiteral && ((ASTNumberLiteral) propertyNode).isInteger()) {
+            property = ((ASTNumberLiteral) propertyNode).getLiteral();
             antVar = true;
         } else if (propertyNode instanceof ASTArrayAccess) {
             // first objectNode is the identifier
@@ -464,11 +529,14 @@ public class Interpreter implements Pars
                 }
             }
             property = narray.jjtGetChild(last).jjtAccept(this, null);
-        } else {
-                throw new JexlException(objectNode, "illegal assignment form");
+        } else if (!isRegister) {
+            throw new JexlException(objectNode, "illegal assignment form");
         }
         // deal with ant variable; set context
-        if (antVar) {
+        if (isRegister) {
+            registers[register] = right;
+            return right;
+        } else if (antVar) {
             if (isVariable && object == null) {
                 if (variableName != null) {
                     if (last > 0) {
@@ -622,6 +690,7 @@ public class Interpreter implements Pars
         /* first objectNode is the loop variable */
         ASTReference loopReference = (ASTReference) node.jjtGetChild(0);
         ASTIdentifier loopVariable = (ASTIdentifier) loopReference.jjtGetChild(0);
+        int register = loopVariable.getRegister();
         /* second objectNode is the variable to iterate */
         Object iterableValue = node.jjtGetChild(1).jjtAccept(this, data);
         // make sure there is a value to iterate on and a statement to execute
@@ -635,7 +704,11 @@ public class Interpreter implements Pars
                 while (itemsIterator.hasNext()) {
                     // set loopVariable to value of iterator
                     Object value = itemsIterator.next();
-                    context.set(loopVariable.image, value);
+                    if (register < 0) {
+                        context.set(loopVariable.image, value);
+                    } else {
+                        registers[register] = value;
+                    }
                     // execute statement
                     result = statement.jjtAccept(this, data);
                 }
@@ -681,8 +754,9 @@ public class Interpreter implements Pars
     public Object visit(ASTIdentifier node, Object data) {
         String name = node.image;
         if (data == null) {
-            if (registers != null && name.charAt(0) == '#') {
-                return registers[Integer.parseInt(name.substring(1))];
+            int register = node.getRegister();
+            if (register >= 0) {
+                return registers[register];
             }
             Object value = context.get(name);
             if (value == null
@@ -698,6 +772,11 @@ public class Interpreter implements Pars
     }
 
     /** {@inheritDoc} */
+    public Object visit(ASTVar node, Object data) {
+        return visit((ASTIdentifier) node, data);
+    }
+
+    /** {@inheritDoc} */
     public Object visit(ASTIfStatement node, Object data) {
         int n = 0;
         try {
@@ -1040,7 +1119,7 @@ public class Interpreter implements Pars
         for (int c = 0; c < numChildren; c++) {
             JexlNode theNode = node.jjtGetChild(c);
             // integer literals may be part of an antish var name only if no bean was found so far
-            if (result == null && theNode instanceof ASTNumberLiteral && theNode.image.matches("\\d*")) {
+            if (result == null && theNode instanceof ASTNumberLiteral && ((ASTNumberLiteral) theNode).isInteger()) {
                 isVariable &= v > 0;
             } else {
                 isVariable &= (theNode instanceof ASTIdentifier);
@@ -1067,11 +1146,18 @@ public class Interpreter implements Pars
         }
         return result;
     }
-
+    
+    /** {@inheritDoc} */
     public Object visit(ASTReferenceExpression node, Object data) {
         ASTArrayAccess upper = node;
         return visit(upper, data);
     }
+    
+    /** {@inheritDoc} */
+    public Object visit(ASTReturnStatement node, Object data) {
+        Object val = node.jjtGetChild(0).jjtAccept(this, data);
+        throw new JexlException.Return(node, null, val);
+    }
 
     /**
      * Check if a null evaluated expression is protected by a ternary expression.
@@ -1137,7 +1223,7 @@ public class Interpreter implements Pars
     public Object visit(ASTTrueNode node, Object data) {
         return Boolean.TRUE;
     }
-    
+
     /** {@inheritDoc} */
     public Object visit(ASTUnaryMinusNode node, Object data) {
         JexlNode valNode = node.jjtGetChild(0);
@@ -1299,6 +1385,14 @@ public class Interpreter implements Pars
         }
         JexlException xjexl = null;
         JexlPropertySet vs = uberspect.getPropertySet(object, attribute, value, node);
+        // if we can't find an exact match, narrow the value argument and try again
+        if (vs == null) {
+            // replace all numbers with the smallest type that will fit
+            Object[] narrow = {value};
+            if (arithmetic.narrowArguments(narrow)) {
+                vs = uberspect.getPropertySet(object, attribute, narrow[0], node);
+            }
+        }
         if (vs != null) {
             try {
                 // cache executor in volatile JexlNode.value
@@ -1322,7 +1416,8 @@ public class Interpreter implements Pars
         if (xjexl == null) {
             String error = "unable to set object property"
                     + ", class: " + object.getClass().getName()
-                    + ", property: " + attribute;
+                    + ", property: " + attribute
+                    + ", argument: " + value.getClass().getSimpleName();
             if (node == null) {
                 throw new UnsupportedOperationException(error);
             }

Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/JexlEngine.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/JexlEngine.java?rev=1145359&r1=1145358&r2=1145359&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/JexlEngine.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/JexlEngine.java Mon Jul 11 21:17:11 2011
@@ -27,10 +27,13 @@ import java.net.URL;
 import java.net.URLConnection;
 import java.lang.ref.SoftReference;
 import java.lang.reflect.Constructor;
+import java.util.ArrayList;
 import java.util.Map;
 import java.util.Set;
 import java.util.Collections;
-import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
 import java.util.Map.Entry;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -44,6 +47,11 @@ import org.apache.commons.jexl2.parser.A
 import org.apache.commons.jexl2.introspection.Uberspect;
 import org.apache.commons.jexl2.introspection.UberspectImpl;
 import org.apache.commons.jexl2.introspection.JexlMethod;
+import org.apache.commons.jexl2.parser.ASTArrayAccess;
+import org.apache.commons.jexl2.parser.ASTIdentifier;
+import org.apache.commons.jexl2.parser.ASTNumberLiteral;
+import org.apache.commons.jexl2.parser.ASTReference;
+import org.apache.commons.jexl2.parser.ASTStringLiteral;
 
 /**
  * <p>
@@ -90,7 +98,7 @@ import org.apache.commons.jexl2.introspe
  * </p>
  * @since 2.0
  */
-public class JexlEngine {    
+public class JexlEngine {
     /**
      * An empty/static/non-mutable JexlContext used instead of null context.
      */
@@ -99,10 +107,12 @@ public class JexlEngine {    
         public Object get(String name) {
             return null;
         }
+
         /** {@inheritDoc} */
         public boolean has(String name) {
             return false;
         }
+
         /** {@inheritDoc} */
         public void set(String name, Object value) {
             throw new UnsupportedOperationException("Not supported in void context.");
@@ -120,10 +130,11 @@ public class JexlEngine {    
     private static final class UberspectHolder {
         /** The default uberspector that handles all introspection patterns. */
         private static final Uberspect UBERSPECT = new UberspectImpl(LogFactory.getLog(JexlEngine.class));
+
         /** Non-instantiable. */
-        private UberspectHolder() {}
+        private UberspectHolder() {
+        }
     }
-    
     /**
      * The Uberspect instance.
      */
@@ -191,7 +202,6 @@ public class JexlEngine {    
         }
     }
 
-
     /**
      *  Gets the default instance of Uberspect.
      * <p>This is lazily initialized to avoid building a default instance if there
@@ -369,7 +379,7 @@ public class JexlEngine {    
     protected Expression createExpression(ASTJexlScript tree, String text) {
         return new ExpressionImpl(this, text, tree);
     }
-    
+
     /**
      * Creates an Expression from a String containing valid
      * JEXL syntax.  This method parses the expression which
@@ -400,7 +410,7 @@ public class JexlEngine {    
         ASTJexlScript 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 + "\" ");
+                    + " to the first expression from the supplied script: \"" + expression + "\" ");
         }
         return createExpression(tree, expression);
     }
@@ -424,8 +434,8 @@ public class JexlEngine {    
      * a corresponding array of arguments containing values should be used during evaluation.
      *
      * @param scriptText A String containing valid JEXL syntax
-     * @param names the parameter names
      * @param info An info structure to carry debugging information if needed
+     * @param names the script parameter names
      * @return A {@link Script} which can be executed using a {@link JexlContext}.
      * @throws JexlException if there is a problem parsing the script.
      */
@@ -447,7 +457,7 @@ public class JexlEngine {    
     protected Script createScript(ASTJexlScript tree, String text) {
         return new ExpressionImpl(this, text, tree);
     }
-    
+
     /**
      * Creates a Script from a {@link File} containing valid JEXL syntax.
      * This method parses the script and validates the syntax.
@@ -537,11 +547,12 @@ public class JexlEngine {    
         expr = "#0" + (expr.charAt(0) == '[' ? "" : ".") + expr + ";";
         try {
             parser.ALLOW_REGISTERS = true;
-            JexlNode tree = parse(expr, null);
-            JexlNode node = tree.jjtGetChild(0);
+            String[] regStrs = {"#0"};
+            ASTJexlScript script = parse(expr, null, regStrs);
+            JexlNode node = script.jjtGetChild(0);
             Interpreter interpreter = createInterpreter(context);
             // set register
-            interpreter.setRegisters(bean);
+            interpreter.setArguments(script.getParameters(), script.createArguments(bean));
             return node.jjtAccept(interpreter, null);
         } catch (JexlException xjexl) {
             if (silent) {
@@ -591,11 +602,12 @@ public class JexlEngine {    
         expr = "#0" + (expr.charAt(0) == '[' ? "" : ".") + expr + "=" + "#1" + ";";
         try {
             parser.ALLOW_REGISTERS = true;
-            JexlNode tree = parse(expr, null);
-            JexlNode node = tree.jjtGetChild(0);
+            String[] regStrs = {"#0", "#1"};
+            ASTJexlScript script = parse(expr, null, regStrs);
+            JexlNode node = script.jjtGetChild(0);
             Interpreter interpreter = createInterpreter(context);
             // set the registers
-            interpreter.setRegisters(bean, value);
+            interpreter.setArguments(script.getParameters(), script.createArguments(bean, value));
             node.jjtAccept(interpreter, null);
         } catch (JexlException xjexl) {
             if (silent) {
@@ -652,7 +664,7 @@ public class JexlEngine {    
      * @param args the constructor arguments
      * @return the created object instance or null on failure when silent
      */
-    public <T> T newInstance(Class<? extends T> clazz, Object...args) {
+    public <T> T newInstance(Class<? extends T> clazz, Object... args) {
         return clazz.cast(doCreateInstance(clazz, args));
     }
 
@@ -663,8 +675,8 @@ public class JexlEngine {    
      * @param args the constructor arguments
      * @return the created object instance or null on failure when silent
      */
-    public Object newInstance(String clazz, Object...args) {
-       return doCreateInstance(clazz, args);
+    public Object newInstance(String clazz, Object... args) {
+        return doCreateInstance(clazz, args);
     }
 
     /**
@@ -674,7 +686,7 @@ public class JexlEngine {    
      * @param args the constructor arguments
      * @return the created object instance or null on failure when silent
      */
-    protected Object doCreateInstance(Object clazz, Object...args) {
+    protected Object doCreateInstance(Object clazz, Object... args) {
         JexlException xjexl = null;
         Object result = null;
         JexlInfo info = debugInfo();
@@ -811,12 +823,110 @@ public class JexlEngine {    
      * Clears the expression cache.
      */
     public void clearCache() {
-        synchronized(parser) {
+        synchronized (parser) {
             cache.clear();
         }
     }
 
     /**
+     * Gets the list of variables accessed by a script.
+     * <p>This method will visit all nodes of a script and extract all variables whether they
+     * are written in 'dot' or 'bracketed' notation. (a.b is equivalent to a['b']).</p>
+     * @param script the script
+     * @return the set of variables, each as a list of strings (ant-ish variables use more than 1 string)
+     */
+    public Set<List<String>> getVariables(Script script) {
+        if (script instanceof ExpressionImpl) {
+            Set<List<String>> refs = new LinkedHashSet<List<String>>();
+            getVariables(((ExpressionImpl) script).script, refs, null);
+            return refs;
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Fills up the list of variables accessed by a node.
+     * @param node the node
+     * @param refs the set of variable being filled
+     * @param ref the current variable being filled
+     */
+    protected void getVariables(JexlNode node, Set<List<String>> refs, List<String> ref) {
+        boolean array = node instanceof ASTArrayAccess;
+        boolean reference = node instanceof ASTReference;
+        int num = node.jjtGetNumChildren();
+        if (array || reference) {
+            List<String> var = ref != null ? ref : new ArrayList<String>();
+            boolean varf = true;
+            for (int i = 0; i < num; ++i) {
+                JexlNode child = node.jjtGetChild(i);
+                if (array) {
+                    if (child instanceof ASTReference && child.jjtGetNumChildren() == 1) {
+                        JexlNode desc = child.jjtGetChild(0);
+                        if (varf && desc.isConstant()) {
+                            var.add(desc.image);
+                        } else if (desc instanceof ASTIdentifier) {
+                            if (((ASTIdentifier)desc).getRegister() < 0) {
+                                List<String> di = new ArrayList<String>(1);
+                                di.add(desc.image);
+                                refs.add(di);
+                            }
+                            var = new ArrayList<String>();
+                            varf = false;
+                        }
+                        continue;
+                    } else if (child instanceof ASTIdentifier) {
+                        if (i == 0 && (((ASTIdentifier)child).getRegister() < 0)) {
+                            var.add(child.image);
+                        }
+                        continue;
+                    }
+                } else {//if (reference) {
+                    if (child instanceof ASTIdentifier ) {
+                        if (((ASTIdentifier)child).getRegister() < 0) {
+                            var.add(child.image);
+                        }
+                        continue;
+                    }
+                }
+                getVariables(child, refs, var);
+            }
+            if (!var.isEmpty() && var != ref) {
+                refs.add(var);
+            }
+        } else {
+            for (int i = 0; i < num; ++i) {
+                getVariables(node.jjtGetChild(i), refs, null);
+            }
+        }
+    }
+
+    /**
+     * Gets the array of parameters from a script.
+     * @param script the script
+     * @return the parameters
+     */
+    public String[] getParameters(Script script) {
+        if (script instanceof ExpressionImpl) {
+            return ((ExpressionImpl) script).getParameters();
+        } else {
+            return null;
+        }
+    }
+    /**
+     * Gets the array of local variable from a script.
+     * @param script the script
+     * @return the parameters
+     */
+    public String[] getLocalVariables(Script script) {
+        if (script instanceof ExpressionImpl) {
+            return ((ExpressionImpl) script).getLocalVariables();
+        } else {
+            return null;
+        }
+    }
+
+    /**
      * Parses an expression.
      * @param expression the expression to parse
      * @param info debug information structure
@@ -826,14 +936,23 @@ public class JexlEngine {    
     protected ASTJexlScript parse(CharSequence expression, JexlInfo info) {
         return parse(expression, info, null);
     }
+
+    /**
+     * Parses an expression.
+     * @param expression the expression to parse
+     * @param info debug information structure
+     * @param names the parameter names array
+     * @return the parsed tree
+     * @throws JexlException if any error occured during parsing
+     */
     protected ASTJexlScript parse(CharSequence expression, JexlInfo info, String[] names) {
         String expr = cleanExpression(expression);
-        ASTJexlScript tree = null;
+        ASTJexlScript script = null;
         synchronized (parser) {
             if (cache != null) {
-                tree = cache.get(expr);
-                if (tree != null) {
-                    return tree;
+                script = cache.get(expr);
+                if (script != null) {
+                    return script;
                 }
             }
             try {
@@ -842,26 +961,32 @@ public class JexlEngine {    
                 if (info == null) {
                     info = debugInfo();
                 }
+                Map<String, Integer> params = null;
                 if (names != null) {
-                    Map<String, Integer> params = new HashMap<String, Integer>();
-                    for(int n = 0; n < names.length; ++n) {
+                    params = new LinkedHashMap<String, Integer>();
+                    for (int n = 0; n < names.length; ++n) {
                         params.put(names[n], Integer.valueOf(n));
                     }
                     parser.setNamedRegisters(params);
                 }
-                tree = parser.parse(reader, info);
+                script = parser.parse(reader, info);
+                params = parser.getNamedRegisters();
+                if (params != null) {
+                    String[] registers = (new ArrayList<String>(params.keySet())).toArray(new String[0]);
+                    script.setParameters(names!= null? names.length : 0, registers);
+                }
                 if (cache != null) {
-                    cache.put(expr, tree);
+                    cache.put(expr, script);
                 }
             } catch (TokenMgrError xtme) {
-                throw new JexlException(info, "!!! " +expression+ " !!!" + ", tokenization failed", xtme);
+                throw new JexlException(info, "!!! " + expression + " !!!" + ", tokenization failed", xtme);
             } catch (ParseException xparse) {
-                throw new JexlException(info, "!!! " +expression+ " !!!" + ", parsing failed", xparse);
+                throw new JexlException(info, "!!! " + expression + " !!!" + ", parsing failed", xparse);
             } finally {
                 parser.setNamedRegisters(null);
             }
         }
-        return tree;
+        return script;
     }
 
     /**
@@ -959,7 +1084,7 @@ public class JexlEngine {    
         } finally {
             try {
                 reader.close();
-            } catch(IOException xio) {
+            } catch (IOException xio) {
                 // ignore
             }
         }

Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/JexlException.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/JexlException.java?rev=1145359&r1=1145358&r2=1145359&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/JexlException.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/JexlException.java Mon Jul 11 21:17:11 2011
@@ -79,6 +79,20 @@ public class JexlException extends Runti
     }
     
     /**
+     * The class of exceptions that will force the interpreter to return, allways behaving in strict mode.
+     */
+    public static class Return extends JexlException {   
+        private final Object result;
+        public Return(JexlNode node, String msg, Object result) {
+            super(node, msg);
+            this.result = result;
+        }
+        public Object getValue() {
+            return result;
+        }
+    }
+    
+    /**
      * Gets information about the cause of this error.
      * <p>
      * The returned string represents the outermost expression in error.

Added: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/NamespaceResolver.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/NamespaceResolver.java?rev=1145359&view=auto
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/NamespaceResolver.java (added)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/NamespaceResolver.java Mon Jul 11 21:17:11 2011
@@ -0,0 +1,34 @@
+/*
+ * 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.jexl2;
+
+/**
+ * In JEXL, a namespace is an object that serves the purpose of encapsulating functions; for instance,
+ * the "math" namespace would be the proper object to expose functions like "log(...)", "sinus(...)", etc.
+ * 
+ * This interface declares how to resolve a namespace from its name; it is used by the interpreter during evalutation.
+ *
+ * In expressions like "ns:function(...)", the resolver is called with resolveNamespace("ns").
+ */
+public interface NamespaceResolver {
+    /**
+     * Resolves a namespace by its name.
+     * @param name the name
+     * @return the namespace object
+     */
+    Object resolveNamespace(String name);
+}

Propchange: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/NamespaceResolver.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/internal/introspection/IntrospectorBase.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/internal/introspection/IntrospectorBase.java?rev=1145359&r1=1145358&r2=1145359&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/internal/introspection/IntrospectorBase.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/internal/introspection/IntrospectorBase.java Mon Jul 11 21:17:11 2011
@@ -77,13 +77,27 @@ public class IntrospectorBase {
         this.rlog = log;
         loader = getClass().getClassLoader();
     }
+    
+    /**
+     * Gets a class by name through this introspector class loader.
+     * @param className the class name
+     * @return the class instance or null if it could not be found
+     */
+    public Class<?> getClassByName(String className) {
+        try {
+            return Class.forName(className, false, loader);
+        } catch(ClassNotFoundException xignore) {
+            return null;
+        }
+    }
 
     /**
      * Gets the method defined by the <code>MethodKey</code> for the class <code>c</code>.
      *
      * @param c     Class in which the method search is taking place
      * @param key   Key of the method being searched for
-     * @return The desired method object or null if no unambiguous method could be found through introspection.
+     * @return The desired method object
+     * @throws MethodKey.AmbiguousException if no unambiguous method could be found through introspection
      */
     public Method getMethod(Class<?> c, MethodKey key) {
         try {
@@ -96,9 +110,8 @@ public class IntrospectorBase {
                            + c.getName() + "."
                            + key.debugString(), xambiguous);
             }
+            return null;
         }
-        return null;
-
     }
 
 

Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/introspection/UberspectImpl.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/introspection/UberspectImpl.java?rev=1145359&r1=1145358&r2=1145359&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/introspection/UberspectImpl.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/introspection/UberspectImpl.java Mon Jul 11 21:17:11 2011
@@ -52,6 +52,14 @@ public class UberspectImpl extends Intro
     public UberspectImpl(Log runtimeLogger) {
         super(runtimeLogger);
     }
+        
+    /**
+     * Resets this Uberspect class loader.
+     * @param cloader the class loader to use
+     */
+    public void setLoader(ClassLoader cloader) {
+        base().setLoader(cloader);
+    }
 
     /**
      * {@inheritDoc}

Added: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/parser/ASTIdentifier.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/parser/ASTIdentifier.java?rev=1145359&view=auto
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/parser/ASTIdentifier.java (added)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/parser/ASTIdentifier.java Mon Jul 11 21:17:11 2011
@@ -0,0 +1,50 @@
+/*
+ * 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.jexl2.parser;
+
+/**
+ * Identifiers, variables and registers.
+ */
+public class ASTIdentifier extends JexlNode {
+    private int register = -1;
+    public ASTIdentifier(int id) {
+        super(id);
+    }
+
+    public ASTIdentifier(Parser p, int id) {
+        super(p, id);
+    }
+    
+    void setRegister(String r) {
+        if (r.charAt(0) == '#') {
+            register = Integer.parseInt(r.substring(1));
+        }
+    }
+    
+    void setRegister(int r) {
+        register = r;
+    }
+    
+    public int getRegister() {
+        return register;
+    }
+
+    @Override
+    public Object jjtAccept(ParserVisitor visitor, Object data) {
+        return visitor.visit(this, data);
+    }
+}

Propchange: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/parser/ASTIdentifier.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/parser/ASTJexlScript.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/parser/ASTJexlScript.java?rev=1145359&r1=1145358&r2=1145359&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/parser/ASTJexlScript.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/parser/ASTJexlScript.java Mon Jul 11 21:17:11 2011
@@ -20,7 +20,10 @@ package org.apache.commons.jexl2.parser;
  * Enhanced script to allow parameters declaration.
  */
 public class ASTJexlScript extends JexlNode {
-    private String[] parameters = null;
+    /** The number of parameters defined. */
+    private int parms = 0;
+    /** Each parameter will use a register but so do local script variables. */
+    private String[] registers = null;
 
     public ASTJexlScript(int id) {
         super(id);
@@ -35,12 +38,77 @@ public class ASTJexlScript extends JexlN
         return visitor.visit(this, data);
     }
 
-    public void setParameters(String[] params) {
-        parameters = params;
+    /**
+     * Sets the parameters and registers
+     * @param parms the number of parameters
+     * @param registers the array of register names
+     */
+    public void setParameters(int parms, String[] registers) {
+        if (parms > registers.length) {
+            throw new IllegalArgumentException();
+        }
+        this.parms = parms;
+        this.registers = registers;
+    }
+    
+    /**
+     * Creates an array of arguments by copying values up to the number of parameters 
+     * @param values the argument values
+     * @return the arguments array
+     */
+    public Object[] createArguments(Object... values) {
+        if (registers != null) {
+            Object[] frame = new Object[registers.length];
+            if (values != null) {
+                System.arraycopy(values, 0, frame, 0, Math.min(parms, values.length));
+            }
+            return frame;
+        } else {
+            return null;
+        }
+    }
+    
+    /**
+     * Gets the (maximum) number of arguments this script expects.
+     * @return the number of parameters
+     */
+    public int getArgCount() {
+        return parms;
+    }
+    
+    /**
+     * Gets this script registers, i.e. parameters and local variables.
+     * @return the register names
+     */
+    public String[] getRegisters() {
+        return registers;
     }
 
+    /**
+     * Gets this script parameters, i.e. registers assigned before creating local variables.
+     * @return the parameter names
+     */
     public String[] getParameters() {
-        return parameters;
+        if (registers != null && parms > 0) {
+            String[] pa = new String[parms];
+            System.arraycopy(registers, 0, pa, 0, parms);
+            return pa;
+        } else {
+            return null;
+        }
     }
 
+    /**
+     * Gets this script local variable, i.e. registers assigned to local variables.
+     * @return the parameter names
+     */
+    public String[] getLocalVariables() {
+        if (registers != null) {
+            String[] pa = new String[registers.length - parms];
+            System.arraycopy(registers, 0, pa, 0, registers.length - parms);
+            return pa;
+        } else {
+            return null;
+        }
+    }
 }

Added: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/parser/ASTVar.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/parser/ASTVar.java?rev=1145359&view=auto
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/parser/ASTVar.java (added)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/parser/ASTVar.java Mon Jul 11 21:17:11 2011
@@ -0,0 +1,35 @@
+/*
+ * 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.jexl2.parser;
+
+/**
+ * Declares a local variable.
+ */
+public class ASTVar extends ASTIdentifier {
+    public ASTVar(int id) {
+        super(id);
+    }
+
+    public ASTVar(Parser p, int id) {
+        super(p, id);
+    }
+    
+    @Override
+    public Object jjtAccept(ParserVisitor visitor, Object data) {
+        return visitor.visit(this, data);
+    }
+}

Propchange: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/parser/ASTVar.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/parser/JexlNode.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/parser/JexlNode.java?rev=1145359&r1=1145358&r2=1145359&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/parser/JexlNode.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/parser/JexlNode.java Mon Jul 11 21:17:11 2011
@@ -65,7 +65,10 @@ public abstract class JexlNode extends S
      * @return true if constant, false otherwise
      */
     public boolean isConstant() {
-        if (this instanceof JexlNode.Literal<?>) {
+        return isConstant(this instanceof JexlNode.Literal<?>);
+    }
+    public boolean isConstant(boolean literal) {
+        if (literal) {
             if (children != null) {
                 for(JexlNode child : children) {
                     if (!child.isConstant()) {

Added: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/parser/JexlParser.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/parser/JexlParser.java?rev=1145359&view=auto
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/parser/JexlParser.java (added)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/parser/JexlParser.java Mon Jul 11 21:17:11 2011
@@ -0,0 +1,133 @@
+/*
+ * 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.jexl2.parser;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ *
+ * @author henri
+ */
+public class JexlParser extends StringParser {
+     /**
+     * The map of named registers aka script parameters.
+     * Each parameter is associated to a register and is materialized as an offset in the registers array used
+     * during evaluation.
+     */
+    protected Map<String, Integer> namedRegisters = null;
+
+    /**
+     * Sets the map of named registers in this parser.
+     * <p>
+     * This is used to allow parameters to be declared before parsing.
+     * </p>
+     * @param registers the register map
+     */
+    public void setNamedRegisters(Map<String, Integer> registers) {
+        namedRegisters = registers;
+    }
+    
+    /**
+     * Gets the map of registers used by this parser.
+     * <p>
+     * Since local variables create new named registers, it is important to regain access after
+     * parsing to known which / how-many registers are needed.
+     * </p>
+     * @return the named register map
+     */
+    public Map<String, Integer> getNamedRegisters() {
+        return namedRegisters;
+    }
+
+    /**
+     * Checks whether an identifier is a local variable or argument, ie stored in a register. 
+     * @param node the identifier
+     * @param image the identifier image
+     * @return the image
+     */
+    public String checkVariable(ASTIdentifier identifier, String image) {
+        if (namedRegisters != null) {
+            Integer register = namedRegisters.get(image);
+            if (register != null) {
+                identifier.setRegister(register);
+            }
+        }
+        return image;
+    }
+    
+    /**
+     * Declares a local variable.
+     * <p>
+     * This method creates an new entry in the named register map.
+     * </p>
+     * @param identifier the identifier used to declare
+     * @param image the variable name
+     */
+    public void declareVariable(ASTVar identifier, String image) {
+        if (namedRegisters == null) {
+            namedRegisters = new LinkedHashMap<String, Integer>();
+        }
+        Integer register = namedRegisters.get(image);
+        if (register == null) {
+            register = Integer.valueOf(namedRegisters.size());
+            namedRegisters.put(image, register);
+        }
+        identifier.setRegister(register);
+        identifier.image = image;
+    }
+
+    
+    /**
+     * Default implementation does nothing but is overriden by generated code.
+     * @param top whether the identifier is beginning an l/r value
+     * @throws ParseException subclasses may throw this 
+     */
+    public void Identifier(boolean top) throws ParseException {
+        // Overriden by generated code
+    }
+    
+    final public void Identifier() throws ParseException {
+        Identifier(false);
+    }
+    
+    public Token getToken(int index) {
+        return null;
+    }
+    
+    void jjtreeOpenNodeScope(Node n) {}
+    /**
+     * Ambiguous statement detector.
+     * @param n the node
+     * @throws ParseException 
+     */
+    void jjtreeCloseNodeScope(Node n) throws ParseException {
+      if (n instanceof ASTAmbiguous && n.jjtGetNumChildren() > 0) {
+          Token tok = this.getToken(0);
+          StringBuilder strb = new StringBuilder("Ambiguous statement ");
+          if (tok != null) {
+              strb.append("@");
+              strb.append(tok.beginLine);
+              strb.append(":");
+              strb.append(tok.beginColumn);
+          }
+          strb.append(", missing ';' between expressions");
+         throw new ParseException(strb.toString());
+      }
+    }
+    
+}

Propchange: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/parser/JexlParser.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/parser/Parser.jjt
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/parser/Parser.jjt?rev=1145359&r1=1145358&r2=1145359&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/parser/Parser.jjt (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/parser/Parser.jjt Mon Jul 11 21:17:11 2011
@@ -33,7 +33,7 @@ package org.apache.commons.jexl2.parser;
 import java.io.Reader;
 import org.apache.commons.jexl2.JexlInfo;
 
-public class Parser extends StringParser
+public class Parser extends JexlParser
 {
     public boolean ALLOW_REGISTERS = false;
 
@@ -52,9 +52,6 @@ public class Parser extends StringParser
          */
 
         ASTJexlScript tree = JexlScript();
-        if (namedRegisters != null) {
-            tree.setParameters(namedRegisters.keySet().toArray(new String[0]));
-        }
         tree.value = info;
         return tree;
     }
@@ -88,11 +85,13 @@ PARSER_END(Parser)
     | < FOREACH : "foreach" > : FOR_EACH_IN
     | < WHILE : "while" >
     | < NEW : "new" >
+    | < VAR : "var" >
     | < EMPTY : "empty" >
     | < SIZE : "size" >
     | < NULL : "null" >
     | < TRUE : "true" >
     | < FALSE : "false" >
+    | < RETURN : "return" >
 }
 
 <FOR_EACH_IN> TOKEN : /* foreach in */
@@ -160,7 +159,7 @@ PARSER_END(Parser)
 
 <REGISTERS> TOKEN : /* REGISTERS: parser.ALLOW_REGISTER must be set to true before calling parse */
 {
-  < REGISTER: "#" ["0"-"9"] >
+  < REGISTER: "#" (["0"-"9"])+ >
 }
 
 <*> TOKEN : /* LITERALS */
@@ -200,6 +199,8 @@ void Statement() #void : {}
     | ForeachStatement()
     | WhileStatement()
     | ExpressionStatement()
+    | ReturnStatement()
+    | Var()
 }
 
 void Block() #Block : {}
@@ -227,10 +228,15 @@ void WhileStatement() : {}
 
 
 void ForeachStatement() : {}
-{
-    <FOR> <LPAREN> Reference() <COLON>  Expression() <RPAREN> Statement()
+{  
+    <FOR> <LPAREN> LValueVar() <COLON>  Expression() <RPAREN> Statement()
 |
-    <FOREACH> <LPAREN> Reference() <IN>  Expression() <RPAREN> Statement()
+    <FOREACH> <LPAREN> LValueVar() <IN>  Expression() <RPAREN> Statement()
+}
+
+void ReturnStatement() : {}
+{
+    <RETURN> Expression()
 }
 
 
@@ -248,6 +254,26 @@ void Assignment() #Assignment(2) : {}
     ConditionalExpression() <assign> Expression()
 }
 
+void Var() #void : {}
+{
+    <VAR> DeclareVar() (LOOKAHEAD(1) <assign> Expression() #Assignment(2))?
+}
+
+void DeclareVar() #Var :
+{
+    Token t;
+}
+{
+    t=<IDENTIFIER> { declareVariable(jjtThis, t.image); }
+}
+
+void LValueVar() #Reference : {}
+{   
+    LOOKAHEAD(1) <VAR> DeclareVar() DotReference() 
+|
+    LOOKAHEAD(1) Identifier(true) DotReference()
+}
+
 /***************************************
  *      Conditional & relational
  ***************************************/
@@ -380,9 +406,9 @@ void Identifier(boolean top) :
     Token t;
 }
 {
-    t=<IDENTIFIER> { jjtThis.image = top? imageOf(jjtThis, t.image) : t.image; }
+    t=<IDENTIFIER> { jjtThis.image = top? checkVariable(jjtThis, t.image) : t.image; }
 |
-    t=<REGISTER> { jjtThis.image = t.image; }
+    t=<REGISTER> { jjtThis.image = t.image; jjtThis.setRegister(t.image); }
 }
 
 void Literal() #void :

Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/parser/StringParser.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/parser/StringParser.java?rev=1145359&r1=1145358&r2=1145359&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/parser/StringParser.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/parser/StringParser.java Mon Jul 11 21:17:11 2011
@@ -16,8 +16,6 @@
  */
 package org.apache.commons.jexl2.parser;
 
-import java.util.Map;
-
 /**
  * Common constant strings utilities.
  * <p>
@@ -39,60 +37,6 @@ public class StringParser {
     /** Default constructor.  */
     public StringParser() {
     }
-
-    /**
-     * The map of named registers aka script parameters.
-     */
-    protected Map<String, Integer> namedRegisters = null;
-
-    public void setNamedRegisters(Map<String, Integer> registers) {
-        namedRegisters = registers;
-    }
-
-    public String imageOf(JexlNode identifier, String image) {
-        if (namedRegisters != null && identifier instanceof ASTIdentifier) {
-            if (!(identifier.jjtGetParent() instanceof ASTIdentifier)) {
-                Integer register = namedRegisters.get(image);
-                if (register != null) {
-                    return '#' + register.toString();
-                }
-            }
-        }
-        return image;
-    }
-    
-    public Token getToken(int index) {
-        return null;
-    }
-    
-    /**
-     * Default implementation does nothing.
-     *
-     * @throws ParseException subclasses may throw this 
-     */
-    public void Identifier(boolean top) throws ParseException {
-        // Overriden by generated code
-    }
-
-    final public void Identifier() throws ParseException {
-        Identifier(false);
-    }
-
-    void jjtreeOpenNodeScope(Node n) {}
-    void jjtreeCloseNodeScope(Node n) throws ParseException {
-      if (n instanceof ASTAmbiguous && n.jjtGetNumChildren() > 0) {
-          Token tok = this.getToken(0);
-          StringBuilder strb = new StringBuilder("Ambiguous statement ");
-          if (tok != null) {
-              strb.append("@");
-              strb.append(tok.beginLine);
-              strb.append(":");
-              strb.append(tok.beginColumn);
-          }
-          strb.append(", missing ';' between expressions");
-         throw new ParseException(strb.toString());
-      }
-    }
     
     /**
      * Builds a string, handles escaping through '\' syntax.

Modified: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl2/JexlTestCase.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl2/JexlTestCase.java?rev=1145359&r1=1145358&r2=1145359&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl2/JexlTestCase.java (original)
+++ commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl2/JexlTestCase.java Mon Jul 11 21:17:11 2011
@@ -96,7 +96,7 @@ public class JexlTestCase extends TestCa
             dbg.debug(node);
             String expressiondbg = dbg.data();
             // recreate expr from string
-            Expression exprdbg = jdbg.createExpression(expressiondbg);
+            Script exprdbg = jdbg.createScript(expressiondbg);
             // make arg cause become the root cause
             JexlNode root = ((ExpressionImpl) exprdbg).script;
             while (root.jjtGetParent() != null) {
@@ -118,7 +118,7 @@ public class JexlTestCase extends TestCa
      * @param script the script to flatten
      * @return the descendants-and-self list
      */
-    private static ArrayList<JexlNode> flatten(JexlNode node) {
+    protected static ArrayList<JexlNode> flatten(JexlNode node) {
         ArrayList<JexlNode> list = new ArrayList<JexlNode>();
         flatten(list, node);
         return list;
@@ -168,7 +168,40 @@ public class JexlTestCase extends TestCa
         }
         return null;
     }
+    
+    /**
+     * A helper class to help debug AST problems.
+     * @param e the script
+     * @return an indented version of the AST
+     */
+    protected String flattenedStr(Script e) {
+        return e.getText() + "\n" + flattenedStr(((ExpressionImpl) e).script);
+    }
+
+    static private String indent(JexlNode node) {
+        StringBuilder strb = new StringBuilder();
+        while (node != null) {
+            strb.append("  ");
+            node = node.jjtGetParent();
+        }
+        return strb.toString();
+    }
+
 
+    private String flattenedStr(JexlNode node) {
+        ArrayList<JexlNode> flattened = flatten(node);
+        StringBuilder strb = new StringBuilder();
+        for (JexlNode flat : flattened) {
+            strb.append(indent(flat));
+            strb.append(flat.getClass().getSimpleName());
+            if (flat.image != null) {
+                strb.append(" = ");
+                strb.append(flat.image);
+            }
+            strb.append("\n");
+        }
+        return strb.toString();
+    }
     /**
      * Dynamically runs a test method.
      * @param name the test method to run

Added: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl2/VarTest.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl2/VarTest.java?rev=1145359&view=auto
==============================================================================
--- commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl2/VarTest.java (added)
+++ commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl2/VarTest.java Mon Jul 11 21:17:11 2011
@@ -0,0 +1,170 @@
+/*
+ * 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.jexl2;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.logging.Logger;
+
+/**
+ * Tests local variables.
+ */
+public class VarTest extends JexlTestCase {
+    Logger LOGGER = Logger.getLogger(VarTest.class.getName());
+
+    public VarTest(String testName) {
+        super(testName);
+    }
+
+    public void testBasic() throws Exception {
+        Script e = JEXL.createScript("var x; x = 42");
+        Object o = e.execute(null);
+        assertEquals("Result is not 42", new Integer(42), o);
+    }
+
+    public void testSimple() throws Exception {
+        Script e = JEXL.createScript("var x = 21; x + x");
+        Object o = e.execute(null);
+        assertEquals("Result is not 42", new Integer(42), o);
+    }
+
+    public void testFor() throws Exception {
+        Script e = JEXL.createScript("var y  = 0; for(var x : [5, 17, 20]) { y = y + x; } y;");
+        Object o = e.execute(null);
+        assertEquals("Result is not 42", new Integer(42), o);
+    }
+
+    public static class NumbersContext extends MapContext implements NamespaceResolver {
+        public Object resolveNamespace(String name) {
+            return name == null ? this : null;
+        }
+
+        public Object numbers() {
+            return new int[]{5, 17, 20};
+        }
+    }
+
+    public void testForFunc() throws Exception {
+        JexlContext jc = new NumbersContext();
+        Script e = JEXL.createScript("var y  = 0; for(var x : numbers()) { y = y + x; } y;");
+        Object o = e.execute(jc);
+        assertEquals("Result is not 42", new Integer(42), o);
+    }
+
+    public void testForFuncReturn() throws Exception {
+        JexlContext jc = new NumbersContext();
+        Script e = JEXL.createScript("var y  = 42; for(var x : numbers()) { if (x > 10) return x } y;");
+        Object o = e.execute(jc);
+        assertEquals("Result is not 17", new Integer(17), o);
+        
+        assertTrue(toString(JEXL.getVariables(e)), JEXL.getVariables(e).isEmpty());
+    }
+
+    String toString(Set<List<String>> refs) {
+        StringBuilder strb = new StringBuilder("{");
+        int r = 0;
+        for (List<String> strs : refs) {
+            if (r++ > 0) {
+                strb.append(", ");
+            }
+            strb.append("{");
+            for (int s = 0; s < strs.size(); ++s) {
+                if (s > 0) {
+                    strb.append(", ");
+                }
+                strb.append('"');
+                strb.append(strs.get(s));
+                strb.append('"');
+            }
+            strb.append("}");
+        }
+        strb.append("}");
+        return strb.toString();
+    }
+    
+    Set<List<String>> mkref(String[][] refs) {
+        Set<List<String>> set = new HashSet<List<String>>();
+        for(String[] ref : refs) {
+            set.add(Arrays.asList(ref));
+        }
+        return set;
+    }
+    
+    boolean eq(Set<List<String>> lhs, Set<List<String>> rhs) {
+        if (lhs.size() != rhs.size()) {
+            return false;
+        }
+        for(List<String> ref : lhs) {
+            if (!rhs.contains(ref)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public void testRefs() throws Exception {
+        Script e;
+        Set<List<String>> vars;
+        Set<List<String>> expect;
+
+        e = JEXL.createScript("e[f]");
+        vars = JEXL.getVariables(e);
+        expect = mkref(new String[][]{{"e"},{"f"}});
+        assertTrue(eq(expect, vars));
+        
+        
+        e = JEXL.createScript("e[f][g]");
+        vars = JEXL.getVariables(e);
+        expect = mkref(new String[][]{{"e"},{"f"},{"g"}});
+        assertTrue(eq(expect, vars));
+
+        e = JEXL.createScript("e['f'].goo");
+        vars = JEXL.getVariables(e);
+        expect = mkref(new String[][]{{"e","f","goo"}});
+        assertTrue(eq(expect, vars));
+
+        e = JEXL.createScript("e['f']");
+        vars = JEXL.getVariables(e);
+        expect = mkref(new String[][]{{"e","f"}});
+        assertTrue(eq(expect, vars));
+
+        e = JEXL.createScript("e[f]['g']");
+        vars = JEXL.getVariables(e);
+        expect = mkref(new String[][]{{"e"},{"f"}});
+        assertTrue(eq(expect, vars));
+        
+        e = JEXL.createScript("e['f']['g']");
+        vars = JEXL.getVariables(e);
+        expect = mkref(new String[][]{{"e","f","g"}});
+        assertTrue(eq(expect, vars));
+
+        e = JEXL.createScript("a['b'].c['d'].e");
+        vars = JEXL.getVariables(e);
+        expect = mkref(new String[][]{{"a", "b", "c", "d", "e"}});
+        assertTrue(eq(expect, vars));
+
+        e = JEXL.createScript("a + b.c + b.c.d + e['f']");
+        //LOGGER.info(flattenedStr(e));
+        vars = JEXL.getVariables(e);
+        expect = mkref(new String[][]{{"a"}, {"b", "c"}, {"b", "c", "d"}, {"e", "f"}});
+        assertTrue(eq(expect, vars));
+    }
+
+
+}

Propchange: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl2/VarTest.java
------------------------------------------------------------------------------
    svn:eol-style = native



Mime
View raw message