You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by he...@apache.org on 2011/07/11 23:17:12 UTC
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...
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