This is an automated email from the ASF dual-hosted git repository.
henrib pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-jexl.git
The following commit(s) were added to refs/heads/master by this push:
new ef73c99a JEXL-372, JEXL-373: fixing multiple vars declaration in loop,
grammar change; refactored postfix/prefix operators; more tests;
ef73c99a is described below
commit ef73c99ad317ed20328a1297dc7b30a89f9a8d13
Author: henrib <[email protected]>
AuthorDate: Fri Jul 1 15:29:11 2022 +0200
JEXL-372, JEXL-373: fixing multiple vars declaration in loop, grammar
change; refactored postfix/prefix operators; more tests;
---
.../org/apache/commons/jexl3/JexlArithmetic.java | 33 ++--
.../org/apache/commons/jexl3/JexlOperator.java | 91 +++++----
.../apache/commons/jexl3/internal/Debugger.java | 96 +++++++--
.../org/apache/commons/jexl3/internal/Engine.java | 2 +-
.../apache/commons/jexl3/internal/Interpreter.java | 135 +++++++------
.../apache/commons/jexl3/internal/Operators.java | 162 +++++++++------
.../org/apache/commons/jexl3/internal/Scope.java | 24 +--
.../commons/jexl3/internal/ScriptVisitor.java | 7 +-
.../apache/commons/jexl3/parser/JexlParser.java | 7 +-
.../org/apache/commons/jexl3/parser/Parser.jjt | 14 +-
.../apache/commons/jexl3/parser/ParserVisitor.java | 12 +-
.../org/apache/commons/jexl3/ArithmeticTest.java | 139 +++++++++++++
.../java/org/apache/commons/jexl3/ForEachTest.java | 27 +++
.../org/apache/commons/jexl3/JexlTestCase.java | 7 +
.../java/org/apache/commons/jexl3/LexicalTest.java | 2 +-
.../org/apache/commons/jexl3/SideEffectTest.java | 217 +++++++++++++++++----
.../org/apache/commons/jexl3/junit/Asserter.java | 14 +-
17 files changed, 729 insertions(+), 260 deletions(-)
diff --git a/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java
b/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java
index 1ca2bd42..4610d227 100644
--- a/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java
+++ b/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java
@@ -534,7 +534,7 @@ public class JexlArithmetic {
}
/**
- * Given a Number, return back the value attempting to narrow it to a
target class.
+ * Given a Number, return the value attempting to narrow it to a target
class.
*
* @param original the original number
* @param narrow the attempted target class
@@ -613,14 +613,14 @@ public class JexlArithmetic {
* if either arguments is a Long, no narrowing to Integer will occur
* </p>
*
- * @param lhs the left hand side operand that lead to the bigi result
- * @param rhs the right hand side operand that lead to the bigi result
+ * @param lhs the left-hand side operand that lead to the bigi result
+ * @param rhs the right-hand side operand that lead to the bigi result
* @param bigi the BigInteger to narrow
* @return an Integer or Long if narrowing is possible, the original
BigInteger otherwise
*/
protected Number narrowBigInteger(final Object lhs, final Object rhs,
final BigInteger bigi) {
//coerce to long if possible
- if (!(lhs instanceof BigInteger || rhs instanceof BigInteger)
+ if ((isNumberable(lhs) || isNumberable(rhs))
&& bigi.compareTo(BIGI_LONG_MAX_VALUE) <= 0
&& bigi.compareTo(BIGI_LONG_MIN_VALUE) >= 0) {
// coerce to int if possible
@@ -637,13 +637,13 @@ public class JexlArithmetic {
}
/**
- * Given a BigDecimal, attempt to narrow it to an Integer or Long if it
fits if
+ * Given a BigDecimal, attempt to narrow it to an Integer or Long if it
fits and
* one of the arguments is a numberable.
*
- * @param lhs the left hand side operand that lead to the bigd result
- * @param rhs the right hand side operand that lead to the bigd result
+ * @param lhs the left-hand side operand that lead to the bigd result
+ * @param rhs the right-hand side operand that lead to the bigd result
* @param bigd the BigDecimal to narrow
- * @return an Integer or Long if narrowing is possible, the original
BigInteger otherwise
+ * @return an Integer or Long if narrowing is possible, the original
BigDecimal otherwise
*/
protected Number narrowBigDecimal(final Object lhs, final Object rhs,
final BigDecimal bigd) {
if (isNumberable(lhs) || isNumberable(rhs)) {
@@ -755,19 +755,21 @@ public class JexlArithmetic {
return ((Long) val) + incr;
}
if (val instanceof BigDecimal) {
- return ((BigDecimal) val).add(BigDecimal.valueOf(incr));
+ BigDecimal bd = (BigDecimal) val;
+ return bd.add(BigDecimal.valueOf(incr), this.mathContext);
}
if (val instanceof BigInteger) {
- return ((BigInteger) val).add(BigInteger.valueOf(incr));
+ BigInteger bi = (BigInteger) val;
+ return bi.add(BigInteger.valueOf(incr));
}
if (val instanceof Float) {
return ((Float) val) + incr;
}
if (val instanceof Short) {
- return (short) ((Short) val) + incr;
+ return (short) (((Short) val) + incr);
}
if (val instanceof Byte) {
- return (byte) ((Byte) val) + incr;
+ return (byte) (((Byte) val) + incr);
}
throw new ArithmeticException("Object "+(incr < 0?
"decrement":"increment")+":(" + val + ")");
}
@@ -1046,6 +1048,7 @@ public class JexlArithmetic {
/**
* Negates a value (unary minus for numbers).
*
+ * @see #isNegateStable()
* @param val the value to negate
* @return the negated value
*/
@@ -1092,7 +1095,6 @@ public class JexlArithmetic {
* <p>This is used to determine whether negate results on number literals
can be cached.
* If the result on calling negate with the same constant argument may
change between calls,
* which means the function is not deterministic, this method must return
false.
- * @see #isNegateStable()
* @return true if negate is idempotent, false otherwise
*/
public boolean isNegateStable() {
@@ -1676,7 +1678,7 @@ public class JexlArithmetic {
*/
private double parseDouble(String arg) throws ArithmeticException {
try {
- return arg.isEmpty()? Double.NaN : Double.parseDouble((String)
arg);
+ return arg.isEmpty()? Double.NaN : Double.parseDouble(arg);
} catch(NumberFormatException xformat) {
throw new ArithmeticException("Double coercion: ("+ arg +")");
}
@@ -1862,8 +1864,7 @@ public class JexlArithmetic {
return parseDouble((String) val);
}
if (val instanceof Character) {
- final int i = ((Character) val);
- return i;
+ return ((Character) val);
}
throw new ArithmeticException("Double coercion: "
+ val.getClass().getName() + ":(" + val + ")");
diff --git a/src/main/java/org/apache/commons/jexl3/JexlOperator.java
b/src/main/java/org/apache/commons/jexl3/JexlOperator.java
index 820ad0d0..2b93eb24 100644
--- a/src/main/java/org/apache/commons/jexl3/JexlOperator.java
+++ b/src/main/java/org/apache/commons/jexl3/JexlOperator.java
@@ -28,7 +28,7 @@ package org.apache.commons.jexl3;
* <p>The default JexlArithmetic implements generic versions of these methods
using Object as arguments.
* You can use your own derived JexlArithmetic that override and/or overload
those operator methods.
* Note that these are overloads by convention, not actual Java overloads.
- * The following rules apply to operator methods:</p>
+ * The following rules apply to all operator methods:</p>
* <ul>
* <li>Operator methods should be public</li>
* <li>Operators return type should be respected when primitive (int,
boolean,...)</li>
@@ -36,6 +36,14 @@ package org.apache.commons.jexl3;
* <li>Operators may return JexlEngine.TRY_AGAIN to fallback on default JEXL
implementation</li>
* </ul>
*
+ * For side effect operators, operators that modify the left-hand size value
(+=, -=, etc), the user implemented
+ * overload methods may return:
+ * <ul>
+ * <li>JexlEngine.TRY_FAIL to let the default fallback behavior be
executed.</li>
+ * <li>Any other value will be used as the new value to be assigned to the
left-hand-side.</li>
+ * </ul>
+ * Note that side effect operators always return the left-hand side value
(with an exception for postfix ++ and --).
+ *
* @since 3.0
*/
public enum JexlOperator {
@@ -44,7 +52,7 @@ public enum JexlOperator {
* Add operator.
* <br><strong>Syntax:</strong> <code>x + y</code>
* <br><strong>Method:</strong> <code>T add(L x, R y);</code>.
- * @see JexlArithmetic#add
+ * @see JexlArithmetic#add(Object, Object)
*/
ADD("+", "add", 2),
@@ -52,7 +60,7 @@ public enum JexlOperator {
* Subtract operator.
* <br><strong>Syntax:</strong> <code>x - y</code>
* <br><strong>Method:</strong> <code>T subtract(L x, R y);</code>.
- * @see JexlArithmetic#subtract
+ * @see JexlArithmetic#subtract(Object, Object)
*/
SUBTRACT("-", "subtract", 2),
@@ -60,7 +68,7 @@ public enum JexlOperator {
* Multiply operator.
* <br><strong>Syntax:</strong> <code>x * y</code>
* <br><strong>Method:</strong> <code>T multiply(L x, R y);</code>.
- * @see JexlArithmetic#multiply
+ * @see JexlArithmetic#multiply(Object, Object)
*/
MULTIPLY("*", "multiply", 2),
@@ -68,7 +76,7 @@ public enum JexlOperator {
* Divide operator.
* <br><strong>Syntax:</strong> <code>x / y</code>
* <br><strong>Method:</strong> <code>T divide(L x, R y);</code>.
- * @see JexlArithmetic#divide
+ * @see JexlArithmetic#divide(Object, Object)
*/
DIVIDE("/", "divide", 2),
@@ -76,7 +84,7 @@ public enum JexlOperator {
* Modulo operator.
* <br><strong>Syntax:</strong> <code>x % y</code>
* <br><strong>Method:</strong> <code>T mod(L x, R y);</code>.
- * @see JexlArithmetic#mod
+ * @see JexlArithmetic#mod(Object, Object)
*/
MOD("%", "mod", 2),
@@ -84,7 +92,7 @@ public enum JexlOperator {
* Bitwise-and operator.
* <br><strong>Syntax:</strong> <code>x & y</code>
* <br><strong>Method:</strong> <code>T and(L x, R y);</code>.
- * @see JexlArithmetic#and
+ * @see JexlArithmetic#and(Object, Object)
*/
AND("&", "and", 2),
@@ -92,7 +100,7 @@ public enum JexlOperator {
* Bitwise-or operator.
* <br><strong>Syntax:</strong> <code>x | y</code>
* <br><strong>Method:</strong> <code>T or(L x, R y);</code>.
- * @see JexlArithmetic#or
+ * @see JexlArithmetic#or(Object, Object)
*/
OR("|", "or", 2),
@@ -100,7 +108,7 @@ public enum JexlOperator {
* Bitwise-xor operator.
* <br><strong>Syntax:</strong> <code>x ^ y</code>
* <br><strong>Method:</strong> <code>T xor(L x, R y);</code>.
- * @see JexlArithmetic#xor
+ * @see JexlArithmetic#xor(Object, Object)
*/
XOR("^", "xor", 2),
@@ -132,7 +140,7 @@ public enum JexlOperator {
* Equals operator.
* <br><strong>Syntax:</strong> <code>x == y</code>
* <br><strong>Method:</strong> <code>boolean equals(L x, R y);</code>.
- * @see JexlArithmetic#equals
+ * @see JexlArithmetic#equals(Object, Object)
*/
EQ("==", "equals", 2),
@@ -140,7 +148,7 @@ public enum JexlOperator {
* Less-than operator.
* <br><strong>Syntax:</strong> <code>x < y</code>
* <br><strong>Method:</strong> <code>boolean lessThan(L x, R y);</code>.
- * @see JexlArithmetic#lessThan
+ * @see JexlArithmetic#lessThan(Object, Object)
*/
LT("<", "lessThan", 2),
@@ -148,7 +156,7 @@ public enum JexlOperator {
* Less-than-or-equal operator.
* <br><strong>Syntax:</strong> <code>x <= y</code>
* <br><strong>Method:</strong> <code>boolean lessThanOrEqual(L x, R
y);</code>.
- * @see JexlArithmetic#lessThanOrEqual
+ * @see JexlArithmetic#lessThanOrEqual(Object, Object)
*/
LTE("<=", "lessThanOrEqual", 2),
@@ -156,7 +164,7 @@ public enum JexlOperator {
* Greater-than operator.
* <br><strong>Syntax:</strong> <code>x > y</code>
* <br><strong>Method:</strong> <code>boolean greaterThan(L x, R
y);</code>.
- * @see JexlArithmetic#greaterThan
+ * @see JexlArithmetic#greaterThan(Object, Object)
*/
GT(">", "greaterThan", 2),
@@ -164,7 +172,7 @@ public enum JexlOperator {
* Greater-than-or-equal operator.
* <br><strong>Syntax:</strong> <code>x >= y</code>
* <br><strong>Method:</strong> <code>boolean greaterThanOrEqual(L x, R
y);</code>.
- * @see JexlArithmetic#greaterThanOrEqual
+ * @see JexlArithmetic#greaterThanOrEqual(Object, Object)
*/
GTE(">=", "greaterThanOrEqual", 2),
@@ -172,7 +180,7 @@ public enum JexlOperator {
* Contains operator.
* <br><strong>Syntax:</strong> <code>x =~ y</code>
* <br><strong>Method:</strong> <code>boolean contains(L x, R y);</code>.
- * @see JexlArithmetic#contains
+ * @see JexlArithmetic#contains(Object, Object)
*/
CONTAINS("=~", "contains", 2),
@@ -180,7 +188,7 @@ public enum JexlOperator {
* Starts-with operator.
* <br><strong>Syntax:</strong> <code>x =^ y</code>
* <br><strong>Method:</strong> <code>boolean startsWith(L x, R y);</code>.
- * @see JexlArithmetic#startsWith
+ * @see JexlArithmetic#startsWith(Object, Object)
*/
STARTSWITH("=^", "startsWith", 2),
@@ -188,7 +196,7 @@ public enum JexlOperator {
* Ends-with operator.
* <br><strong>Syntax:</strong> <code>x =$ y</code>
* <br><strong>Method:</strong> <code>boolean endsWith(L x, R y);</code>.
- * @see JexlArithmetic#endsWith
+ * @see JexlArithmetic#endsWith(Object, Object)
*/
ENDSWITH("=$", "endsWith", 2),
@@ -196,7 +204,7 @@ public enum JexlOperator {
* Not operator.
* <br><strong>Syntax:</strong> <code>!x</code>
* <br><strong>Method:</strong> <code>T not(L x);</code>.
- * @see JexlArithmetic#not
+ * @see JexlArithmetic#not(Object)
*/
NOT("!", "not", 1),
@@ -204,7 +212,7 @@ public enum JexlOperator {
* Complement operator.
* <br><strong>Syntax:</strong> <code>~x</code>
* <br><strong>Method:</strong> <code>T complement(L x);</code>.
- * @see JexlArithmetic#complement
+ * @see JexlArithmetic#complement(Object)
*/
COMPLEMENT("~", "complement", 1),
@@ -212,7 +220,7 @@ public enum JexlOperator {
* Negate operator.
* <br><strong>Syntax:</strong> <code>-x</code>
* <br><strong>Method:</strong> <code>T negate(L x);</code>.
- * @see JexlArithmetic#negate
+ * @see JexlArithmetic#negate(Object)
*/
NEGATE("-", "negate", 1),
@@ -220,7 +228,7 @@ public enum JexlOperator {
* Positivize operator.
* <br><strong>Syntax:</strong> <code>+x</code>
* <br><strong>Method:</strong> <code>T positivize(L x);</code>.
- * @see JexlArithmetic#positivize
+ * @see JexlArithmetic#positivize(Object)
*/
POSITIVIZE("+", "positivize", 1),
@@ -228,7 +236,7 @@ public enum JexlOperator {
* Empty operator.
* <br><strong>Syntax:</strong> <code>empty x</code> or
<code>empty(x)</code>
* <br><strong>Method:</strong> <code>boolean empty(L x);</code>.
- * @see JexlArithmetic#empty
+ * @see JexlArithmetic#empty(Object)
*/
EMPTY("empty", "empty", 1),
@@ -236,7 +244,7 @@ public enum JexlOperator {
* Size operator.
* <br><strong>Syntax:</strong> <code>size x</code> or <code>size(x)</code>
* <br><strong>Method:</strong> <code>int size(L x);</code>.
- * @see JexlArithmetic#size
+ * @see JexlArithmetic#size(Object)
*/
SIZE("size", "size", 1),
@@ -319,15 +327,15 @@ public enum JexlOperator {
/**
* Increment pseudo-operator.
- * <br>No syntax, used as helper for <code>++</code>.
- * @see JexlArithmetic#increment
+ * <br>No syntax, used as helper for the prefix and postfix versions of
<code>++</code>.
+ * @see JexlArithmetic#increment(Object)
*/
INCREMENT("+1", "increment", 1),
/**
* Decrement pseudo-operator.
- * <br>No syntax, used as helper for <code>--</code>.
- * @see JexlArithmetic#decrement
+ * <br>No syntax, used as helper for the prefix and postfix versions of
<code>--</code>.
+ * @see JexlArithmetic#decrement(Object)
*/
DECREMENT("-1", "decrement", 1),
@@ -336,28 +344,28 @@ public enum JexlOperator {
* <br><strong>Syntax:</strong> <code>++x</code>
* <br><strong>Method:</strong> <code>T incrementAndGet(L x);</code>.
*/
- INCREMENT_AND_GET("++.", "incrementAndGet", INCREMENT),
+ INCREMENT_AND_GET("++.", "incrementAndGet", INCREMENT, 1),
/**
* Postfix ++, increments and returns the value before incrementing.
* <br><strong>Syntax:</strong> <code>x++</code>
* <br><strong>Method:</strong> <code>T getAndIncrement(L x);</code>.
*/
- GET_AND_INCREMENT(".++", "getAndIncrement", INCREMENT),
+ GET_AND_INCREMENT(".++", "getAndIncrement", INCREMENT, 1),
/**
* Prefix --, decrements and returns the value after decrementing.
* <br><strong>Syntax:</strong> <code>--x</code>
* <br><strong>Method:</strong> <code>T decrementAndGet(L x);</code>.
*/
- DECREMENT_AND_GET("--.", "decrementAndGet", DECREMENT),
+ DECREMENT_AND_GET("--.", "decrementAndGet", DECREMENT, 1),
/**
* Postfix --, decrements and returns the value before decrementing.
* <br><strong>Syntax:</strong> <code>x--</code>
* <br><strong>Method:</strong> <code>T getAndDecrement(L x);</code>.
*/
- GET_AND_DECREMENT(".--", "getAndDecrement", DECREMENT),
+ GET_AND_DECREMENT(".--", "getAndDecrement", DECREMENT, 1),
/**
* Marker for side effect.
@@ -431,23 +439,32 @@ public enum JexlOperator {
* @param argc the number of parameters for the method
*/
JexlOperator(final String o, final String m, final int argc) {
- this.operator = o;
- this.methodName = m;
- this.arity = argc;
- this.base = null;
+ this(o, m, null, argc);
}
/**
- * Creates a side-effect operator.
+ * Creates a side effect operator with arity == 2.
*
* @param o the operator name
* @param m the method name associated to this operator in a JexlArithmetic
* @param b the base operator, ie + for +=
*/
JexlOperator(final String o, final String m, final JexlOperator b) {
+ this(o, m, b, 2);
+ }
+
+ /**
+ * Creates a side effect operator.
+ *
+ * @param o the operator name
+ * @param m the method name associated to this operator in a JexlArithmetic
+ * @param b the base operator, ie + for +=
+ * @param a the operator arity
+ */
+ JexlOperator(final String o, final String m, final JexlOperator b, final
int a) {
this.operator = o;
this.methodName = m;
- this.arity = 2;
+ this.arity = a;
this.base = b;
}
diff --git a/src/main/java/org/apache/commons/jexl3/internal/Debugger.java
b/src/main/java/org/apache/commons/jexl3/internal/Debugger.java
index e24bf8f1..196c0fcf 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Debugger.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Debugger.java
@@ -50,6 +50,8 @@ public class Debugger extends ParserVisitor implements
JexlInfo.Detail {
protected int depth = Integer.MAX_VALUE;
/** Arrow symbol. */
protected String arrow = "->";
+ /** EOL. */
+ protected String lf = "\n";
/**
* Creates a Debugger.
@@ -234,6 +236,16 @@ public class Debugger extends ParserVisitor implements
JexlInfo.Detail {
return this;
}
+ /**
+ * Sets this debugger line-feed string.
+ * @param lf the string used to delineate lines (usually "\" or "")
+ * @return this debugger instance
+ */
+ public Debugger lineFeed(final String lf) {
+ this.lf = lf;
+ return this;
+ }
+
/**
* Checks if a child node is the cause to debug & adds its
representation to the rebuilt expression.
* @param node the child node
@@ -264,7 +276,6 @@ public class Debugger extends ParserVisitor implements
JexlInfo.Detail {
*/
private static boolean isStatement(JexlNode child) {
return child instanceof ASTJexlScript
- || child instanceof ASTJexlLambda
|| child instanceof ASTBlock
|| child instanceof ASTIfStatement
|| child instanceof ASTForeachStatement
@@ -313,7 +324,7 @@ public class Debugger extends ParserVisitor implements
JexlInfo.Detail {
if (!isStatement(child) && !semicolTerminated(builder)) {
builder.append(';');
if (indent > 0) {
- builder.append('\n');
+ builder.append(lf);
} else {
builder.append(' ');
}
@@ -537,7 +548,7 @@ public class Debugger extends ParserVisitor implements
JexlInfo.Detail {
builder.append('{');
if (indent > 0) {
indentLevel += 1;
- builder.append('\n');
+ builder.append(lf);
} else {
builder.append(' ');
}
@@ -554,6 +565,9 @@ public class Debugger extends ParserVisitor implements
JexlInfo.Detail {
}
}
}
+ if (!Character.isSpaceChar(builder.charAt(builder.length() - 1))) {
+ builder.append(' ');
+ }
builder.append('}');
return data;
}
@@ -615,15 +629,45 @@ public class Debugger extends ParserVisitor implements
JexlInfo.Detail {
return check(node, "break", data);
}
+
@Override
protected Object visit(final ASTForeachStatement node, final Object data) {
+ final int form = node.getLoopForm();
builder.append("for(");
- accept(node.jjtGetChild(0), data);
- builder.append(" : ");
- accept(node.jjtGetChild(1), data);
- builder.append(") ");
- if (node.jjtGetNumChildren() > 2) {
- acceptStatement(node.jjtGetChild(2), data);
+ final JexlNode body;
+ if (form == 0) {
+ // for( .. : ...)
+ accept(node.jjtGetChild(0), data);
+ builder.append(" : ");
+ accept(node.jjtGetChild(1), data);
+ builder.append(") ");
+ body = node.jjtGetNumChildren() > 2? node.jjtGetChild(2) : null;
+ } else {
+ // for( .. ; ... ; ..)
+ int nc = 0;
+ // first child is var declaration(s)
+ final JexlNode vars = (form & 1) != 0 ? node.jjtGetChild(nc++) :
null;
+ final JexlNode predicate = (form & 2) != 0 ?
node.jjtGetChild(nc++) : null;
+ // the loop step
+ final JexlNode step = (form & 4) != 0 ? node.jjtGetChild(nc++) :
null;
+ // last child is body
+ body = (form & 8) != 0 ? node.jjtGetChild(nc) : null;
+ if (vars != null) {
+ accept(vars, data);
+ }
+ builder.append("; ");
+ if (predicate != null) {
+ accept(predicate, data);
+ }
+ builder.append("; ");
+ if (step != null) {
+ accept(step, data);
+ }
+ builder.append(") ");
+ }
+ // the body
+ if (body != null) {
+ accept(body, data);
} else {
builder.append(';');
}
@@ -1047,6 +1091,36 @@ public class Debugger extends ParserVisitor implements
JexlInfo.Detail {
return data;
}
+ @Override
+ protected Object visit(final ASTDefineVars node, final Object data) {
+ final int num = node.jjtGetNumChildren();
+ if (num > 0) {
+ // var, let, const
+ accept(node.jjtGetChild(0), data);
+ for (int i = 1; i < num; ++i) {
+ builder.append(", ");
+ JexlNode child = node.jjtGetChild(i);
+ if (child instanceof ASTAssignment) {
+ ASTAssignment assign = (ASTAssignment) child;
+ int nc = assign.jjtGetNumChildren();
+ ASTVar var = (ASTVar) assign.jjtGetChild(0);
+ builder.append(var.getName());
+ if (nc > 1) {
+ builder.append(" = ");
+ accept(assign.jjtGetChild(1), data);
+ }
+ } else if (child instanceof ASTVar) {
+ ASTVar var = (ASTVar) child;
+ builder.append(var.getName());
+ } else {
+ // that's odd
+ accept(child, data);
+ }
+ }
+ }
+ return data;
+ }
+
@Override
protected Object visit(final ASTWhileStatement node, final Object data) {
builder.append("while (");
@@ -1132,7 +1206,7 @@ public class Debugger extends ParserVisitor implements
JexlInfo.Detail {
@Override
protected Object visit(final ASTGetDecrementNode node, final Object data) {
- return postfixChild(node, "++", data);
+ return postfixChild(node, "--", data);
}
@Override
@@ -1147,7 +1221,7 @@ public class Debugger extends ParserVisitor implements
JexlInfo.Detail {
@Override
protected Object visit(final ASTIncrementGetNode node, final Object data) {
- return prefixChild(node, "--", data);
+ return prefixChild(node, "++", data);
}
@Override
diff --git a/src/main/java/org/apache/commons/jexl3/internal/Engine.java
b/src/main/java/org/apache/commons/jexl3/internal/Engine.java
index c701b6fd..cf3c8d49 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Engine.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Engine.java
@@ -74,7 +74,7 @@ public class Engine extends JexlEngine {
*/
private static final class UberspectHolder {
/** The default uberspector that handles all introspection patterns. */
- private static final Uberspect UBERSPECT =
+ static final Uberspect UBERSPECT =
new Uberspect(LogFactory.getLog(JexlEngine.class),
JexlUberspect.JEXL_STRATEGY,
JexlPermissions.parse());
diff --git a/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
b/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
index 7b9861e9..2ce7923f 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
@@ -19,6 +19,7 @@ package org.apache.commons.jexl3.internal;
import java.util.Iterator;
import java.util.concurrent.Callable;
+import java.util.function.Consumer;
import org.apache.commons.jexl3.JexlArithmetic;
import org.apache.commons.jexl3.JexlContext;
@@ -173,6 +174,7 @@ public class Interpreter extends InterpreterBase {
public Object getAttribute(final Object object, final Object attribute) {
return getAttribute(object, attribute, null);
}
+
/**
* Sets an attribute of an object.
*
@@ -627,7 +629,7 @@ public class Interpreter extends InterpreterBase {
return node.getLoopForm() == 0 ? forIterator(node, data) :
forLoop(node, data);
}
- protected Object forIterator(final ASTForeachStatement node, final Object
data) {
+ private Object forIterator(final ASTForeachStatement node, final Object
data) {
Object result = null;
/* first objectNode is the loop variable */
final ASTReference loopReference = (ASTReference) node.jjtGetChild(0);
@@ -704,7 +706,7 @@ public class Interpreter extends InterpreterBase {
return result;
}
- protected Object forLoop(final ASTForeachStatement node, final Object
data) {
+ private Object forLoop(final ASTForeachStatement node, final Object data) {
Object result = null;
int nc;
int form = node.getLoopForm();
@@ -733,22 +735,24 @@ public class Interpreter extends InterpreterBase {
}
// initialize after eventual creation of local lexical frame
init.jjtAccept(this, data);
- nc = 1;
+ // other inits
+ for (JexlNode moreAssignment = node.jjtGetChild(nc);
+ moreAssignment instanceof ASTAssignment;
+ moreAssignment = node.jjtGetChild(++nc)) {
+ moreAssignment.jjtAccept(this, data);
+ }
} else {
locals = null;
nc = 0;
}
- Object forEach = null;
try {
// the loop condition
final JexlNode predicate = (form & 2) != 0? node.jjtGetChild(nc++)
: null;
// the loop step
final JexlNode step = (form & 4) != 0? node.jjtGetChild(nc++) :
null;
// last child is body
- final JexlNode statement = (form & 8) != 0 ?
node.jjtGetChild(nc++) : null;
- // get an iterator for the collection/array etc via the
introspector.
- final Iterator<?> itemsIterator = null;
- int cnt = 0;
+ final JexlNode statement = (form & 8) != 0 ? node.jjtGetChild(nc)
: null;
+ // while(predicate())...
while (predicate == null ||
arithmetic.toBoolean(predicate.jjtAccept(this, data))) {
cancelCheck(node);
// the body
@@ -768,8 +772,6 @@ public class Interpreter extends InterpreterBase {
}
}
} finally {
- // closeable iterator handling
- closeIfSupported(forEach);
// restore lexical frame
if (locals != null) {
block = block.pop();
@@ -1368,15 +1370,6 @@ public class Interpreter extends InterpreterBase {
return executeAssign(node, JexlOperator.INCREMENT_AND_GET, data);
}
- /**
- * Helper for postfix assignment operators.
- * @param operator the operator
- * @return true if operator is a postfix operator (x++, y--)
- */
- private static boolean isPostfix(JexlOperator operator) {
- return operator == JexlOperator.GET_AND_INCREMENT || operator ==
JexlOperator.GET_AND_DECREMENT;
- }
-
/**
* Executes an assignment with an optional side-effect operator.
* @param node the node
@@ -1388,9 +1381,9 @@ public class Interpreter extends InterpreterBase {
cancelCheck(node);
// left contains the reference to assign to
final JexlNode left = node.jjtGetChild(0);
- ASTIdentifier var = null;
+ final ASTIdentifier var;
Object object = null;
- int symbol = -1;
+ final int symbol;
// check var decl with assign is ok
if (left instanceof ASTIdentifier) {
var = (ASTIdentifier) left;
@@ -1404,50 +1397,54 @@ public class Interpreter extends InterpreterBase {
return undefinedVariable(var, var.getName());
}
}
+ } else {
+ var = null;
+ symbol = -1;
}
boolean antish = options.isAntish();
// 0: determine initial object & property:
final int last = left.jjtGetNumChildren() - 1;
// right is the value expression to assign
- Object right = node.jjtGetNumChildren() < 2? null:
node.jjtGetChild(1).jjtAccept(this, data);
- // previous value for postfix assignment operators (x++, x--)
- Object actual = null;
+ final Object right = node.jjtGetNumChildren() < 2? null:
node.jjtGetChild(1).jjtAccept(this, data);
+ // actual value to return, right in most cases
+ Object actual = right;
// a (var?) v = ... expression
if (var != null) {
if (symbol >= 0) {
// check we are not assigning a symbol itself
if (last < 0) {
- if (assignop != null) {
- final Object self = actual = getVariable(frame, block,
var);
- right = operators.tryAssignOverload(node, assignop,
self, right);
- if (right == JexlOperator.ASSIGN) {
- return self;
+ if (assignop == null) {
+ // make the closure accessible to itself, ie capture
the currently set variable after frame creation
+ if (right instanceof Closure) {
+ ((Closure) right).setCaptured(symbol, right);
}
+ frame.set(symbol, right);
+ } else {
+ // go through potential overload
+ final Object self = getVariable(frame, block, var);
+ final Consumer<Object> f = r -> frame.set(symbol, r);
+ actual = operators.tryAssignOverload(node, assignop,
f, self, right);
}
- frame.set(symbol, right);
- // make the closure accessible to itself, ie capture the
currently set variable after frame creation
- if (right instanceof Closure) {
- ((Closure) right).setCaptured(symbol, right);
- }
- return isPostfix(assignop)? actual : right; // 1
+ return actual; // 1
}
object = getVariable(frame, block, var);
// top level is a symbol, can not be an antish var
antish = false;
} else {
// check we are not assigning direct global
+ final String name = var.getName();
if (last < 0) {
- if (assignop != null) {
- final Object self = actual =
context.get(var.getName());
- right = operators.tryAssignOverload(node, assignop,
self, right);
- if (right == JexlOperator.ASSIGN) {
- return self;
- }
+ if (assignop == null) {
+ setContextVariable(node, name, right);
+ } else {
+ // go through potential overload
+ final Object self = context.get(name);
+ final Consumer<Object> f = r ->
setContextVariable(node, name, r);
+ actual = operators.tryAssignOverload(node, assignop,
f, self, right);
}
- setContextVariable(node, var.getName(), right);
- return isPostfix(assignop)? actual : right; // 2
+ return actual; // 2
}
- object = context.get(var.getName());
+ object = context.get(name);
// top level accesses object, can not be an antish var
if (object != null) {
antish = false;
@@ -1510,19 +1507,18 @@ public class Interpreter extends InterpreterBase {
if (propertyId != null) {
// deal with creating/assignining antish variable
if (antish && ant != null && object == null &&
!propertyId.isSafe() && !propertyId.isExpression()) {
- if (last > 0) {
- ant.append('.');
- }
+ ant.append('.');
ant.append(propertyId.getName());
- if (assignop != null) {
+ final String name = ant.toString();
+ if (assignop == null) {
+ setContextVariable(propertyNode, name, right);
+ } else {
final Object self = actual = context.get(ant.toString());
- right = operators.tryAssignOverload(node, assignop, self,
right);
- if (right == JexlOperator.ASSIGN) {
- return self;
- }
+ final JexlNode pnode = propertyNode;
+ final Consumer<Object> assign = r ->
setContextVariable(pnode, name, r);
+ actual = operators.tryAssignOverload(node, assignop,
assign, self, right);
}
- setContextVariable(propertyNode, ant.toString(), right);
- return isPostfix(assignop)? actual : right; // 3
+ return actual; // 3
}
// property of an object ?
property = evalIdentifier(propertyId);
@@ -1546,15 +1542,26 @@ public class Interpreter extends InterpreterBase {
return unsolvableProperty(objectNode, "<null>.<?>", true, null);
}
// 3: one before last, assign
- if (assignop != null) {
- final Object self = actual = getAttribute(object, property,
propertyNode);
- right = operators.tryAssignOverload(node, assignop, self, right);
- if (right == JexlOperator.ASSIGN) {
- return self;
- }
+ if (assignop == null) {
+ setAttribute(object, property, right, propertyNode);
+ } else {
+ final Object self = getAttribute(object, property, propertyNode);
+ final Object o = object;
+ final JexlNode n = propertyNode;
+ final Consumer<Object> assign = r -> setAttribute(o, property, r,
n);
+ actual = operators.tryAssignOverload(node, assignop, assign, self,
right);
+ }
+ return actual;
+ }
+
+ @Override
+ protected Object visit(final ASTDefineVars node, final Object data) {
+ final int argc = node.jjtGetNumChildren();
+ Object result = null;
+ for (int i = 0; i < argc; i++) {
+ result = node.jjtGetChild(i).jjtAccept(this, data);
}
- setAttribute(object, property, right, propertyNode);
- return isPostfix(assignop)? actual : right; // 4
+ return result;
}
@Override
@@ -1756,7 +1763,7 @@ public class Interpreter extends InterpreterBase {
return call.eval(mCALL);
}
// functor is a var, may be method is a global one ?
- if (isavar && target == context) {
+ if (isavar) {
if (call.isContextMethod(methodName, argv)) {
return call.eval(methodName);
}
@@ -1824,8 +1831,8 @@ public class Interpreter extends InterpreterBase {
}
}
boolean narrow = false;
- JexlMethod ctor = null;
Funcall funcall = null;
+ JexlMethod ctor;
while (true) {
// try as stated
ctor = uberspect.getConstructor(target, argv);
diff --git a/src/main/java/org/apache/commons/jexl3/internal/Operators.java
b/src/main/java/org/apache/commons/jexl3/internal/Operators.java
index aa548e0e..f0da705e 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Operators.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Operators.java
@@ -16,7 +16,6 @@
*/
package org.apache.commons.jexl3.internal;
-import java.lang.reflect.Method;
import org.apache.commons.jexl3.JexlArithmetic;
import org.apache.commons.jexl3.JexlEngine;
import org.apache.commons.jexl3.JexlException;
@@ -26,6 +25,9 @@ import org.apache.commons.jexl3.introspection.JexlMethod;
import org.apache.commons.jexl3.introspection.JexlUberspect;
import org.apache.commons.jexl3.parser.JexlNode;
+import java.lang.reflect.Method;
+import java.util.function.Consumer;
+
/**
* Helper class to deal with operator overloading and specifics.
* @since 3.0
@@ -95,7 +97,7 @@ public class Operators {
* @param args the arguments
* @return the result of the operator evaluation or TRY_FAILED
*/
- protected Object tryOverload(final JexlNode node, final JexlOperator
operator, final Object... args) {
+ protected Object tryOverload(final JexlNode node, final JexlOperator
operator, Object... args) {
if (operators != null && operators.overloads(operator)) {
final JexlArithmetic arithmetic = interpreter.arithmetic;
final boolean cache = interpreter.cache;
@@ -113,18 +115,39 @@ public class Operators {
final JexlMethod vm = operators.getOperator(operator, args);
if (vm != null && !isArithmetic(vm)) {
final Object result = vm.invoke(arithmetic, args);
- if (cache) {
+ if (cache && !vm.tryFailed(result)) {
node.jjtSetValue(vm);
}
return result;
}
} catch (final Exception xany) {
- return interpreter.operatorError(node, operator, xany);
+ // ignore return if lenient, will return try_failed
+ interpreter.operatorError(node, operator, xany);
}
}
return JexlEngine.TRY_FAILED;
}
+ /**
+ * Helper for postfix assignment operators.
+ * @param operator the operator
+ * @return true if operator is a postfix operator (x++, y--)
+ */
+ private static boolean isPostfix(JexlOperator operator) {
+ return operator == JexlOperator.GET_AND_INCREMENT || operator ==
JexlOperator.GET_AND_DECREMENT;
+ }
+
+ /**
+ * Tidy arguments based on operator arity.
+ * <p>The interpreter may add a null to the arguments of operator
expecting only one parameter.</p>
+ * @param operator the operator
+ * @param args the arguements (as seen by the interpreter)
+ * @return the tidied arguments
+ */
+ private Object[] arguments(JexlOperator operator, Object...args) {
+ return operator.getArity() == 1 && args.length > 1 ? new
Object[]{args[0]} : args;
+ }
+
/**
* Evaluates an assign operator.
* <p>
@@ -139,72 +162,91 @@ public class Operators {
* JexlEngine.TRY_FAILED if no operation was performed,
* the value to use as the side effect argument otherwise
*/
- protected Object tryAssignOverload(final JexlNode node, final JexlOperator
operator, final Object...args) {
+ protected Object tryAssignOverload(final JexlNode node,
+ final JexlOperator operator,
+ final Consumer<Object> assignFun,
+ final Object...args) {
final JexlArithmetic arithmetic = interpreter.arithmetic;
if (args.length < operator.getArity()) {
return JexlEngine.TRY_FAILED;
}
- // try to call overload with side effect
- Object result = tryOverload(node, operator, args);
- if (result != JexlEngine.TRY_FAILED) {
- return result;
- }
- // call base operator
- final JexlOperator base = operator.getBaseOperator();
- if (base == null) {
- throw new IllegalArgumentException("must be called with a
side-effect operator");
- }
- if (operators != null && operators.overloads(base)) {
- // in case there is an overload on the base operator
- try {
- final JexlMethod vm = operators.getOperator(base, args);
- if (vm != null) {
- result = vm.invoke(arithmetic, args);
- if (result != JexlEngine.TRY_FAILED) {
- return result;
- }
+ Object result;
+ try {
+ // if some overloads exist...
+ if (operators != null) {
+ // try to call overload with side effect; the object is modified
+ result = tryOverload(node, operator, arguments(operator, args));
+ if (result != JexlEngine.TRY_FAILED) {
+ return result; // 1
+ }
+ // try to call base overload (ie + for +=)
+ final JexlOperator base = operator.getBaseOperator();
+ if (base != null && operators.overloads(base)) {
+ result = tryOverload(node, base, arguments(base, args));
+ if (result != JexlEngine.TRY_FAILED) {
+ assignFun.accept(result);
+ return isPostfix(operator) ? args[0] : result; // 2
}
- } catch (final Exception xany) {
- interpreter.operatorError(node, base, xany);
}
}
// base eval
- try {
- switch (operator) {
- case SELF_ADD:
- return arithmetic.add(args[0], args[1]);
- case SELF_SUBTRACT:
- return arithmetic.subtract(args[0], args[1]);
- case SELF_MULTIPLY:
- return arithmetic.multiply(args[0], args[1]);
- case SELF_DIVIDE:
- return arithmetic.divide(args[0], args[1]);
- case SELF_MOD:
- return arithmetic.mod(args[0], args[1]);
- case SELF_AND:
- return arithmetic.and(args[0], args[1]);
- case SELF_OR:
- return arithmetic.or(args[0], args[1]);
- case SELF_XOR:
- return arithmetic.xor(args[0], args[1]);
- case SELF_SHIFTLEFT:
- return arithmetic.shiftLeft(args[0], args[1]);
- case SELF_SHIFTRIGHT:
- return arithmetic.shiftRight(args[0], args[1]);
- case SELF_SHIFTRIGHTU:
- return arithmetic.shiftRightUnsigned(args[0], args[1]);
- case INCREMENT_AND_GET:
- case GET_AND_INCREMENT:
- return arithmetic.increment(args[0]);
- case DECREMENT_AND_GET:
- case GET_AND_DECREMENT:
- return arithmetic.decrement(args[0]);
- default:
- // unexpected, new operator added?
- throw new
UnsupportedOperationException(operator.getOperatorSymbol());
+ switch (operator) {
+ case SELF_ADD:
+ result = arithmetic.add(args[0], args[1]);
+ break;
+ case SELF_SUBTRACT:
+ result = arithmetic.subtract(args[0], args[1]);
+ break;
+ case SELF_MULTIPLY:
+ result = arithmetic.multiply(args[0], args[1]);
+ break;
+ case SELF_DIVIDE:
+ result = arithmetic.divide(args[0], args[1]);
+ break;
+ case SELF_MOD:
+ result = arithmetic.mod(args[0], args[1]);
+ break;
+ case SELF_AND:
+ result = arithmetic.and(args[0], args[1]);
+ break;
+ case SELF_OR:
+ result = arithmetic.or(args[0], args[1]);
+ break;
+ case SELF_XOR:
+ result = arithmetic.xor(args[0], args[1]);
+ break;
+ case SELF_SHIFTLEFT:
+ result = arithmetic.shiftLeft(args[0], args[1]);
+ break;
+ case SELF_SHIFTRIGHT:
+ result = arithmetic.shiftRight(args[0], args[1]);
+ break;
+ case SELF_SHIFTRIGHTU:
+ result = arithmetic.shiftRightUnsigned(args[0], args[1]);
+ break;
+ case INCREMENT_AND_GET:
+ result = arithmetic.increment(args[0]);
+ break;
+ case DECREMENT_AND_GET:
+ result = arithmetic.decrement(args[0]);
+ break;
+ case GET_AND_INCREMENT:
+ result = args[0];
+ assignFun.accept(arithmetic.increment(result));
+ return result; // 3
+ case GET_AND_DECREMENT: {
+ result = args[0];
+ assignFun.accept(arithmetic.decrement(result));
+ return result; // 4
+ }
+ default:
+ // unexpected, new operator added?
+ throw new
UnsupportedOperationException(operator.getOperatorSymbol());
}
+ assignFun.accept(result);
+ return result; // 5
} catch (final Exception xany) {
- interpreter.operatorError(node, base, xany);
+ interpreter.operatorError(node, operator, xany);
}
return JexlEngine.TRY_FAILED;
}
diff --git a/src/main/java/org/apache/commons/jexl3/internal/Scope.java
b/src/main/java/org/apache/commons/jexl3/internal/Scope.java
index ee14bbd8..2951ff60 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Scope.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Scope.java
@@ -84,7 +84,7 @@ public final class Scope {
public Scope(final Scope scope, final String... parameters) {
if (parameters != null) {
parms = parameters.length;
- namedVariables = new LinkedHashMap<String, Integer>();
+ namedVariables = new LinkedHashMap<>();
for (int p = 0; p < parms; ++p) {
namedVariables.put(parameters[p], p);
}
@@ -140,10 +140,10 @@ public final class Scope {
final Integer pr = parent.getSymbol(name, true);
if (pr != null) {
if (capturedVariables == null) {
- capturedVariables = new LinkedHashMap<Integer, Integer>();
+ capturedVariables = new LinkedHashMap<>();
}
if (namedVariables == null) {
- namedVariables = new LinkedHashMap<String, Integer>();
+ namedVariables = new LinkedHashMap<>();
}
register = namedVariables.size();
namedVariables.put(name, register);
@@ -193,7 +193,7 @@ public final class Scope {
*/
public int declareParameter(final String name) {
if (namedVariables == null) {
- namedVariables = new LinkedHashMap<String, Integer>();
+ namedVariables = new LinkedHashMap<>();
} else if (vars > 0) {
throw new IllegalStateException("cant declare parameters after
variables");
}
@@ -214,9 +214,9 @@ public final class Scope {
* @param name the variable name
* @return the register index storing this variable
*/
- public int declareVariable(final String name, boolean lexical, boolean
constant) {
+ public int declareVariable(final String name) {
if (namedVariables == null) {
- namedVariables = new LinkedHashMap<String, Integer>();
+ namedVariables = new LinkedHashMap<>();
}
Integer register = namedVariables.get(name);
if (register == null) {
@@ -228,7 +228,7 @@ public final class Scope {
final Integer pr = parent.getSymbol(name, true);
if (pr != null) {
if (capturedVariables == null) {
- capturedVariables = new LinkedHashMap<Integer,
Integer>();
+ capturedVariables = new LinkedHashMap<>();
}
capturedVariables.put(register, pr);
}
@@ -285,7 +285,7 @@ public final class Scope {
*/
public String[] getCapturedVariables() {
if (capturedVariables != null) {
- final List<String> captured = new ArrayList<String>(vars);
+ final List<String> captured = new ArrayList<>(vars);
for (final Map.Entry<String, Integer> entry :
namedVariables.entrySet()) {
final int symnum = entry.getValue();
if (symnum >= parms && capturedVariables.containsKey(symnum)) {
@@ -293,7 +293,7 @@ public final class Scope {
}
}
if (!captured.isEmpty()) {
- return captured.toArray(new String[captured.size()]);
+ return captured.toArray(new String[0]);
}
}
return EMPTY_STRS;
@@ -328,7 +328,7 @@ public final class Scope {
* @param bound number of known bound parameters (curry)
* @return the parameter names
*/
- protected String[] getParameters(final int bound) {
+ String[] getParameters(final int bound) {
final int unbound = parms - bound;
if ((namedVariables == null) || (unbound <= 0)) {
return EMPTY_STRS;
@@ -352,14 +352,14 @@ public final class Scope {
if ((namedVariables == null) || (vars <= 0)) {
return EMPTY_STRS;
}
- final List<String> locals = new ArrayList<String>(vars);
+ final List<String> locals = new ArrayList<>(vars);
for (final Map.Entry<String, Integer> entry :
namedVariables.entrySet()) {
final int symnum = entry.getValue();
if (symnum >= parms && (capturedVariables == null ||
!capturedVariables.containsKey(symnum))) {
locals.add(entry.getKey());
}
}
- return locals.toArray(new String[locals.size()]);
+ return locals.toArray(new String[0]);
}
}
diff --git a/src/main/java/org/apache/commons/jexl3/internal/ScriptVisitor.java
b/src/main/java/org/apache/commons/jexl3/internal/ScriptVisitor.java
index 5964d348..9da2182d 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/ScriptVisitor.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/ScriptVisitor.java
@@ -21,7 +21,7 @@ import org.apache.commons.jexl3.JexlScript;
import org.apache.commons.jexl3.parser.*;
/**
- * Fully abstract to avoid public interface exposition.
+ * Concrete visitor base, used for feature and operator controllers.
*/
public class ScriptVisitor extends ParserVisitor {
/**
@@ -116,6 +116,11 @@ public class ScriptVisitor extends ParserVisitor {
return visitNode(node, data);
}
+ @Override
+ protected Object visit(final ASTDefineVars node, final Object data) {
+ return visitNode(node, data);
+ }
+
@Override
protected Object visit(final ASTReference node, final Object data) {
return visitNode(node, data);
diff --git a/src/main/java/org/apache/commons/jexl3/parser/JexlParser.java
b/src/main/java/org/apache/commons/jexl3/parser/JexlParser.java
index b5db9195..2edef122 100644
--- a/src/main/java/org/apache/commons/jexl3/parser/JexlParser.java
+++ b/src/main/java/org/apache/commons/jexl3/parser/JexlParser.java
@@ -237,7 +237,7 @@ public abstract class JexlParser extends StringParser {
}
/**
- * Gets the lexical unit currently used by this parser.
+ * Gets the lexical unit used by this parser.
* @return the named register map
*/
protected LexicalUnit getUnit() {
@@ -399,7 +399,7 @@ public abstract class JexlParser extends StringParser {
if (scope == null) {
scope = new Scope(null);
}
- final int symbol = scope.declareVariable(name, true, true);
+ final int symbol = scope.declareVariable(name);
variable.setSymbol(symbol, name);
variable.setLexical(true);
if (scope.isCapturedSymbol(symbol)) {
@@ -433,7 +433,7 @@ public abstract class JexlParser extends StringParser {
if (scope == null) {
scope = new Scope(null);
}
- final int symbol = scope.declareVariable(name, lexical, constant);
+ final int symbol = scope.declareVariable(name);
variable.setSymbol(symbol, name);
variable.setLexical(lexical);
variable.setConstant(constant);
@@ -701,7 +701,6 @@ public abstract class JexlParser extends StringParser {
protected void throwParsingException(final Token parsed) {
JexlInfo xinfo = null;
String msg = "unrecoverable state";
- JexlException.Parsing xparse = null;
Token token = parsed;
if (token == null) {
token = this.getToken(0);
diff --git a/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt
b/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt
index 18ad3509..074c8471 100644
--- a/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt
+++ b/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt
@@ -285,9 +285,11 @@ TOKEN_MGR_DECLS : {
<*> TOKEN :
{
< STRING_LITERAL:
- "\"" (~["\"","\\","\n","\r","\u2028","\u2029"] | "\\"
~["\n","\r","\u2028","\u2029"])* "\""
+ "\"" (~["\"","\\","\n","\r","\t","\f","\b","\u2028","\u2029"]
+ | "\\" ~["\n","\r","\t","\f","\b","\u2028","\u2029"])* "\""
|
- "'" (~["'","\\","\n","\r","\u2028","\u2029"] | "\\"
~["\n","\r","\u2028","\u2029"])* "'"
+ "'" (~["'","\\","\n","\r","\t","\f","\b","\u2028","\u2029"]
+ | "\\" ~["\n","\r","\t","\f","\b","\u2028","\u2029"])* "'"
> { popDot(); } /* Revert state to default if was DOT_ID. */
}
@@ -301,7 +303,7 @@ TOKEN_MGR_DECLS : {
<*> TOKEN :
{
< REGEX_LITERAL:
- "~" "/" (~["/","\n","\r","\u2028","\u2029"] | "\\" "/" )* "/"
+ "~" "/" (~["/","\n","\r","\t","\f","\b","\u2028","\u2029"] | "\\" "/" )*
"/"
> { popDot(); } /* Revert state to default if was DOT_ID. */
}
@@ -466,11 +468,11 @@ void ForEachVar() #Reference : {}
void Var() #void : {}
{
- <VAR> DefineVar() (<COMMA> DefineVar())*
+ (<VAR> DefineVar() (<COMMA> DefineVar())*) #DefineVars(>1)
|
- <LET> DefineLet() (<COMMA> DefineLet())*
+ (<LET> DefineLet() (<COMMA> DefineLet())*) #DefineVars(>1)
|
- <CONST> DefineConst() (<COMMA> DefineConst())*
+ (<CONST> DefineConst() (<COMMA> DefineConst())*) #DefineVars(>1)
}
void DefineVar() #void : {}
diff --git a/src/main/java/org/apache/commons/jexl3/parser/ParserVisitor.java
b/src/main/java/org/apache/commons/jexl3/parser/ParserVisitor.java
index f03d4a99..e457c417 100644
--- a/src/main/java/org/apache/commons/jexl3/parser/ParserVisitor.java
+++ b/src/main/java/org/apache/commons/jexl3/parser/ParserVisitor.java
@@ -16,8 +16,6 @@
*/
package org.apache.commons.jexl3.parser;
-import org.apache.commons.jexl3.JexlOperator;
-
/**
* Fully abstract to avoid public interface exposition.
*/
@@ -64,6 +62,8 @@ public abstract class ParserVisitor {
protected abstract Object visit(ASTVar node, Object data);
+ protected abstract Object visit(ASTDefineVars node, Object data);
+
protected abstract Object visit(ASTReference node, Object data);
protected abstract Object visit(ASTTernaryNode node, Object data);
@@ -194,13 +194,13 @@ public abstract class ParserVisitor {
protected abstract Object visit(ASTSetShiftRightUnsignedNode node, final
Object data);
- protected abstract Object visit(final ASTGetDecrementNode node, final
Object data);
+ protected abstract Object visit(ASTGetDecrementNode node, final Object
data);
- protected abstract Object visit(final ASTGetIncrementNode node, final
Object data);
+ protected abstract Object visit(ASTGetIncrementNode node, final Object
data);
- protected abstract Object visit(final ASTDecrementGetNode node, final
Object data);
+ protected abstract Object visit(ASTDecrementGetNode node, final Object
data);
- protected abstract Object visit(final ASTIncrementGetNode node, final
Object data);
+ protected abstract Object visit(ASTIncrementGetNode node, final Object
data);
protected abstract Object visit(ASTJxltLiteral node, Object data);
diff --git a/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java
b/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java
index 8c1ca580..61778347 100644
--- a/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java
+++ b/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java
@@ -33,8 +33,10 @@ import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.MathContext;
import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -384,6 +386,143 @@ public class ArithmeticTest extends JexlTestCase {
}
}
+ @Test
+ public void testMinusMinusPrefix() throws Exception {
+ asserter.setVariable("aByte", new Byte((byte) 2));
+ asserter.setVariable("aShort", new Short((short) 3));
+ asserter.setVariable("anInteger", new Integer(4));
+ asserter.setVariable("aLong", new Long(5));
+ asserter.setVariable("aFloat", new Float(6.6));
+ asserter.setVariable("aDouble", new Double(7.7));
+ asserter.setVariable("aBigInteger", new BigInteger("8"));
+ asserter.setVariable("aBigDecimal", new BigDecimal("9.9"));
+ asserter.setVariable("aString", "forty-two");
+
+ asserter.assertExpression("--aByte", new Byte((byte) 1));
+ asserter.assertExpression("--aShort", new Short((short) 2));
+ asserter.assertExpression("--anInteger", new Integer(3));
+ asserter.assertExpression("--aLong", new Long(4));
+ asserter.assertExpression("--aFloat", new Float(5.6));
+ asserter.assertExpression("--aDouble", new Double(6.7));
+ asserter.assertExpression("--aBigInteger", new BigInteger("7"));
+ asserter.assertExpression("--aBigDecimal", new BigDecimal("8.9"));
+
+ asserter.failExpression("aString--", "--", String::contains);
+ }
+
+ @Test
+ public void testMinusMinusPostfix() throws Exception {
+ asserter.setVariable("aByte", new Byte((byte) 2));
+ asserter.setVariable("aShort", new Short((short) 3));
+ asserter.setVariable("anInteger", new Integer(4));
+ asserter.setVariable("aLong", new Long(5));
+ asserter.setVariable("aFloat", new Float(6.6));
+ asserter.setVariable("aDouble", new Double(7.7));
+ asserter.setVariable("aBigInteger", new BigInteger("8"));
+ asserter.setVariable("aBigDecimal", new BigDecimal("9.9"));
+ asserter.setVariable("aString", "forty-two");
+
+ asserter.assertExpression("aByte--",new Byte((byte) 2));
+ asserter.assertExpression("aShort--", new Short((short) 3));
+ asserter.assertExpression("anInteger--", new Integer(4));
+ asserter.assertExpression("aLong--", new Long(5));
+ asserter.assertExpression("aFloat--", new Float(6.6));
+ asserter.assertExpression("aDouble--", new Double(7.7));
+ asserter.assertExpression("aBigInteger--", new BigInteger("8"));
+ asserter.assertExpression("aBigDecimal--", new BigDecimal("9.9"));
+
+ asserter.assertExpression("aByte", new Byte((byte) 1));
+ asserter.assertExpression("aShort", new Short((short) 2));
+ asserter.assertExpression("anInteger", new Integer(3));
+ asserter.assertExpression("aLong", new Long(4));
+ asserter.assertExpression("aFloat", new Float(5.6));
+ asserter.assertExpression("aDouble", new Double(6.7));
+ asserter.assertExpression("aBigInteger", new BigInteger("7"));
+ asserter.assertExpression("aBigDecimal", new BigDecimal("8.9"));
+
+ asserter.failExpression("aString--", "--", String::contains);
+ }
+
+ @Test
+ public void testPlusPlusPrefix() throws Exception {
+ asserter.setVariable("aByte", new Byte((byte) 0));
+ asserter.setVariable("aShort", new Short((short) 1));
+ asserter.setVariable("anInteger", new Integer(2));
+ asserter.setVariable("aLong", new Long(3));
+ asserter.setVariable("aFloat", new Float(4.4));
+ asserter.setVariable("aDouble", new Double(5.5));
+ asserter.setVariable("aBigInteger", new BigInteger("6"));
+ asserter.setVariable("aBigDecimal", new BigDecimal("7.7"));
+ asserter.setVariable("aString", "forty-two");
+
+ asserter.assertExpression("++aByte", new Byte((byte) 1));
+ asserter.assertExpression("++aShort", new Short((short) 2));
+ asserter.assertExpression("++anInteger", new Integer(3));
+ asserter.assertExpression("++aLong", new Long(4));
+ asserter.assertExpression("++aFloat", new Float(5.4));
+ asserter.assertExpression("++aDouble", new Double(6.5));
+ asserter.assertExpression("++aBigInteger", new BigInteger("7"));
+ asserter.assertExpression("++aBigDecimal", new BigDecimal("8.7"));
+
+ asserter.failExpression("++aString", "++", String::contains);
+ }
+
+ @Test
+ public void testPlusPlusPostfix() throws Exception {
+ asserter.setVariable("aByte", new Byte((byte) 0));
+ asserter.setVariable("aShort", new Short((short) 1));
+ asserter.setVariable("anInteger", new Integer(2));
+ asserter.setVariable("aLong", new Long(3));
+ asserter.setVariable("aFloat", new Float(4.4));
+ asserter.setVariable("aDouble", new Double(5.5));
+ asserter.setVariable("aBigInteger", new BigInteger("6"));
+ asserter.setVariable("aBigDecimal", new BigDecimal("7.7"));
+ asserter.setVariable("aString", "forty-two");
+
+ asserter.assertExpression("aByte++", new Byte((byte) 0));
+ asserter.assertExpression("aShort++", new Short((short) 1));
+ asserter.assertExpression("anInteger++", new Integer(2));
+ asserter.assertExpression("aLong++", new Long(3));
+ asserter.assertExpression("aFloat++", new Float(4.4));
+ asserter.assertExpression("aDouble++", new Double(5.5));
+ asserter.assertExpression("aBigInteger++", new BigInteger("6"));
+ asserter.assertExpression("aBigDecimal++", new BigDecimal("7.7"));
+
+ asserter.assertExpression("aByte", new Byte((byte) 1));
+ asserter.assertExpression("aShort", new Short((short) 2));
+ asserter.assertExpression("anInteger", new Integer(3));
+ asserter.assertExpression("aLong", new Long(4));
+ asserter.assertExpression("aFloat", new Float(5.4));
+ asserter.assertExpression("aDouble", new Double(6.5));
+ asserter.assertExpression("aBigInteger", new BigInteger("7"));
+ asserter.assertExpression("aBigDecimal", new BigDecimal("8.7"));
+
+ asserter.failExpression("aString++", "++", String::contains);
+ }
+
+ @Test
+ public void testNarrowBig() throws Exception {
+ List<String> ls = Arrays.asList("zero", "one", "two");
+ asserter.setVariable("list",ls);
+ asserter.setVariable("aBigDecimal", new BigDecimal("1"));
+ asserter.setVariable("aBigInteger", new BigDecimal("1"));
+ asserter.assertExpression("list.get(aBigDecimal)", "one");
+ asserter.assertExpression("list.get(aBigInteger)", "one");
+ }
+
+ @Test
+ public void testNarrowBigDecimal() throws Exception {
+ asserter.setVariable("bi420", BigInteger.valueOf(420));
+ asserter.setVariable("bi10", BigInteger.valueOf(10));
+ asserter.setVariable("bd420", new BigDecimal("420"));
+ asserter.setVariable("bd10", new BigDecimal("10"));
+ asserter.assertExpression("420 / bi10", 42);
+ asserter.assertExpression("420l / bi10", 42L);
+ asserter.assertExpression("bi420 / 420", 1);
+ asserter.assertExpression("bi420 / 420l", 1L);
+ asserter.assertExpression("bd420 / 10", new BigDecimal("42"));
+ }
+
/**
* test some simple mathematical calculations
*/
diff --git a/src/test/java/org/apache/commons/jexl3/ForEachTest.java
b/src/test/java/org/apache/commons/jexl3/ForEachTest.java
index 48308f0a..0207cfbc 100644
--- a/src/test/java/org/apache/commons/jexl3/ForEachTest.java
+++ b/src/test/java/org/apache/commons/jexl3/ForEachTest.java
@@ -19,8 +19,11 @@ package org.apache.commons.jexl3;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
+
+import org.apache.commons.jexl3.internal.Debugger;
import org.junit.Assert;
import org.junit.Test;
@@ -36,6 +39,30 @@ public class ForEachTest extends JexlTestCase {
super("ForEachTest");
}
+ @Test public void testForLoop0b0() {
+ String src = "(l)->{ for(let x = 0, y = 0; x < 4; ++x) l.add(x) }";
+ JexlEngine jexl = new JexlBuilder().safe(false).create();
+ JexlScript script = jexl.createScript(src);
+ List<Integer> l = new ArrayList<>();
+ Object result = script.execute(null, l);
+ Assert.assertNotNull(result);
+ Assert.assertEquals(Arrays.asList(0, 1, 2, 3), l);
+ String resrc = toString(script);
+ Assert.assertEquals(src, resrc);
+ }
+
+ @Test public void testForLoop0a() {
+ String src = "(l)->{ for(let x = 0; x < 4; ++x) { l.add(x); } }";
+ JexlEngine jexl = new JexlBuilder().safe(false).create();
+ JexlScript script = jexl.createScript(src);
+ List<Integer> l = new ArrayList<>();
+ Object result = script.execute(null, l);
+ Assert.assertNotNull(result);
+ Assert.assertEquals(Arrays.asList(0, 1, 2, 3), l);
+ String resrc = toString(script);
+ Assert.assertEquals(src, resrc);
+ }
+
@Test
public void testForEachWithEmptyStatement() throws Exception {
final JexlScript e = JEXL.createScript("for(item : list) ;");
diff --git a/src/test/java/org/apache/commons/jexl3/JexlTestCase.java
b/src/test/java/org/apache/commons/jexl3/JexlTestCase.java
index a8249556..68dafbfc 100644
--- a/src/test/java/org/apache/commons/jexl3/JexlTestCase.java
+++ b/src/test/java/org/apache/commons/jexl3/JexlTestCase.java
@@ -20,6 +20,7 @@ package org.apache.commons.jexl3;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
+import org.apache.commons.jexl3.internal.Debugger;
import org.apache.commons.jexl3.internal.OptionsContext;
import org.apache.commons.jexl3.internal.Util;
import org.apache.commons.jexl3.internal.introspection.Uberspect;
@@ -125,6 +126,12 @@ public class JexlTestCase {
return arg.trim().replaceAll("\\s+", " ");
}
+ public static String toString(JexlScript script) {
+ Debugger d = new Debugger().lineFeed("").indentation(0);
+ d.debug(script);
+ return d.toString();
+ }
+
/**
* A very secure singleton.
*/
diff --git a/src/test/java/org/apache/commons/jexl3/LexicalTest.java
b/src/test/java/org/apache/commons/jexl3/LexicalTest.java
index e5faaef9..43d0fd9f 100644
--- a/src/test/java/org/apache/commons/jexl3/LexicalTest.java
+++ b/src/test/java/org/apache/commons/jexl3/LexicalTest.java
@@ -520,7 +520,7 @@ public class LexicalTest {
final JexlEngine jexl = new JexlBuilder().strict(true).create();
final JexlScript script = jexl.createScript("let x = 32; (()->{
for(let x : null) { let c = 0; { return x; } } } )(); ");
Assert.assertNotNull(script);
- String dbg = script.getParsedText();
+ String dbg = JexlTestCase.toString(script);
String src = script.getSourceText();
Assert.assertTrue(JexlTestCase.equalsIgnoreWhiteSpace(src, dbg));
}
diff --git a/src/test/java/org/apache/commons/jexl3/SideEffectTest.java
b/src/test/java/org/apache/commons/jexl3/SideEffectTest.java
index a198165a..022f22d3 100644
--- a/src/test/java/org/apache/commons/jexl3/SideEffectTest.java
+++ b/src/test/java/org/apache/commons/jexl3/SideEffectTest.java
@@ -23,12 +23,16 @@ import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Collections;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.apache.commons.jexl3.internal.introspection.NoJexlTest;
+import org.apache.commons.jexl3.jexl342.OptionalArithmetic;
import org.apache.commons.jexl3.junit.Asserter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Assert;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
@@ -363,70 +367,157 @@ public class SideEffectTest extends JexlTestCase {
Object result;
script = jexl.createScript("(x, y)->{ x += y }");
result = script.execute(jc, 3115, 15);
- Assert.assertEquals(3115 + 15, result);
+ Assert.assertEquals(3115 + 15, result);
final Var v0 = new Var(3115);
result = script.execute(jc, v0, new Var(15));
Assert.assertEquals(result, v0);
- Assert.assertEquals(3115 + 15, v0.value);
+ Assert.assertEquals(3115 + 15, v0.value);
script = jexl.createScript("(x, y)->{ x -= y}");
result = script.execute(jc, 3115, 15);
- Assert.assertEquals(3115 - 15, result);
+ Assert.assertEquals(3115 - 15, result);
final Var v1 = new Var(3115);
result = script.execute(jc, v1, new Var(15));
Assert.assertNotEquals(result, v1); // not a real side effect
- Assert.assertEquals(3115 - 15, ((Var) result).value);
+ Assert.assertEquals(3115 - 15, ((Var) result).value);
script = jexl.createScript("(x, y)->{ x *= y }");
result = script.execute(jc, 3115, 15);
- Assert.assertEquals(3115 * 15, result);
+ Assert.assertEquals(3115 * 15, result);
final Var v2 = new Var(3115);
result = script.execute(jc, v2, new Var(15));
Assert.assertEquals(result, v2);
- Assert.assertEquals(3115 * 15, v2.value);
+ Assert.assertEquals(3115 * 15, v2.value);
script = jexl.createScript("(x, y)->{ x /= y }");
result = script.execute(jc, 3115, 15);
- Assert.assertEquals(3115 / 15, result);
+ Assert.assertEquals(3115 / 15, result);
final Var v3 = new Var(3115);
result = script.execute(jc, v3, new Var(15));
Assert.assertEquals(result, v3);
- Assert.assertEquals(3115 / 15, v3.value);
+ Assert.assertEquals(3115 / 15, v3.value);
script = jexl.createScript("(x, y)->{ x %= y }");
result = script.execute(jc, 3115, 15);
- Assert.assertEquals(3115 % 15, result);
+ Assert.assertEquals(3115 % 15, result);
final Var v4 = new Var(3115);
result = script.execute(jc, v4, new Var(15));
Assert.assertEquals(result, v4);
- Assert.assertEquals(3115 % 15, v4.value);
+ Assert.assertEquals(3115 % 15, v4.value);
script = jexl.createScript("(x, y)->{ x &= y }");
result = script.execute(jc, 3115, 15);
- Assert.assertEquals(3115L & 15, result);
+ Assert.assertEquals(3115L & 15, result);
final Var v5 = new Var(3115);
result = script.execute(jc, v5, new Var(15));
Assert.assertEquals(result, v5);
- Assert.assertEquals(3115 & 15, v5.value);
+ Assert.assertEquals(3115 & 15, v5.value);
script = jexl.createScript("(x, y)->{ x |= y }");
result = script.execute(jc, 3115, 15);
- Assert.assertEquals(3115L | 15, result);
+ Assert.assertEquals(3115L | 15, result);
final Var v6 = new Var(3115);
result = script.execute(jc, v6, new Var(15));
Assert.assertEquals(result, v6);
- Assert.assertEquals(3115L | 15, v6.value);
+ Assert.assertEquals(3115L | 15, v6.value);
script = jexl.createScript("(x, y)->{ x ^= y }");
result = script.execute(jc, 3115, 15);
- Assert.assertEquals(3115L ^ 15, result);
+ Assert.assertEquals(3115L ^ 15, result);
final Var v7 = new Var(3115);
result = script.execute(jc, v7, new Var(15));
Assert.assertEquals(result, v7);
- Assert.assertEquals(3115L ^ 15, v7.value);
+ Assert.assertEquals(3115L ^ 15, v7.value);
+
+ script = jexl.createScript("(x, y)->{ x >>>= y }");
+ result = script.execute(jc, 234453115, 5);
+ Assert.assertEquals(234453115L >>> 5, result);
+ final Var v8 = new Var(234453115);
+ result = script.execute(jc, v8, 5);
+ Assert.assertEquals(result, v8);
+ Assert.assertEquals(234453115L >>> 5, v8.value);
+
+ script = jexl.createScript("(x, y)->{ x >>= y }");
+ result = script.execute(jc, 435566788L, 7);
+ Assert.assertEquals(435566788L >> 7, result);
+ final Var v9 = new Var(435566788);
+ result = script.execute(jc, v9, 7);
+ Assert.assertEquals(result, v9);
+ Assert.assertEquals(435566788L >> 7, v9.value);
+
+ script = jexl.createScript("(x, y)->{ x <<= y }");
+ result = script.execute(jc, 3115, 2);
+ Assert.assertEquals(3115L << 2, result);
+ final Var v10 = new Var(3115);
+ result = script.execute(jc, v10, 2);
+ Assert.assertEquals(result, v10);
+ Assert.assertEquals(3115L << 2, v10.value);
}
+ @Test
+ public void testIncrementSelf() throws Exception {
+ final JexlEngine jexl = new JexlBuilder().cache(64).arithmetic(new
SelfArithmetic(false)).create();
+ final JexlContext jc = null;
+ runSelfIncrement(jexl, jc);
+ runSelfIncrement(jexl, jc);
+ }
+
+ @Test
+ public void testIncrementSelfNoCache() throws Exception {
+ final JexlEngine jexl = new JexlBuilder().cache(0).arithmetic(new
SelfArithmetic(false)).create();
+ final JexlContext jc = null;
+ runSelfIncrement(jexl, jc);
+ }
+
+ protected void runSelfIncrement(final JexlEngine jexl, final JexlContext
jc) {
+ JexlScript script = jexl.createScript("x -> [+x, +(x++), +x]");
+ final Var v11 = new Var(3115);
+ final AtomicInteger i11 = new AtomicInteger(3115);
+ for(Object v : Arrays.asList(v11, i11)) {
+ Object result = script.execute(jc, v);
+ Assert.assertTrue(result instanceof int[]);
+ int[] r = (int[]) result;
+ Assert.assertEquals(3115, r[0]);
+ Assert.assertEquals(3115, r[1]);
+ Assert.assertEquals(3116, r[2]);
+ }
+
+ script = jexl.createScript("x -> [+x, +(++x), +x]");
+ final Var v12 = new Var(3189);
+ final AtomicInteger i12 = new AtomicInteger(3189);
+ for(Object v : Arrays.asList(v12, i12)) {
+ Object result = script.execute(jc, v);
+ Assert.assertTrue(result instanceof int[]);
+ int[] r = (int[]) result;
+ Assert.assertEquals(3189, r[0]);
+ Assert.assertEquals(3190, r[1]);
+ Assert.assertEquals(3190, r[2]);
+ }
+
+ script = jexl.createScript("x -> [+x, +(x--), +x]");
+ final Var v13 = new Var(3115);
+ for(Object v : Arrays.asList(v13)) {
+ Object result = script.execute(jc, v13);
+ Assert.assertTrue(result instanceof int[]);
+ int[] r = (int[]) result;
+ Assert.assertEquals(3115, r[0]);
+ Assert.assertEquals(3115, r[1]);
+ Assert.assertEquals(3114, r[2]);
+ }
+
+ script = jexl.createScript("x -> [+x, +(--x), +x]");
+ final Var v14 = new Var(3189);
+ for(Object v : Arrays.asList(v14)) {
+ Object result = script.execute(jc, v);
+ Assert.assertTrue(result instanceof int[]);
+ int[] r = (int[]) result;
+ Assert.assertEquals(3189, r[0]);
+ Assert.assertEquals(3188, r[1]);
+ Assert.assertEquals(3188, r[2]);
+ }
+ }
+
@Test
public void testOverrideGetSet() throws Exception {
final JexlEngine jexl = new JexlBuilder().cache(64).arithmetic(new
SelfArithmetic(false)).create();
@@ -456,14 +547,13 @@ public class SideEffectTest extends JexlTestCase {
value = v;
}
- @Override
- public String toString() {
+ @Override public String toString() {
return Integer.toString(value);
}
}
// an arithmetic that performs side effects
- public static class SelfArithmetic extends JexlArithmetic {
+ public static class SelfArithmetic extends OptionalArithmetic {
public SelfArithmetic(final boolean strict) {
super(strict);
}
@@ -484,9 +574,9 @@ public class SideEffectTest extends JexlTestCase {
return "VALUE".equals(property)? var.value = v :
JexlEngine.TRY_FAILED;
}
- public JexlOperator selfAdd(final Var lhs, final Var rhs) {
+ public Var selfAdd(final Var lhs, final Var rhs) {
lhs.value += rhs.value;
- return JexlOperator.ASSIGN;
+ return lhs;
}
// for kicks, this one does not side effect but overloads nevertheless
@@ -494,46 +584,97 @@ public class SideEffectTest extends JexlTestCase {
return new Var(lhs.value - rhs.value);
}
- public JexlOperator selfDivide(final Var lhs, final Var rhs) {
+ public Var selfDivide(final Var lhs, final Var rhs) {
lhs.value /= rhs.value;
- return JexlOperator.ASSIGN;
+ return lhs;
}
- public JexlOperator selfMultiply(final Var lhs, final Var rhs) {
+ public Var selfMultiply(final Var lhs, final Var rhs) {
lhs.value *= rhs.value;
- return JexlOperator.ASSIGN;
+ return lhs;
}
- public JexlOperator selfMod(final Var lhs, final Var rhs) {
+ public Var selfMod(final Var lhs, final Var rhs) {
lhs.value %= rhs.value;
- return JexlOperator.ASSIGN;
+ return lhs;
}
public Var and(final Var lhs, final Var rhs) {
return new Var(lhs.value & rhs.value);
}
- public JexlOperator selfAnd(final Var lhs, final Var rhs) {
+ public Var selfAnd(final Var lhs, final Var rhs) {
lhs.value &= rhs.value;
- return JexlOperator.ASSIGN;
+ return lhs;
}
public Var or(final Var lhs, final Var rhs) {
return new Var(lhs.value | rhs.value);
}
- public JexlOperator selfOr(final Var lhs, final Var rhs) {
+ public Var selfOr(final Var lhs, final Var rhs) {
lhs.value |= rhs.value;
- return JexlOperator.ASSIGN;
+ return lhs;
}
public Var xor(final Var lhs, final Var rhs) {
return new Var(lhs.value ^ rhs.value);
}
- public JexlOperator selfXor(final Var lhs, final Var rhs) {
+ public Var selfXor(final Var lhs, final Var rhs) {
lhs.value ^= rhs.value;
- return JexlOperator.ASSIGN;
+ return lhs;
+ }
+
+ public Var shiftLeft(final Var lhs, final int rhs) {
+ return new Var(lhs.value << rhs);
+ }
+
+ public Var selfShiftLeft(final Var lhs, final int rhs) {
+ lhs.value <<= rhs;
+ return lhs;
+ }
+
+ public Var shiftRight(final Var lhs, final int rhs) {
+ return new Var(lhs.value >> rhs);
+ }
+
+ public Var selfShiftRight(final Var lhs, final int rhs) {
+ lhs.value >>= rhs;
+ return lhs;
+ }
+
+ public Var shiftRightUnsigned(final Var lhs, final int rhs) {
+ return new Var(lhs.value >>> rhs);
+ }
+
+ public Var selfShiftRightUnsigned(final Var lhs, final int rhs) {
+ lhs.value >>>= rhs;
+ return lhs;
+ }
+
+ public int increment(final Var lhs) {
+ return lhs.value + 1;
+ }
+
+ public int decrement(final Var lhs) {
+ return lhs.value - 1;
+ }
+
+ public int incrementAndGet(AtomicInteger i) {
+ return i.incrementAndGet();
+ }
+
+ public int getAndIncrement(AtomicInteger i) {
+ return i.getAndIncrement();
+ }
+
+ public int positivize(Var n) {
+ return n.value;
+ }
+
+ public int positivize(Number n) {
+ return n.intValue();
}
}
@@ -545,14 +686,14 @@ public class SideEffectTest extends JexlTestCase {
super(astrict);
}
- public JexlOperator selfAdd(final Collection<String> c, final String
item) throws IOException {
+ public Object selfAdd(final Collection<String> c, final String item)
throws IOException {
c.add(item);
- return JexlOperator.ASSIGN;
+ return c;
}
- public JexlOperator selfAdd(final Appendable c, final String item)
throws IOException {
+ public Object selfAdd(final Appendable c, final String item) throws
IOException {
c.append(item);
- return JexlOperator.ASSIGN;
+ return c;
}
@Override
@@ -572,7 +713,7 @@ public class SideEffectTest extends JexlTestCase {
}
if (c instanceof Appendable) {
((Appendable) c).append(item);
- return JexlOperator.ASSIGN;
+ return c;
}
return JexlEngine.TRY_FAILED;
}
diff --git a/src/test/java/org/apache/commons/jexl3/junit/Asserter.java
b/src/test/java/org/apache/commons/jexl3/junit/Asserter.java
index 2aaf29a9..8d5db9a5 100644
--- a/src/test/java/org/apache/commons/jexl3/junit/Asserter.java
+++ b/src/test/java/org/apache/commons/jexl3/junit/Asserter.java
@@ -18,8 +18,10 @@ package org.apache.commons.jexl3.junit;
import java.lang.reflect.Array;
import java.math.BigDecimal;
+import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;
+import java.util.function.BiPredicate;
import org.apache.commons.jexl3.JexlEvalContext;
@@ -99,8 +101,11 @@ public class Asserter extends Assert {
final JexlArithmetic jexla = engine.getArithmetic();
Assert.assertEquals("expression: " + expression, 0,
((BigDecimal)
expected).compareTo(jexla.toBigDecimal(value)));
- }
- if (expected != null && value != null) {
+ } else if (expected instanceof BigInteger) {
+ final JexlArithmetic jexla = engine.getArithmetic();
+ Assert.assertEquals("expression: " + expression, 0,
+ ((BigInteger)
expected).compareTo(jexla.toBigInteger(value)));
+ } else if (expected != null && value != null) {
if (expected.getClass().isArray() && value.getClass().isArray()) {
final int esz = Array.getLength(expected);
final int vsz = Array.getLength(value);
@@ -130,12 +135,15 @@ public class Asserter extends Assert {
* @throws Exception if the expression did not fail or the exception did
not match the expected pattern
*/
public void failExpression(final String expression, final String
matchException) throws Exception {
+ failExpression(expression, matchException, String::matches);
+ }
+ public void failExpression(final String expression, final String
matchException, BiPredicate<String,String> predicate) throws Exception {
try {
final JexlScript exp = engine.createScript(expression);
exp.execute(context);
fail("expression: " + expression);
} catch (final JexlException xjexl) {
- if (matchException != null &&
!xjexl.getMessage().matches(matchException)) {
+ if (matchException != null && !predicate.test(xjexl.getMessage(),
matchException)) {
fail("expression: " + expression + ", expected: " +
matchException + ", got " + xjexl.getMessage());
}
}