You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by ra...@apache.org on 2009/07/15 05:43:50 UTC
svn commit: r794133 [1/2] - in /commons/proper/jexl/branches/2.0/src:
java/org/apache/commons/jexl/ java/org/apache/commons/jexl/parser/
java/org/apache/commons/jexl/util/introspection/
test/org/apache/commons/jexl/
Author: rahul
Date: Wed Jul 15 03:43:49 2009
New Revision: 794133
URL: http://svn.apache.org/viewvc?rev=794133&view=rev
Log:
Add an evaluator supporting syntax similar to the unified EL evaluator used in JSP/JSF, but based on JEXL (see UnifiedJEXL.java).
Add some tests for existing issues (see IssuesTest.java).
Some other miscellaneous improvements (catching up with JEXL-55 changes).
Thanks to Henri Biestro <hbiestro at gmail dot com>.
JEXL-58
Added:
commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/UnifiedJEXL.java (with props)
commons/proper/jexl/branches/2.0/src/test/org/apache/commons/jexl/IssuesTest.java (with props)
commons/proper/jexl/branches/2.0/src/test/org/apache/commons/jexl/UnifiedJEXLTest.java (with props)
Modified:
commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/Debugger.java
commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/Expression.java
commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/ExpressionImpl.java
commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/Interpreter.java
commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/JexlArithmetic.java
commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/JexlEngine.java
commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/JexlException.java
commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/ScriptImpl.java
commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/package.html
commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/parser/Parser.jjt
commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/ClassMap.java
commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/UberspectImpl.java
commons/proper/jexl/branches/2.0/src/test/org/apache/commons/jexl/ArithmeticTest.java
commons/proper/jexl/branches/2.0/src/test/org/apache/commons/jexl/AssignTest.java
commons/proper/jexl/branches/2.0/src/test/org/apache/commons/jexl/Jexl.java
Modified: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/Debugger.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/Debugger.java?rev=794133&r1=794132&r2=794133&view=diff
==============================================================================
--- commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/Debugger.java (original)
+++ commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/Debugger.java Wed Jul 15 03:43:49 2009
@@ -91,17 +91,19 @@
* @return true if the cause was located, false otherwise
*/
public boolean debug(Node cause) {
- builder = new StringBuilder();
- this.cause = cause;
- // make arg cause become the root cause
- Node root = cause;
- while (root.jjtGetParent() != null) {
- root = root.jjtGetParent();
- }
start = 0;
end = 0;
- root.jjtAccept(this, null);
- return start > 0;
+ if (cause != null) {
+ builder = new StringBuilder();
+ this.cause = cause;
+ // make arg cause become the root cause
+ Node root = cause;
+ while (root.jjtGetParent() != null) {
+ root = root.jjtGetParent();
+ }
+ root.jjtAccept(this, null);
+ }
+ return end > 0;
}
/**
Modified: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/Expression.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/Expression.java?rev=794133&r1=794132&r2=794133&view=diff
==============================================================================
--- commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/Expression.java (original)
+++ commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/Expression.java Wed Jul 15 03:43:49 2009
@@ -41,9 +41,9 @@
*
* @param context A JexlContext containing variables.
* @return The result of this evaluation
- * @throws Exception on any error
+ * @throws JexlException on any error
*/
- Object evaluate(JexlContext context) throws Exception;
+ Object evaluate(JexlContext context);
/**
* Returns the JEXL expression this Expression was created with.
Modified: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/ExpressionImpl.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/ExpressionImpl.java?rev=794133&r1=794132&r2=794133&view=diff
==============================================================================
--- commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/ExpressionImpl.java (original)
+++ commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/ExpressionImpl.java Wed Jul 15 03:43:49 2009
@@ -17,12 +17,10 @@
package org.apache.commons.jexl;
-import java.util.List;
-
import org.apache.commons.jexl.parser.SimpleNode;
/**
- * Instances of ExpressionImpl are created by the {@link ExpressionFactory},
+ * Instances of ExpressionImpl are created by the {@link JexlEngine},
* and this is the default implementation of the {@link Expression} interface.
*
* @since 1.0
@@ -30,30 +28,19 @@
* @version $Id$
*/
class ExpressionImpl implements Expression {
-
- // TODO: move resolving to interpreter
- /** resolvers called before expression evaluation. */
- protected List preResolvers;
-
- /** resolvers called after expression evaluation. */
- protected List postResolvers;
-
+ /** The engine for this expression. */
+ protected final JexlEngine jexl;
/**
* Original expression. This is just a 'snippet', not a valid statement
* (i.e. foo.bar() vs foo.bar();
*/
- protected String expression;
+ protected final String expression;
/**
- * The resulting AST we can call value() on.
+ * The resulting AST we can interpret.
*/
- protected SimpleNode node;
-
- /** The engine for this expression. */
- protected final JexlEngine jexl;
+ protected final SimpleNode node;
- /** The interpreter of the expression. */
- protected Interpreter interpreter;
/**
* do not let this be generally instantiated with a 'new'.
@@ -71,11 +58,14 @@
/**
* {@inheritDoc}
*/
- public Object evaluate(JexlContext context) throws Exception {
+ public Object evaluate(JexlContext context) {
Interpreter interpreter = jexl.createInterpreter(context);
- return interpreter.interpret(node, jexl.isSilent());
+ return interpreter.interpret(node);
}
-
+
+ /**
+ * {@inheritDoc}
+ */
public String dump() {
Debugger debug = new Debugger();
return debug.debug(node)? debug.toString() : "/*?*/";
@@ -93,6 +83,7 @@
*
* @return the expression or blank if it's null.
*/
+ @Override
public String toString() {
String expr = getExpression();
return expr == null ? "" : expr;
Modified: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/Interpreter.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/Interpreter.java?rev=794133&r1=794132&r2=794133&view=diff
==============================================================================
--- commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/Interpreter.java (original)
+++ commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/Interpreter.java Wed Jul 15 03:43:49 2009
@@ -26,6 +26,8 @@
import java.util.List;
import java.util.Map;
+import org.apache.commons.logging.*;
+
import org.apache.commons.jexl.parser.ASTAddNode;
import org.apache.commons.jexl.parser.ASTAndNode;
import org.apache.commons.jexl.parser.ASTArrayAccess;
@@ -74,61 +76,87 @@
import org.apache.commons.jexl.parser.ASTWhileStatement;
import org.apache.commons.jexl.parser.Node;
import org.apache.commons.jexl.parser.SimpleNode;
+import org.apache.commons.jexl.parser.ParserVisitor;
+
import org.apache.commons.jexl.util.introspection.Info;
import org.apache.commons.jexl.util.introspection.Uberspect;
import org.apache.commons.jexl.util.introspection.VelMethod;
import org.apache.commons.jexl.util.introspection.VelPropertyGet;
import org.apache.commons.jexl.util.introspection.VelPropertySet;
-import org.apache.commons.jexl.parser.ParserVisitor;
-
/**
* An interpreter of JEXL syntax.
*
* @since 2.0
*/
public class Interpreter implements ParserVisitor {
+ /** The logger. */
+ protected final Log LOG;
/** The uberspect. */
- private final Uberspect uberspect;
+ protected final Uberspect uberspect;
/** the arithmetic handler. */
- private final Arithmetic arithmetic;
+ protected final Arithmetic arithmetic;
/** The map of registered functions. */
- private final Map<String,Object> functions;
+ protected final Map<String,Object> functions;
/** The context to store/retrieve variables. */
- private final JexlContext context;
+ protected final JexlContext context;
+ /** silent */
+ protected boolean silent;
+ /** registers; 2 * {name, value} */
+ protected Object[] registers = null;
/** dummy velocity info. */
- private static final Info DUMMY = new Info("", 1, 1);
+ protected static final Info DUMMY = new Info("", 1, 1);
/** empty params for method matching. */
- static private final Object[] EMPTY_PARAMS = new Object[0];
-
+ protected static final Object[] EMPTY_PARAMS = new Object[0];
/**
* Create the interpreter.
- * @param ctx the context to retrieve variables from.
- * @param uber the helper to perform introspection,
- * @param arith the arithmetic handler
+ * @param jexl the engine creating this interpreter
+ * @param context the context to evaluate expression
*/
- public Interpreter(Uberspect uber, Arithmetic arith, Map<String,Object> functions, JexlContext context) {
- this.uberspect = uber;
- this.arithmetic = arith;
- this.functions = functions;
+ public Interpreter(JexlEngine jexl, JexlContext context) {
+ this.LOG = jexl.LOG;
+ this.uberspect = jexl.uberspect;
+ this.arithmetic = jexl.arithmetic;
+ this.functions = jexl.functions;
+ this.silent = jexl.silent;
this.context = context;
}
/**
+ * Sets whether this interpreter throws JexlException during evaluation.
+ * @param silent true means no JexlException will be thrown but will be logged
+ * as info through the Jexl engine logger, false allows them to be thrown.
+ */
+ public void setSilent(boolean silent) {
+ this.silent = silent;
+ }
+
+ /**
+ * Checks whether this interpreter throws JexlException during evaluation.
+ */
+ public boolean isSilent() {
+ return this.silent;
+ }
+
+ /**
* Interpret the given script/expression.
- *
+ * <p>
+ * If the underlying JEXL engine is silent, errors will be logged through its logger as info.
+ * </p>
* @param node the script or expression to interpret.
- * @param aContext the context to interpret against.
* @return the result of the interpretation.
+ * @throws JexlException if any error occurs during interpretation.
*/
- public Object interpret(SimpleNode node, boolean silent) {
+ public Object interpret(SimpleNode node) {
try {
return node.jjtAccept(this, null);
}
- catch (JexlException error) {
- if (silent)
+ catch(JexlException xjexl) {
+ if (silent) {
+ LOG.warn(xjexl.getMessage(), xjexl.getCause());
return null;
- throw error;
+ }
+ throw xjexl;
}
}
@@ -148,15 +176,28 @@
/**
* Gets the uberspect.
- *
* @return an {@link Uberspect}
*/
protected Uberspect getUberspect() {
return uberspect;
}
+ /**
+ * Sets this interpreter registers for bean access/assign expressions.
+ * @param registers the array of registers
+ */
+ protected void setRegisters(Object[] registers) {
+ this.registers = registers;
+ }
+
+
public Object visit(SimpleNode node, Object data) {
- throw new UnsupportedOperationException("unexpected node " + node);
+ int numChildren = node.jjtGetNumChildren();
+ Object result = null;
+ for (int i = 0; i < numChildren; i++) {
+ result = node.jjtGetChild(i).jjtAccept(this, data);
+ }
+ return result;
}
/** {@inheritDoc} */
@@ -216,7 +257,7 @@
return object;
}
-
+
/** {@inheritDoc} */
public Object visit(ASTAssignment node, Object data) {
// left contains the reference to assign to
@@ -388,6 +429,8 @@
return arithmetic.divide(left, right);
}
catch (RuntimeException xrt) {
+ if (silent && xrt instanceof ArithmeticException)
+ return 0.0;
throw new JexlException(node, "divide error", xrt);
}
}
@@ -509,6 +552,10 @@
public Object visit(ASTIdentifier node, Object data) {
String name = node.image;
if (data == null) {
+ if (registers != null) {
+ if (registers[0].equals(name)) return registers[1];
+ if (registers[2].equals(name)) return registers[3];
+ }
return context.getVars().get(name);
} else {
return getAttribute(data, name, node);
@@ -728,6 +775,8 @@
return arithmetic.mod(left, right);
}
catch (RuntimeException xrt) {
+ if (silent && xrt instanceof ArithmeticException)
+ return 0.0;
throw new JexlException(node, "% error", xrt);
}
}
@@ -824,7 +873,6 @@
/** {@inheritDoc} */
public Object visit(ASTReferenceExpression node, Object data) {
-
return node.jjtGetChild(0).jjtAccept(this, data);
}
@@ -1027,7 +1075,6 @@
* @param attribute the attribute of the object, e.g. an index (1, 0, 2) or
* key for a map
* @param value the value to assign to the object's attribute
- * @return the attribute.
*/
public void setAttribute(Object object, Object attribute, Object value) {
setAttribute(object, attribute, null);
Modified: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/JexlArithmetic.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/JexlArithmetic.java?rev=794133&r1=794132&r2=794133&view=diff
==============================================================================
--- commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/JexlArithmetic.java (original)
+++ commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/JexlArithmetic.java Wed Jul 15 03:43:49 2009
@@ -24,7 +24,13 @@
* @since 2.0
*/
class JexlArithmetic implements Arithmetic {
-
+ /**
+ * The result of +,/,-,* when both operands are null.
+ * @return Long(0)
+ */
+ protected Object NullNull() {
+ return 0l;
+ }
/**
* Add two values together.
* Rules are:<ol>
@@ -41,7 +47,7 @@
*/
public Object add(Object left, Object right) {
if (left == null && right == null) {
- return new Long(0);
+ return NullNull();
}
try {
@@ -76,6 +82,8 @@
return result;
} catch (java.lang.NumberFormatException nfe) {
// Well, use strings!
+ if (left == null) left = "";
+ else if (right == null) right = "";
return left.toString().concat(right.toString());
}
}
@@ -92,16 +100,13 @@
*/
public Object divide(Object left, Object right) {
if (left == null && right == null) {
- return new Long(0);
+ return NullNull();
}
// if both are bigintegers use that type
if (left instanceof BigInteger && right instanceof BigInteger) {
BigInteger l = toBigInteger(left);
BigInteger r = toBigInteger(right);
- if (r.compareTo(BigInteger.valueOf(0)) == 0) {
- return r;
- }
return l.divide(r);
}
@@ -109,18 +114,16 @@
if (left instanceof BigDecimal || right instanceof BigDecimal) {
BigDecimal l = toBigDecimal(left);
BigDecimal r = toBigDecimal(right);
- if (r.compareTo(BigDecimal.valueOf(0)) == 0) {
- return r;
- }
return l.divide(r, BigDecimal.ROUND_HALF_UP);
}
double l = toDouble(left);
double r = toDouble(right);
- if (r == 0) {
- return new Double(r);
+ double d = l / r;
+ if (Double.isNaN(d) || Double.isInfinite(d)) {
+ throw new ArithmeticException("/");
}
- return new Double(l / r);
+ return d;
}
@@ -136,22 +139,23 @@
*/
public Object mod(Object left, Object right) {
if (left == null && right == null) {
- return new Long(0);
+ return NullNull();
}
if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
double l = toDouble(left);
double r = toDouble(right);
- return new Double(l % r);
+ double d = l % r;
+ if (Double.isNaN(d) || Double.isInfinite(d)) {
+ throw new ArithmeticException("%");
+ }
+ return d;
}
// if both are bigintegers use that type
if (left instanceof BigInteger && right instanceof BigInteger) {
BigInteger l = toBigInteger(left);
BigInteger r = toBigInteger(right);
- if (r.compareTo(BigInteger.valueOf(0)) == 0) {
- return r;
- }
return l.mod(r);
}
@@ -159,9 +163,6 @@
if (left instanceof BigDecimal || right instanceof BigDecimal) {
BigDecimal l = toBigDecimal(left);
BigDecimal r = toBigDecimal(right);
- if (r.compareTo(BigDecimal.valueOf(0)) == 0) {
- return r;
- }
BigInteger intDiv = l.divide(r, BigDecimal.ROUND_HALF_UP).toBigInteger();
BigInteger intValue = (r.multiply(new BigDecimal(intDiv))).toBigInteger();
BigDecimal remainder = new BigDecimal(l.subtract(new BigDecimal(intValue)).toBigInteger());
@@ -195,7 +196,7 @@
*/
public Object multiply(Object left, Object right) {
if (left == null && right == null) {
- return new Long(0);
+ return NullNull();
}
if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
@@ -245,7 +246,7 @@
*/
public Object subtract(Object left, Object right) {
if (left == null && right == null) {
- return new Long(0);
+ return NullNull();
}
if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
Modified: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/JexlEngine.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/JexlEngine.java?rev=794133&r1=794132&r2=794133&view=diff
==============================================================================
--- commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/JexlEngine.java (original)
+++ commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/JexlEngine.java Wed Jul 15 03:43:49 2009
@@ -22,9 +22,9 @@
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringReader;
+import java.io.Reader;
import java.util.Map;
import java.util.Collections;
-import java.util.HashMap;
import java.net.URL;
import java.net.URLConnection;
import org.apache.commons.logging.*;
@@ -68,17 +68,35 @@
* Whether expressions evaluated by this engine will throw exceptions or
* return null
*/
- boolean silent = true;
+ protected boolean silent = true;
/**
* The map of 'prefix:function' to object implementing the function.
*/
protected Map<String,Object> functions = Collections.EMPTY_MAP;
+
+ /**
+ * The expression cache.
+ */
+ protected Map<String,SimpleNode> cache = null;
+
/**
* ExpressionFactory & ScriptFactory need a singleton and this is the package
* instance fulfilling that pattern.
*/
- static final JexlEngine DEFAULT = new JexlEngine();
+ protected static final JexlEngine DEFAULT = new JexlEngine();
+
+ /**
+ * An empty/static/non-mutable JexlContext used instead of null context
+ */
+ protected static final JexlContext EMPTY_CONTEXT = new JexlContext() {
+ public void setVars(Map vars) {
+ throw new UnsupportedOperationException("Immutable JexlContext");
+ }
+ public Map getVars() {
+ return java.util.Collections.EMPTY_MAP;
+ }
+ };
/**
* Creates a default engine
@@ -97,12 +115,15 @@
public JexlEngine(Uberspect uberspect, Arithmetic arithmetic, Map<String,Object> funcs, Log log) {
this.uberspect = uberspect == null? Introspector.getUberspect() : uberspect;
this.arithmetic = arithmetic == null? new JexlArithmetic() : arithmetic;
- if (funcs != null)
+ if (funcs != null) {
this.functions = funcs;
- if (log == null)
+ }
+ if (log == null) {
log = LogFactory.getLog(JexlEngine.class);
- if (log == null)
+ }
+ if (log == null) {
throw new NullPointerException("logger can not be null");
+ }
this.LOG = log;
}
@@ -120,11 +141,27 @@
public boolean isSilent() {
return this.silent;
}
-
+
+ /**
+ * Sets a cache of the defined size for expressions.
+ * @param size if not strictly positive, no cache is used.
+ */
+ public void setCache(int size) {
+ // since the cache is only used during parse, use same sync object
+ synchronized(parser) {
+ if (size <= 0) {
+ cache = null;
+ }
+ else if (cache == null || cache.size() != size) {
+ cache = createCache(size);
+ }
+ }
+ }
+
/**
* Sets the map of function namespaces.
* <p>
- * It should be defined once not modified afterwards since it might be shared
+ * It should be defined once and not modified afterwards since it might be shared
* between multiple engines evaluating expressions concurrently.
* </p>
* <p>
@@ -159,7 +196,7 @@
public Map<String, Object> getFunctions() {
return functions;
}
-
+
/**
* Creates an Expression from a String containing valid
* JEXL syntax. This method parses the expression which
@@ -172,33 +209,14 @@
*/
public Expression createExpression(String expression)
throws ParseException {
- String expr = cleanExpression(expression);
-
- // Parse the Expression
- SimpleNode tree;
- synchronized (parser) {
- LOG.debug("Parsing expression: " + expr);
- try {
- tree = parser.parse(new StringReader(expr));
- } catch (TokenMgrError tme) {
- throw new ParseException(tme.getMessage());
- } catch (ParseException e) {
- throw e;
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
+ // Parse the expression
+ SimpleNode tree = parse(expression);
if (tree.jjtGetNumChildren() > 1) {
LOG.warn("The JEXL Expression created will be a reference"
+ " to the first expression from the supplied script: \""
+ expression + "\" ");
}
-
- // Must be a simple reference, expression, statement or if, otherwise
- // throw an exception.
SimpleNode node = (SimpleNode) tree.jjtGetChild(0);
-
return new ExpressionImpl(this, expression, node);
}
@@ -213,22 +231,14 @@
* problem parsing the script.
*/
public Script createScript(String scriptText) throws Exception {
- String cleanText = cleanExpression(scriptText);
- SimpleNode script;
- // Parse the Expression
- synchronized (parser) {
- LOG.debug("Parsing script: " + cleanText);
- try {
- script = parser.parse(new StringReader(cleanText));
- } catch (TokenMgrError tme) {
- throw new ParseException(tme.getMessage());
- }
+ if (scriptText == null) {
+ throw new NullPointerException("scriptText is null");
}
+ SimpleNode script = parse(scriptText);
if (script instanceof ASTJexlScript) {
- return new ScriptImpl(this, cleanText, (ASTJexlScript) script);
+ return new ScriptImpl(this, scriptText, (ASTJexlScript) script);
} else {
- throw new IllegalStateException("Parsed script is not "
- + "an ASTJexlScript");
+ throw new IllegalStateException("Parsed script is not an ASTJexlScript");
}
}
@@ -277,24 +287,178 @@
new InputStreamReader(connection.getInputStream()));
return createScript(readerToString(reader));
}
+
+ /**
+ * Access properties of a bean using an expression.
+ * <p>
+ * jexl.get(myobject, "foo.bar"); should equate to
+ * myobject.getFoo().getBar(); (or myobject.getFoo().get("bar"))
+ * </p>
+ * <p>
+ * If the JEXL engine is silent, errors will be logged through its logger as info.
+ * </p>
+ * @param bean the bean to get properties from
+ * @param expr the property expression
+ * @return the value of the property
+ */
+ public Object getProperty(Object bean, String expr) {
+ return getProperty(EMPTY_CONTEXT, bean, expr);
+ }
+ public Object getProperty(JexlContext context, Object bean, String expr) {
+ Map<String, Object> vars = context.getVars();
+ // lets build 1 unique & unused identifiers wrt context
+ String r0 = "$0";
+ for(int s = 0; vars.containsKey(r0); ++s) r0 = r0 + s;
+ expr = r0 + (expr.charAt(0) =='['? "" : "." )+ expr +";";
+ try {
+ SimpleNode tree = parse(expr);
+ SimpleNode node = (SimpleNode) tree.jjtGetChild(0).jjtGetChild(0);
+ Interpreter interpreter = createInterpreter(context);
+ // ensure 4 objects in register array
+ Object[] r = {r0, bean, r0, bean};
+ interpreter.setRegisters(r);
+ return node.jjtAccept(interpreter, null);
+ }
+ catch(JexlException xjexl) {
+ if (silent) {
+ LOG.warn(xjexl.getMessage(), xjexl.getCause());
+ return null;
+ }
+ throw xjexl;
+ }
+ catch(ParseException xparse) {
+ if (silent) {
+ LOG.warn(xparse.getMessage(), xparse.getCause());
+ return null;
+ }
+ throw new RuntimeException(xparse);
+ }
+ }
+
+ /**
+ * Assign properties of a bean using an expression.
+ * <p>
+ * jexl.set(myobject, "foo.bar", 10); should equate to
+ * myobject.getFoo().setBar(10); (or myobject.getFoo().put("bar", 10) )
+ * </p>
+ * <p>
+ * If the JEXL engine is silent, errors will be logged through its logger as info.
+ * </p>
+ * @param bean the bean to set properties in
+ * @param expr the property expression
+ * @param value the value of the property
+ */
+ public void setProperty(Object bean, String expr, Object value) {
+ setProperty(EMPTY_CONTEXT, bean, expr, value);
+ }
+ public void setProperty(JexlContext context, Object bean, String expr, Object value) {
+ Map<String, Object> vars = context.getVars();
+ // lets build 2 unique & unused identifiers wrt context
+ String r0 = "$0", r1 = "$1";
+ for(int s = 0; vars.containsKey(r0); ++s) r0 = r0 + s;
+ for(int s = 0; vars.containsKey(r1); ++s) r1 = r1 + s;
+ // synthetize expr
+ expr = r0 + (expr.charAt(0) =='['? "" : "." )+ expr +"=" + r1 +";";
+ try {
+ SimpleNode tree = parse(expr);
+ SimpleNode node = (SimpleNode) tree.jjtGetChild(0).jjtGetChild(0);
+ Interpreter interpreter = createInterpreter(context);
+ // set the registers
+ Object[] r = {r0, bean, r1, value};
+ interpreter.setRegisters(r);
+ node.jjtAccept(interpreter, null);
+ }
+ catch(JexlException xjexl) {
+ if (silent) {
+ LOG.warn(xjexl.getMessage(), xjexl.getCause());
+ return;
+ }
+ throw xjexl;
+ }
+ catch(ParseException xparse) {
+ if (silent) {
+ LOG.warn(xparse.getMessage(), xparse.getCause());
+ return;
+ }
+ throw new RuntimeException(xparse);
+ }
+ }
/**
- * Creates an interpreter
+ * Creates an interpreter.
+ * @param context a JexlContext; if null, the EMPTY_CONTEXT is used instead.
+ * @return an Interpreter
*/
protected Interpreter createInterpreter(JexlContext context) {
- return new Interpreter(uberspect, arithmetic, functions, context);
+ if (context == null) context = EMPTY_CONTEXT;
+ return new Interpreter(this, context);
+ }
+
+ /**
+ * Creates a SimpleNode cache.
+ * @param cacheSize the cache size, must be > 0
+ * @return a Map usable as a cache bounded to the given size
+ */
+ protected Map<String,SimpleNode> createCache(final int cacheSize) {
+ return new java.util.LinkedHashMap<String,SimpleNode>(cacheSize, 0.75f, true) {
+ @Override
+ protected boolean removeEldestEntry(Map.Entry eldest) {
+ return size() > cacheSize;
+ }
+ };
}
+
/**
- * Trims the expression and adds a semi-colon if missing.
- * @param expression to clean
+ * Parses an expression.
+ * @param expression
+ * @return the parsed tree
+ * @throws ParseException if any error occured during parsing
+ */
+ protected SimpleNode parse(CharSequence expression) throws ParseException {
+ String expr = cleanExpression(expression);
+ SimpleNode tree = null;
+ synchronized (parser) {
+ LOG.debug("Parsing expression: " + expression);
+ if (cache != null) {
+ tree = cache.get(expr);
+ if (tree != null) {
+ return tree;
+ }
+ }
+ try {
+ Reader reader = expr.endsWith(";")?
+ new StringReader(expr) :
+ new StringReader(expr + ";");
+ tree = parser.parse(reader);
+ if (cache != null) {
+ cache.put(expr, tree);
+ }
+ } catch (TokenMgrError tme) {
+ throw new ParseException(tme.getMessage());
+ } catch (ParseException e) {
+ throw e;
+ }
+ }
+ return tree;
+ }
+
+ /**
+ * Trims the expression from front & ending spaces.
+ * @param str expression to clean
* @return trimmed expression ending in a semi-colon
*/
- protected String cleanExpression(String expression) {
- String expr = expression.trim();
- if (!expr.endsWith(";")) {
- expr += ";";
+ protected String cleanExpression(CharSequence str) {
+ if (str != null) {
+ int start = 0;
+ int end = str.length();
+ if (end > 0) {
+ for(start = 0; start < end && str.charAt(start) == ' '; ++start); // trim front spaces
+ for(;end > 0 && str.charAt(end - 1) == ' '; --end); // trim ending spaces
+ return str.subSequence(start, end).toString();
+ }
+ return "";
}
- return expr;
+ return null;
}
/**
@@ -306,7 +470,7 @@
*/
protected static String readerToString(BufferedReader reader)
throws IOException {
- StringBuffer buffer = new StringBuffer();
+ StringBuilder buffer = new StringBuilder();
try {
String line;
while ((line = reader.readLine()) != null) {
Modified: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/JexlException.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/JexlException.java?rev=794133&r1=794132&r2=794133&view=diff
==============================================================================
--- commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/JexlException.java (original)
+++ commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/JexlException.java Wed Jul 15 03:43:49 2009
@@ -26,6 +26,7 @@
public JexlException(Node node, String msg) {
super(msg);
+ mark = node;
}
public JexlException(Node node, String msg, Throwable cause) {
@@ -60,7 +61,8 @@
* - msg is the actual explanation message for this error
* @return this error as a string
*/
- public String toString() {
+ @Override
+ public String getMessage() {
Debugger dbg = new Debugger();
StringBuilder msg = new StringBuilder();
if (dbg.debug(mark)) {
@@ -72,7 +74,7 @@
msg.append(dbg.data());
msg.append("\n");
}
- msg.append(super.toString());
+ msg.append(super.getMessage());
return msg.toString();
}
}
Modified: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/ScriptImpl.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/ScriptImpl.java?rev=794133&r1=794132&r2=794133&view=diff
==============================================================================
--- commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/ScriptImpl.java (original)
+++ commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/ScriptImpl.java Wed Jul 15 03:43:49 2009
@@ -43,17 +43,11 @@
jexl = engine;
}
- /**
- * {@inheritDoc}
- */
public Object execute(JexlContext context) throws Exception {
Interpreter interpreter = jexl.createInterpreter(context);
- return interpreter.interpret(parsedScript, jexl.isSilent());
+ return interpreter.interpret(parsedScript);
}
- /**
- * {@inheritDoc}
- */
public String getText() {
return text;
}
Added: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/UnifiedJEXL.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/UnifiedJEXL.java?rev=794133&view=auto
==============================================================================
--- commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/UnifiedJEXL.java (added)
+++ commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/UnifiedJEXL.java Wed Jul 15 03:43:49 2009
@@ -0,0 +1,948 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl;
+
+import java.util.Map;
+import java.util.LinkedHashMap;
+import java.util.ArrayList;
+import org.apache.commons.jexl.parser.SimpleNode;
+import org.apache.commons.jexl.parser.ParseException;
+
+/**
+ * An evaluator similar to the unified EL evaluator used in JSP/JSF based on JEXL.
+ * It is intended to be used in configuration modules, XML based frameworks or JSP taglibs
+ * and facilitate the implementation of expression evaluation.
+ * <p>
+ * An expression can mix immediate, deferred and nested sub-expressions as well as string constants;<ol>
+ * <li>The "immediate" syntax is of the form "...${jexl-expr}..."</li>
+ * <li>The "deferred" syntax is of the form "...#{jexl-expr}..."</li>
+ * <li>The "nested" syntax is of the form "...#{...${jexl-expr0}...}..."</li>
+ * <li>The "composite" syntax is of the form "...${jexl-expr0}... #{jexl-expr1}..."</li>
+ * </ol>
+ * </p>
+ * <p>
+ * Deferred & immediate expression carry different intentions:
+ * <ol>
+ * <li>An immediate expression indicate that evaluation is intended to be performed close to
+ * the definition/parsing point.</li>
+ * <li>A deferred expression indicate that evaluation is intended to occur at a later stage.</li>
+ * </ol>
+ * </p>
+ * <p>
+ * For instance: "Hello ${name}, now is #{time}" is a composite "deferred" expression since one
+ * of its subexpressions is deferred. Furthermore, this (composite) expression intent is
+ * to perform two evaluations; one close to its definition and another one in a later
+ * phase.
+ * </p>
+ * <p>
+ * The API reflects this feature in 2 methods, prepare and evaluate. The prepare method
+ * will evaluate the immediate subexpression and return an expression that contains only
+ * the deferred subexpressions (& constants), a prepared expression. Such a prepared expression
+ * is suitable for a later phase evaluation that may occur with a different JexlContext.
+ * Note that it is valid to call evaluate without prepare in which case the same JexlContext
+ * is used for the 2 evaluation phases.
+ * </p>
+ * <p>
+ * In the most common use-case where deferred expressions are to be kept around as properties of objects,
+ * one should parse & prepare an expression before storing it and evaluate it each time
+ * the property storing it is accessed.
+ * </p>
+ * <p>
+ * Note that nested expression use the JEXL syntax as in:
+ * <code>"#{${bar}+'.charAt(2)'}"</code>
+ * The most common mistake leading to an invalid expression being the following:
+ * <code>"#{${bar}charAt(2)}"</code>
+ * </p>
+ */
+public class UnifiedJEXL {
+ /** The engine for this expression. */
+ private final JexlEngine jexl;
+ /** The expression cache. */
+ private final Map<String, Expression> cache;
+
+ /**
+ * Creates a new instance of UnifiedJEXL with a default size cache.
+ * @param jexl the JexlEngine to use.
+ */
+ public UnifiedJEXL(JexlEngine jexl) {
+ this(jexl, 512);
+ }
+
+ /**
+ * Creates a new instance of UnifiedJEXL creating a local cache.
+ * @param jexl the JexlEngine to use.
+ * @param cacheSize the number of expressions in this cache
+ */
+ public UnifiedJEXL(JexlEngine jexl, int cacheSize) {
+ this.jexl = jexl;
+ this.cache = cacheSize > 0 ? createCache(cacheSize) : null;
+ }
+
+ /**
+ * Creates an expression cache.
+ * @param size the cache size, must be > 0
+ * @return a LinkedHashMap
+ */
+ static private Map<String, Expression> createCache(final int cacheSize) {
+ return new LinkedHashMap<String, Expression>(cacheSize, 0.75f, true) {
+ @Override
+ protected boolean removeEldestEntry(Map.Entry eldest) {
+ return size() > cacheSize;
+ }
+ };
+ }
+
+ /**
+ * Types of expressions.
+ * Each instance carries a counter index per (composite sub-) expression type.
+ * @see ExpressionBuilder
+ */
+ private static enum ExpressionType {
+ CONSTANT(0), // constant count at index 0
+ IMMEDIATE(1), // immediate count at index 1
+ DEFERRED(2), // deferred count at index 2
+ NESTED(2), // nested are counted as deferred thus count at index 2
+ COMPOSITE(-1); // composite are not counted
+ /** the index in arrays of expression counters for composite expressions. */
+ private final int index;
+ ExpressionType(int index) {
+ this.index = index;
+ }
+ }
+
+ /**
+ * A helper class to build expressions.
+ * Keeps count of sub-expressions by type.
+ */
+ private static class ExpressionBuilder {
+ final int[] counts;
+ final ArrayList<Expression> expressions;
+
+ ExpressionBuilder(int size) {
+ counts = new int[]{0, 0, 0};
+ expressions = new ArrayList<Expression>(size <= 0? 3 : size);
+ }
+
+ /**
+ * Adds an expression to the list of expressions, maintain per-type counts.
+ * @param expr the expression to add
+ */
+ void add(Expression expr) {
+ counts[expr.getType().index] += 1;
+ expressions.add(expr);
+ }
+
+ /**
+ * Builds an expression from a source, performs checks
+ * @param el the unified el instance
+ * @param source the source expression
+ * @return an expression
+ */
+ Expression build(UnifiedJEXL el, Expression source) {
+ int sum = 0;
+ for(int i : counts) sum += i;
+ if (expressions.size() != sum) {
+ String error = "parsing algorithm error, exprs: " + expressions.size() +
+ ", constant:" + counts[ExpressionType.CONSTANT.index] +
+ ", immediate:" + counts[ExpressionType.IMMEDIATE.index] +
+ ", deferred:" + counts[ExpressionType.DEFERRED.index];
+ throw new IllegalStateException(error);
+ }
+ // if only one sub-expr, no need to create a composite
+ return (expressions.size() == 1)?
+ expressions.get(0) :
+ el.new Composite(counts, expressions, source);
+ }
+ }
+
+
+ /**
+ * Gets the JexlEngine underlying the UnifiedJEXL.
+ * @return the JexlEngine
+ */
+ public JexlEngine getEngine() {
+ return jexl;
+ }
+
+ /**
+ * The sole type of (runtime) exception the UnifiedJEXL can throw.
+ */
+ public class Exception extends RuntimeException {
+ public Exception(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+ }
+
+ /**
+ * The abstract base class for all expressions, immediate '${...}' and deferred '#{...}'.
+ */
+ public abstract class Expression {
+ final Expression source;
+
+ Expression(Expression source) {
+ this.source = source != null ? source : this;
+ }
+
+ /**
+ * Generate the string corresponding to this expression.
+ * @return the string representation
+ */
+ public String asString() {
+ return toString();
+ }
+
+ /**
+ * When the expression is dependant upon immediate and deferred sub-expressions,
+ * evaluates the immediate sub-expressions with the context passed as parameter
+ * and returns this expression deferred form.
+ * <p>
+ * In effect, this binds the result of the immediate sub-expressions evaluation in the
+ * context, allowing to differ evaluation of the remaining (deferred) expression within another context.
+ * This only has an effect to nested & composite expressions that contain differed & immediate sub-expressions.
+ * </p>
+ * <p>
+ * If the underlying JEXL engine is silent, errors will be logged through its logger as info.
+ * </p>
+ * @param context the context to use for immediate expression evaluations
+ * @return an expression or null if an error occurs and the {@link JexlEngine} is silent
+ * @throws {@link Exception} if any error occurs and the {@link JexlEngine} is not silent
+ */
+ public abstract Expression prepare(JexlContext context);
+
+ /**
+ * Evaluates this expression.
+ * <p>
+ * If the underlying JEXL engine is silent, errors will be logged through its logger as info.
+ * </p>
+ * @param context the variable context
+ * @return the result of this expression evaluation or null if an error occurs and the {@link JexlEngine} is silent
+ * @throws [@link Exception} if an error occurs and the {@link JexlEngine} is not silent
+ */
+ public abstract Object evaluate(JexlContext context);
+
+ /**
+ * Checks whether this expression is immediate.
+ * @return true if immediate, false otherwise
+ */
+ public boolean isImmediate() {
+ return true;
+ }
+
+ /**
+ * Checks whether this expression is deferred.
+ * @return true if deferred, false otherwise
+ */
+ public final boolean isDeferred() {
+ return !isImmediate();
+ }
+
+ /**
+ * Retrieves this expression's source expression.
+ * If this expression was prepared, this allows to retrieve the
+ * original expression that lead to it.
+ * Other expressions return themselves.
+ * @return the source expression
+ */
+ public final Expression getSource() {
+ return source;
+ }
+
+ /**
+ * Gets this expression type.
+ * @return its type
+ */
+ abstract ExpressionType getType();
+
+ /**
+ * Prepares a sub-expression for interpretation.
+ * @param interpreter a JEXL interpreter
+ * @return a prepared expression
+ * @throws ParseException (only for nested & composite)
+ */
+ abstract Expression prepare(Interpreter interpreter) throws ParseException;
+
+ /**
+ * Intreprets a sub-expression.
+ * @param interpreter a JEXL interpreter
+ * @return the result of interpretation
+ * @throws ParseException (only for nested & composite)
+ */
+ abstract Object evaluate(Interpreter interpreter) throws ParseException;
+ }
+
+
+ /** A constant expression. */
+ private class Constant extends Expression {
+ private final Object value;
+
+ Constant(Object value, Expression source) {
+ super(source);
+ if (value == null) {
+ throw new NullPointerException("constant can not be null");
+ }
+ this.value = value;
+ }
+
+ ExpressionType getType() {
+ return ExpressionType.CONSTANT;
+ }
+
+ @Override
+ public String toString() {
+ String str = value.toString();
+ if (value instanceof String || value instanceof CharSequence) {
+ StringBuilder strb = new StringBuilder(str.length() + 2);
+ if (source != this) {
+ strb.append(source.toString());
+ strb.append(" /*= ");
+ }
+ strb.append('"');
+ for (int i = 0, size = str.length(); i < size; ++i) {
+ char c = str.charAt(i);
+ if (c == '"')
+ strb.append('\\');
+ strb.append(c);
+ }
+ strb.append('"');
+ if (source != this) {
+ strb.append(" */");
+ }
+ return strb.toString();
+ }
+ return str;
+ }
+
+ @Override
+ public String asString() {
+ return value.toString();
+ }
+
+ @Override
+ public Expression prepare(JexlContext context) {
+ return this;
+ }
+
+ @Override
+ Expression prepare(Interpreter interpreter) throws ParseException {
+ return this;
+ }
+
+ @Override
+ public Object evaluate(JexlContext context) {
+ return value;
+ }
+
+ @Override
+ Object evaluate(Interpreter interpreter) throws ParseException {
+ return value;
+ }
+
+ }
+
+
+ /** An immediate expression: ${jexl}. */
+ private class Immediate extends Expression {
+ private final CharSequence expr;
+ private final SimpleNode node;
+
+ Immediate(CharSequence expr, SimpleNode node, Expression source) {
+ super(source);
+ this.expr = expr;
+ this.node = node;
+ }
+
+ @Override
+ ExpressionType getType() {
+ return ExpressionType.IMMEDIATE;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder strb = new StringBuilder(expr.length() + 3);
+ if (source != this) {
+ strb.append(source.toString());
+ strb.append(" /*= ");
+ }
+ strb.append("${");
+ strb.append(expr);
+ strb.append("}");
+ if (source != this) {
+ strb.append(" */");
+ }
+ return strb.toString();
+ }
+
+ @Override
+ public String asString() {
+ return expr.toString();
+ }
+
+ @Override
+ public Expression prepare(JexlContext context) {
+ return this;
+ }
+
+ @Override
+ Expression prepare(Interpreter interpreter) throws ParseException {
+ return this;
+ }
+
+ @Override
+ public Object evaluate(JexlContext context) {
+ return UnifiedJEXL.this.evaluate(context, this);
+ }
+
+ @Override
+ Object evaluate(Interpreter interpreter) throws ParseException {
+ return interpreter.interpret(node);
+ }
+
+ }
+
+
+ /** A deferred expression: #{jexl}. */
+ private class Deferred extends Expression {
+ protected final CharSequence expr;
+ protected final SimpleNode node;
+
+ Deferred(CharSequence expr, SimpleNode node, Expression source) {
+ super(source);
+ this.expr = expr.toString();
+ this.node = node;
+ }
+
+ @Override
+ public boolean isImmediate() {
+ return false;
+ }
+
+ ExpressionType getType() {
+ return ExpressionType.DEFERRED;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder strb = new StringBuilder(expr.length() + 3);
+ if (source != this) {
+ strb.append(source.toString());
+ strb.append(" /*= ");
+ }
+ strb.append("#{");
+ strb.append(expr);
+ strb.append("}");
+ if (source != this) {
+ strb.append(" */");
+ }
+ return strb.toString();
+ }
+
+ @Override
+ public String asString() {
+ return expr.toString();
+ }
+
+ @Override
+ public Expression prepare(JexlContext context) {
+ return this;
+ }
+
+ @Override
+ Expression prepare(Interpreter interpreter) throws ParseException {
+ return this;
+ }
+
+ @Override
+ public Object evaluate(JexlContext context) {
+ return UnifiedJEXL.this.evaluate(context, this);
+ }
+
+ @Override
+ Object evaluate(Interpreter interpreter) throws ParseException {
+ return interpreter.interpret(node);
+ }
+ }
+
+
+ /**
+ * A deferred expression that nests an immediate expression.
+ * #{...${jexl}...}
+ * Note that the deferred syntax is JEXL's, not UnifiedJEXL.
+ */
+ private class Nested extends Deferred {
+ Nested(CharSequence expr, SimpleNode node, Expression source) {
+ super(expr, node, source);
+ if (this.source != this) {
+ throw new IllegalArgumentException("Nested expression can not have a source");
+ }
+ }
+
+ @Override
+ ExpressionType getType() {
+ return ExpressionType.NESTED;
+ }
+
+ @Override
+ public String toString() {
+ return expr.toString();
+ }
+
+ @Override
+ public Expression prepare(JexlContext context) {
+ return UnifiedJEXL.this.prepare(context, this);
+ }
+
+ @Override
+ public Expression prepare(Interpreter interpreter) throws ParseException {
+ String value = interpreter.interpret(node).toString();
+ SimpleNode dnode = toNode(value);
+ return new Deferred(value, dnode, this);
+ }
+
+ @Override
+ public Object evaluate(Interpreter interpreter) throws ParseException {
+ return prepare(interpreter).evaluate(interpreter);
+ }
+ }
+
+
+ /** A composite expression: "... ${...} ... #{...} ...". */
+ private class Composite extends Expression {
+ // bit encoded (deferred count > 0) bit 1, (immediate count > 0) bit 0
+ final int meta;
+ // the list of expression resulting from parsing
+ final Expression[] exprs;
+
+ Composite(int[] counts, ArrayList<Expression> exprs, Expression source) {
+ super(source);
+ this.exprs = exprs.toArray(new Expression[exprs.size()]);
+ this.meta = (counts[ExpressionType.DEFERRED.index] > 0? 2 : 0) |
+ (counts[ExpressionType.IMMEDIATE.index] > 0? 1 : 0);
+ }
+
+ int size() {
+ return exprs.length;
+ }
+
+ ExpressionType getType() {
+ return ExpressionType.COMPOSITE;
+ }
+
+ @Override
+ public boolean isImmediate() {
+ // immediate if no deferred
+ return (meta & 2) != 0;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder strb = new StringBuilder();
+ if (source != this) {
+ strb.append(source.toString());
+ strb.append(" /*= ");
+ }
+ for (Expression e : exprs) {
+ strb.append(e.toString());
+ }
+ if (source != this) {
+ strb.append(" */ ");
+ }
+ return strb.toString();
+ }
+
+ @Override
+ public String asString() {
+ StringBuilder strb = new StringBuilder();
+ for (Expression e : exprs) {
+ strb.append(e.asString());
+ }
+ return strb.toString();
+ }
+
+ @Override
+ public Expression prepare(JexlContext context) {
+ return UnifiedJEXL.this.prepare(context, this);
+ }
+
+ @Override
+ Expression prepare(Interpreter interpreter) throws ParseException {
+ // if this composite is not its own source, it is already prepared
+ if (source != this) return this;
+ // we need to eval immediate expressions if there are some deferred/nested
+ // ie both immediate & deferred counts > 0, bits 1 & 0 set, (1 << 1) & 1 == 3
+ final boolean evalImmediate = meta == 3;
+ final int size = size();
+ final ExpressionBuilder builder = new ExpressionBuilder(size);
+ // tracking whether prepare will return a different expression
+ boolean eq = true;
+ for(int e = 0; e < size; ++e) {
+ Expression expr = exprs[e];
+ Expression prepared = expr.prepare(interpreter);
+ if (evalImmediate && prepared instanceof Immediate) {
+ // evaluate immediate as constant
+ Object value = prepared.evaluate(interpreter);
+ prepared = value == null? null : new Constant(value, prepared);
+ }
+ // add it if not null
+ if (prepared != null) {
+ builder.add(prepared);
+ }
+ // keep track of expression equivalence
+ eq &= expr == prepared;
+ }
+ Expression ready = eq? this : builder.build(UnifiedJEXL.this, this);
+ return ready;
+ }
+
+
+ @Override
+ public Object evaluate(JexlContext context) {
+ return UnifiedJEXL.this.evaluate(context, this);
+ }
+
+ @Override
+ Object evaluate(Interpreter interpreter) throws ParseException {
+ final int size = size();
+ Object value = null;
+ // common case: evaluate all expressions & concatenate them as a string
+ StringBuilder strb = new StringBuilder();
+ for (int e = 0; e < size; ++e) {
+ value = exprs[e].evaluate(interpreter);
+ if (value != null) {
+ strb.append(value.toString());
+ }
+ }
+ value = strb.toString();
+ return value;
+ }
+ }
+
+ /** Creates a a {@link UnifiedJEXL.Expression} from an expression string.
+ * Uses & fills up the expression cache if any.
+ * <p>
+ * If the underlying JEXL engine is silent, errors will be logged through its logger as info.
+ * </p>
+ * @param expression the UnifiedJEXL string expression
+ * @return the UnifiedJEXL object expression, null if silent and an error occured
+ * @throws [@link Exception} if an error occurs and the {@link JexlEngine} is not silent
+ */
+ public Expression parse(String expression) {
+ try {
+ if (cache == null) {
+ return parseExpression(expression);
+ }
+ else synchronized (cache) {
+ Expression stmt = cache.get(expression);
+ if (stmt == null) {
+ stmt = parseExpression(expression);
+ cache.put(expression, stmt);
+ }
+ return stmt;
+ }
+ }
+ catch(JexlException xjexl) {
+ Exception xuel = new Exception("failed to parse '" + expression + "'", xjexl);
+ if (jexl.isSilent()) {
+ jexl.LOG.warn(xuel.getMessage(), xuel.getCause());
+ return null;
+ }
+ throw xuel;
+ }
+ catch(ParseException xparse) {
+ Exception xuel = new Exception("failed to parse '" + expression + "'", xparse);
+ if (jexl.isSilent()) {
+ jexl.LOG.warn(xuel.getMessage(), xuel.getCause());
+ return null;
+ }
+ throw xuel;
+ }
+ }
+
+ /**
+ * Prepares an expression (nested & composites), handles exception reporting.
+ *
+ * @param context the JEXL context to use
+ * @param expr the expression to prepare
+ * @return a prepared expression
+ */
+ Expression prepare(JexlContext context, Expression expr) {
+ try {
+ Interpreter interpreter = jexl.createInterpreter(context);
+ interpreter.setSilent(false);
+ return expr.prepare(interpreter);
+ }
+ catch (JexlException xjexl) {
+ Exception xuel = createException("prepare" , expr, xjexl);
+ if (jexl.isSilent()) {
+ jexl.LOG.warn(xuel.getMessage(), xuel.getCause());
+ return null;
+ }
+ throw xuel;
+ }
+ catch (ParseException xparse) {
+ Exception xuel = createException("prepare" , expr, xparse);
+ if (jexl.isSilent()) {
+ jexl.LOG.warn(xuel.getMessage(), xuel.getCause());
+ return null;
+ }
+ throw xuel;
+ }
+ }
+
+ /**
+ * Evaluates an expression (nested & composites), handles exception reporting.
+ *
+ * @param context the JEXL context to use
+ * @param expr the expression to prepare
+ * @return the result of the evaluation
+ */
+ Object evaluate(JexlContext context, Expression expr) {
+ try {
+ Interpreter interpreter = jexl.createInterpreter(context);
+ interpreter.setSilent(false);
+ return expr.evaluate(interpreter);
+ }
+ catch (JexlException xjexl) {
+ Exception xuel = createException("evaluate" , expr, xjexl);
+ if (jexl.isSilent()) {
+ jexl.LOG.warn(xuel.getMessage(), xuel.getCause());
+ return null;
+ }
+ throw xuel;
+ }
+ catch (ParseException xparse) {
+ Exception xuel = createException("evaluate" , expr, xparse);
+ if (jexl.isSilent()) {
+ jexl.LOG.warn(xuel.getMessage(), xuel.getCause());
+ return null;
+ }
+ throw xuel;
+ }
+ }
+
+ /**
+ * Use the JEXL parser to create the AST for an expression.
+ * @param expression the expression to parse
+ * @return the AST
+ */
+ private SimpleNode toNode(CharSequence expression) throws ParseException {
+ return (SimpleNode) jexl.parse(expression).jjtGetChild(0);
+ }
+
+ /**
+ * Creates a UnifiedJEXL.Exception from a JexlException.
+ * @param action parse, prepare, evaluate
+ * @param expr the expression
+ * @param xjexl the exception
+ * @return a "meaningfull" error message
+ */
+ private Exception createException(String action, Expression expr, java.lang.Exception xany) {
+ StringBuilder strb = new StringBuilder("failed to ");
+ strb.append(action);
+ strb.append(" '");
+ strb.append(expr.toString());
+ strb.append("'");
+ Throwable cause = xany.getCause();
+ if (cause != null) {
+ String causeMsg = cause.getMessage();
+ if (causeMsg != null) {
+ strb.append(", ");
+ strb.append(causeMsg);
+ }
+ }
+ return new Exception(strb.toString(), xany);
+ }
+
+ /** The different parsing states. */
+ private static enum ParseState {
+ CONST, // parsing a constant string
+ IMMEDIATE0, // seen $
+ DEFERRED0, // seen #
+ IMMEDIATE1, // seen ${
+ DEFERRED1, // seen #{
+ ESCAPE // seen \
+ }
+
+ /**
+ * Parses a unified expression
+ * @param expr the expression
+ * @param counts the expression type counters
+ * @return the list of expressions
+ * @throws Exception
+ */
+ private Expression parseExpression(String expr) throws ParseException {
+ final int size = expr.length();
+ ExpressionBuilder builder = new ExpressionBuilder(0);
+ StringBuilder strb = new StringBuilder(size);
+ ParseState state = ParseState.CONST;
+ int inner = 0;
+ boolean nested = false;
+ int inested = -1;
+ for (int i = 0; i < size; ++i) {
+ char c = expr.charAt(i);
+ switch (state) {
+ case CONST: {
+ if (c == '$') {
+ state = ParseState.IMMEDIATE0;
+ } else if (c == '#') {
+ inested = i;
+ state = ParseState.DEFERRED0;
+ } else if (c == '\\') {
+ state = ParseState.ESCAPE;
+ } else {
+ // do buildup expr
+ strb.append(c);
+ }
+ break;
+ }
+ case IMMEDIATE0: { // $
+ if (c == '{') {
+ state = ParseState.IMMEDIATE1;
+ // if chars in buffer, create constant
+ if (strb.length() > 0) {
+ Expression cexpr = new Constant(strb.toString(), null);
+ builder.add(cexpr);
+ strb.delete(0, Integer.MAX_VALUE);
+ }
+ } else {
+ // revert to CONST
+ strb.append('$');
+ strb.append(c);
+ state = ParseState.CONST;
+ }
+ break;
+ }
+ case DEFERRED0: { // #
+ if (c == '{') {
+ state = ParseState.DEFERRED1;
+ // if chars in buffer, create constant
+ if (strb.length() > 0) {
+ Expression cexpr = new Constant(strb.toString(), null);
+ builder.add(cexpr);
+ strb.delete(0, Integer.MAX_VALUE);
+ }
+ } else {
+ // revert to CONST
+ strb.append('#');
+ strb.append(c);
+ state = ParseState.CONST;
+ }
+ break;
+ }
+ case IMMEDIATE1: { // ${...
+ if (c == '}') {
+ // materialize the immediate expr
+ //Expression iexpr = createExpression(ExpressionType.IMMEDIATE, strb, null);
+ Expression iexpr = new Immediate(strb.toString(), toNode(strb), null);
+ builder.add(iexpr);
+ strb.delete(0, Integer.MAX_VALUE);
+ state = ParseState.CONST;
+ } else {
+ // do buildup expr
+ strb.append(c);
+ }
+ break;
+ }
+ case DEFERRED1: { // #{...
+ // skip inner strings (for '}')
+ if (c == '"' || c == '\'') {
+ strb.append(c);
+ i = readDeferredString(strb, expr, i + 1, c);
+ continue;
+ }
+ // nested immediate in deferred; need to balance count of '{' & '}'
+ if (c == '{') {
+ if (expr.charAt(i - 1) == '$') {
+ inner += 1;
+ strb.deleteCharAt(strb.length() - 1);
+ nested = true;
+ }
+ continue;
+ }
+ // closing '}'
+ if (c == '}') {
+ // balance nested immediate
+ if (inner > 0) {
+ inner -= 1;
+ } else {
+ // materialize the nested/deferred expr
+ Expression dexpr = nested?
+ new Nested(expr.substring(inested, i+1), toNode(strb), null) :
+ new Deferred(strb.toString(), toNode(strb), null);
+ builder.add(dexpr);
+ strb.delete(0, Integer.MAX_VALUE);
+ nested = false;
+ state = ParseState.CONST;
+ }
+ } else {
+ // do buildup expr
+ strb.append(c);
+ }
+ break;
+ }
+ case ESCAPE: {
+ if (c == '#') {
+ strb.append('#');
+ } else {
+ strb.append('\\');
+ strb.append(c);
+ }
+ state = ParseState.CONST;
+ }
+
+ }
+ }
+ // we should be in that state
+ if (state != ParseState.CONST)
+ throw new IllegalStateException("malformed expression: " + expr);
+ // if any chars were buffered, add them as a constant
+ if (strb.length() > 0) {
+ Expression cexpr = new Constant(strb.toString(), null);
+ builder.add(cexpr);
+ }
+ return builder.build(this, null);
+ }
+
+ /**
+ * Read the remainder of a string till a given separator,
+ * handles escaping through '\' syntax.
+ * @param strb the destination buffer to copy characters into
+ * @param str the origin
+ * @param index the offset into the origin
+ * @param sep the separator, single or double quote, marking end of string
+ * @return the offset in origin
+ */
+ static private int readDeferredString(StringBuilder strb, String str, int index, char sep) {
+ boolean escape = false;
+ for(;index < str.length();++index) {
+ char c = str.charAt(index);
+ if (escape) {
+ strb.append(c);
+ escape = false;
+ continue;
+ }
+ if (c == '\\') {
+ escape = true;
+ continue;
+ }
+ strb.append(c);
+ if (c == sep) {
+ break;
+ }
+ }
+ return index;
+ }
+}
Propchange: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/UnifiedJEXL.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/UnifiedJEXL.java
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL
Modified: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/package.html
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/package.html?rev=794133&r1=794132&r2=794133&view=diff
==============================================================================
--- commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/package.html (original)
+++ commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/package.html Wed Jul 15 03:43:49 2009
@@ -57,7 +57,7 @@
jc.getVars().put("foo", new Foo() );
// Now evaluate the expression, getting the result
- Object o = e.evaluate(jc);</pre>
+ Object o = e.evaluate(jc);
</pre>
</body>
Modified: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/parser/Parser.jjt
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/parser/Parser.jjt?rev=794133&r1=794132&r2=794133&view=diff
==============================================================================
--- commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/parser/Parser.jjt (original)
+++ commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/parser/Parser.jjt Wed Jul 15 03:43:49 2009
@@ -47,7 +47,7 @@
{
public SimpleNode parse(Reader reader)
- throws Exception
+ throws ParseException
{
ReInit(reader);
@@ -66,9 +66,10 @@
SKIP : /* WHITE SPACE */
{
- <COMMENT: ( "##" ( ~["\"","\n","\r"] )* ("\n" | "\r") ) >
-|
- " "
+ <COMMENT: ( "##" ( ~["\"","\n","\r"] )* ("\n" | "\r") ) >
+| <"/*" (~["*"])* "*" ("*" | ~["*","/"] (~["*"])* "*")* "/">
+| <"//" (~["\n","\r"])* ("\n" | "\r" | "\r\n")>
+| " "
| "\t"
| "\n"
| "\r"
Modified: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/ClassMap.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/ClassMap.java?rev=794133&r1=794132&r2=794133&view=diff
==============================================================================
--- commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/ClassMap.java (original)
+++ commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/ClassMap.java Wed Jul 15 03:43:49 2009
@@ -29,9 +29,9 @@
/**
* Taken from the Velocity tree so we can be self-sufficient
*
- * A cache of introspection information for a specific class instance. Keys
- * {@link MethodKey} objects by an agregation of the method name and
- * the names of classes that make up the parameters.
+ * A cache of introspection information for a specific class instance.
+ * Keys objects by an agregation of the method name and the names of classes
+ * that make up the parameters.
*
* @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
* @author <a href="mailto:bob@werken.com">Bob McWhirter</a>
Modified: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/UberspectImpl.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/UberspectImpl.java?rev=794133&r1=794132&r2=794133&view=diff
==============================================================================
--- commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/UberspectImpl.java (original)
+++ commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/UberspectImpl.java Wed Jul 15 03:43:49 2009
@@ -409,9 +409,7 @@
}
} // CSON: VisibilityModifier
- /**
- * {@inheritDoc}
- */
+
public static class VelGetterImpl implements VelPropertyGet {
/**
* executor for performing the get.
@@ -457,9 +455,7 @@
}
}
- /**
- * {@inheritDoc}
- */
+
public static class VelSetterImpl implements VelPropertySet {
/**
* the method to call.
Modified: commons/proper/jexl/branches/2.0/src/test/org/apache/commons/jexl/ArithmeticTest.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/test/org/apache/commons/jexl/ArithmeticTest.java?rev=794133&r1=794132&r2=794133&view=diff
==============================================================================
--- commons/proper/jexl/branches/2.0/src/test/org/apache/commons/jexl/ArithmeticTest.java (original)
+++ commons/proper/jexl/branches/2.0/src/test/org/apache/commons/jexl/ArithmeticTest.java Wed Jul 15 03:43:49 2009
@@ -16,6 +16,7 @@
*/
package org.apache.commons.jexl;
+import java.util.Map;
import java.math.BigDecimal;
import java.math.BigInteger;
@@ -112,4 +113,78 @@
asserter.assertExpression("imanull + 2", new Long(2));
asserter.assertExpression("imanull + imanull", new Long(0));
}
+
+ /**
+ *
+ * if silent, all arith exception return 0.0
+ * if not silent, all arith exception throw
+ * @throws Exception
+ */
+ public void testDivideByZero() throws Exception {
+ JexlContext context = JexlHelper.createContext();
+ Map vars = context.getVars();
+ vars.put("aByte", new Byte((byte) 1));
+ vars.put("aShort", new Short((short) 2));
+ vars.put("anInteger", new Integer(3));
+ vars.put("aLong", new Long(4));
+ vars.put("aFloat", new Float(5.5));
+ vars.put("aDouble", new Double(6.6));
+ vars.put("aBigInteger", new BigInteger("7"));
+ vars.put("aBigDecimal", new BigDecimal("8.8"));
+
+
+ vars.put("zByte", new Byte((byte) 0));
+ vars.put("zShort", new Short((short) 0));
+ vars.put("zInteger", new Integer(0));
+ vars.put("zLong", new Long(0));
+ vars.put("zFloat", new Float(0));
+ vars.put("zDouble", new Double(0));
+ vars.put("zBigInteger", new BigInteger("0"));
+ vars.put("zBigDecimal", new BigDecimal("0"));
+
+ String[] tnames = {
+ "Byte", "Short", "Integer", "Long",
+ "Float", "Double",
+ "BigInteger", "BigDecimal"
+ };
+ // number of permutations this will generate
+ final int PERMS = tnames.length * tnames.length;
+
+ JexlEngine jexl = new JexlEngine();
+ // for non-silent, silent...
+ for (int s = 0; s < 2; ++s) {
+ jexl.setSilent(s != 0);
+ int zthrow = 0;
+ int zeval = 0;
+ // for vars of all types...
+ for (String vname : tnames) {
+ // for zeros of all types...
+ for (String zname : tnames) {
+ // divide var by zero
+ String expr = "a" + vname + " / " + "z" + zname;
+ try {
+ Expression zexpr = jexl.createExpression(expr);
+ Object nan = zexpr.evaluate(context);
+ // check we have a zero & incremement zero count
+ if (nan instanceof Number) {
+ double zero = ((Number) nan).doubleValue();
+ if (zero == 0.0)
+ zeval += 1;
+ }
+ }
+ catch (Exception any) {
+ // increment the exception count
+ zthrow += 1;
+ }
+ }
+ }
+ if (!jexl.isSilent())
+ assertTrue("All expressions should have thrown " + zthrow,
+ zthrow == PERMS);
+ else
+ assertTrue("All expressions should have zeroed" + zeval,
+ zeval == PERMS);
+ }
+ }
+
}
Modified: commons/proper/jexl/branches/2.0/src/test/org/apache/commons/jexl/AssignTest.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/test/org/apache/commons/jexl/AssignTest.java?rev=794133&r1=794132&r2=794133&view=diff
==============================================================================
--- commons/proper/jexl/branches/2.0/src/test/org/apache/commons/jexl/AssignTest.java (original)
+++ commons/proper/jexl/branches/2.0/src/test/org/apache/commons/jexl/AssignTest.java Wed Jul 15 03:43:49 2009
@@ -120,7 +120,6 @@
public void testArray() throws Exception {
Expression assign = JEXL.createExpression("froboz[\"value\"] = 10");
Expression check = JEXL.createExpression("froboz[\"value\"]");
- System.out.print(assign.dump());
JexlContext jc = JexlHelper.createContext();
Froboz froboz = new Froboz(0);
jc.getVars().put("froboz", froboz);
@@ -134,12 +133,24 @@
Expression assign = JEXL.createExpression("quux.froboz.value = 10");
Expression check = JEXL.createExpression("quux[\"froboz\"].value");
JexlContext jc = JexlHelper.createContext();
- jc.getVars().put("quux", new Quux("xuuq", 100));
+ Quux quux = new Quux("xuuq", 100);
+ jc.getVars().put("quux", quux);
Object o = assign.evaluate(jc);
assertEquals("Result is not 10", new Integer(10), o);
o = check.evaluate(jc);
assertEquals("Result is not 10", new Integer(10), o);
}
+
+ public void testUtil() throws Exception {
+ JexlContext jc = JexlHelper.createContext();
+ Quux quux = new Quux("xuuq", 100);
+ JEXL.setProperty(quux, "froboz.value", 100);
+ Object o = JEXL.getProperty(quux, "froboz.value");
+ assertEquals("Result is not 100", new Integer(100), o);
+ JEXL.setProperty(quux, "['froboz'].value", 1000);
+ o = JEXL.getProperty(quux, "['froboz']['value']");
+ assertEquals("Result is not 1000", new Integer(1000), o);
+ }
public void testTernary() throws Exception {
@@ -169,7 +180,7 @@
}
public static void main(String[] args) throws Exception {
- new AssignTest("debug").testAmbiguous();
+ new AssignTest("debug").testUtil();
//new AssignTest("debug").testArray();
}
}
\ No newline at end of file
Added: commons/proper/jexl/branches/2.0/src/test/org/apache/commons/jexl/IssuesTest.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/test/org/apache/commons/jexl/IssuesTest.java?rev=794133&view=auto
==============================================================================
--- commons/proper/jexl/branches/2.0/src/test/org/apache/commons/jexl/IssuesTest.java (added)
+++ commons/proper/jexl/branches/2.0/src/test/org/apache/commons/jexl/IssuesTest.java Wed Jul 15 03:43:49 2009
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.jexl;
+import java.util.Map;
+import junit.framework.TestCase;
+
+/**
+ * Test cases for reported issues
+ */
+public class IssuesTest extends TestCase {
+
+ // JEXL-52: can be implemented by deriving Interpreter.{g,s}etAttribute
+ // JEXL-50: can be implemented through a namespace:function or through JexlArithmetic derivation
+
+ // JEXL-49: blocks not parsed (fixed)
+ public void test49() throws Exception {
+ JexlContext ctxt = JexlHelper.createContext();
+ Map vars = ctxt.getVars();
+
+ String stmt = "{a = 'b'; c = 'd';}";
+ Script expr = ScriptFactory.createScript(stmt);
+ Object value = expr.execute(ctxt);
+ assertTrue("JEXL-49 is not fixed", vars.get("a").equals("b") && vars.get("c").equals("d"));
+ }
+
+
+ // JEXL-48: bad assignment detection
+ public static class Another {
+ private Boolean foo = true;
+
+ public Boolean foo() {
+ return foo;
+ }
+
+ public int goo() {
+ return 100;
+ }
+ }
+
+ public static class Foo {
+ private Another inner;
+
+ Foo() {
+ inner = new Another();
+ }
+
+ public Another getInner() {
+ return inner;
+ }
+ }
+
+ public void test48() throws Exception {
+ JexlEngine jexl = new JexlEngine();
+ // ensure errors will throw
+ jexl.setSilent(false);
+ String jexlExp = "(foo.getInner().foo() eq true) and (foo.getInner().goo() = (foo.getInner().goo()+1-1))";
+ Expression e = jexl.createExpression( jexlExp );
+ JexlContext jc = JexlHelper.createContext();
+ jc.getVars().put("foo", new Foo() );
+
+ try {
+ Object o = e.evaluate(jc);
+ fail("Should have failed due to invalid assignment");
+ }
+ catch(JexlException xjexl) {
+ // expected
+ }
+ }
+
+ // JEXL-47: C style comments (single & multi line) (fixed)
+ // JEXL-44: comments dont allow double quotes (fixed)
+ public void test47() throws Exception {
+ JexlEngine jexl = new JexlEngine();
+ // ensure errors will throw
+ jexl.setSilent(false);
+ JexlContext ctxt = JexlHelper.createContext();
+
+ Expression expr = jexl.createExpression( "true//false\n" );
+ Object value = expr.evaluate(ctxt);
+ assertTrue("should be true", (Boolean) value);
+
+ expr = jexl.createExpression( "/*true*/false" );
+ value = expr.evaluate(ctxt);
+ assertFalse("should be false", (Boolean) value);
+
+ expr = jexl.createExpression( "/*\"true\"*/false" );
+ value = expr.evaluate(ctxt);
+ assertFalse("should be false", (Boolean) value);
+ }
+
+ // JEXL-46: regexp syntax; should we really add more syntactic elements?
+ // JEXL-45: unhandled division by zero (fixed see ArithmeticTest)
+
+ // JEXL-42: NullPointerException evaluating an expression (fixed)
+ public void test42() throws Exception {
+ JexlEngine jexl = new JexlEngine();
+ UnifiedJEXL uel = new UnifiedJEXL(jexl);
+ // ensure errors will throw
+ //jexl.setSilent(false);
+ JexlContext ctxt = JexlHelper.createContext();
+ ctxt.getVars().put("ax", "ok" );
+
+ UnifiedJEXL.Expression expr = uel.parse( "${ax+(bx)}" );
+ Object value = expr.evaluate(ctxt);
+ assertTrue("should be ok", "ok".equals(value));
+ }
+
+ public static void main(String[] args) throws Exception {
+ new IssuesTest().test42();
+ }
+
+}
Propchange: commons/proper/jexl/branches/2.0/src/test/org/apache/commons/jexl/IssuesTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: commons/proper/jexl/branches/2.0/src/test/org/apache/commons/jexl/IssuesTest.java
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL
Modified: commons/proper/jexl/branches/2.0/src/test/org/apache/commons/jexl/Jexl.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/test/org/apache/commons/jexl/Jexl.java?rev=794133&r1=794132&r2=794133&view=diff
==============================================================================
--- commons/proper/jexl/branches/2.0/src/test/org/apache/commons/jexl/Jexl.java (original)
+++ commons/proper/jexl/branches/2.0/src/test/org/apache/commons/jexl/Jexl.java Wed Jul 15 03:43:49 2009
@@ -31,15 +31,6 @@
JexlContext context = new JexlContext() {
public Map getVars() { return System.getProperties(); }
public void setVars(Map map) { }
-
- public void setFunctions(Map<String, Object> prefixes) {
- throw new UnsupportedOperationException("Not supported yet.");
- }
-
- public Map<String, Object> getFunctions() {
- throw new UnsupportedOperationException("Not supported yet.");
- }
-
};
try {
for (int i = 0; i < args.length; i++) {