Added: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Operators.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Operators.java?rev=1692852&view=auto ============================================================================== --- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Operators.java (added) +++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Operators.java Mon Jul 27 10:02:49 2015 @@ -0,0 +1,406 @@ +/* + * 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.jexl3.internal; + +import org.apache.commons.jexl3.JexlArithmetic; +import org.apache.commons.jexl3.JexlEngine; +import org.apache.commons.jexl3.JexlException; +import org.apache.commons.jexl3.JexlOperator; +import org.apache.commons.jexl3.introspection.JexlMethod; +import org.apache.commons.jexl3.introspection.JexlUberspect; +import org.apache.commons.jexl3.parser.JexlNode; + +/** + * Helper class to deal with operator overloading and specifics. + * @since 3.0 + */ +public class Operators { + /** The owner. */ + protected final Interpreter interpreter; + /** The overloaded arithmetic operators. */ + protected final JexlArithmetic.Uberspect operators; + + /** + * Constructor. + * @param owner the owning interpreter + */ + protected Operators(Interpreter owner) { + final JexlArithmetic arithmetic = owner.arithmetic; + final JexlUberspect uberspect = owner.uberspect; + this.interpreter = owner; + this.operators = uberspect.getArithmetic(arithmetic); + } + + /** + * Checks whether a method returns a boolean or a Boolean + * @param vm the JexlMethod (may be null) + * @return true of false + */ + private boolean returnsBoolean(JexlMethod vm) { + if (vm !=null) { + Class<?> rc = vm.getReturnType(); + return Boolean.TYPE.equals(rc) || Boolean.class.equals(rc); + } + return false; + } + + /** + * Attempts to call a monadic operator. + * <p> + * This takes care of finding and caching the operator method when appropriate + * @param node the syntactic node + * @param operator the operator + * @param arg the argument + * @return the result of the operator evaluation or TRY_FAILED + */ + protected Object tryOverload(JexlNode node, JexlOperator operator, Object arg) { + if (operators != null && operators.overloads(operator)) { + final JexlArithmetic arithmetic = interpreter.arithmetic; + final boolean cache = interpreter.cache; + if (cache) { + Object cached = node.jjtGetValue(); + if (cached instanceof JexlMethod) { + JexlMethod me = (JexlMethod) cached; + Object eval = me.tryInvoke(operator.getMethodName(), arithmetic, arg); + if (!me.tryFailed(eval)) { + return eval; + } + } + } + try { + JexlMethod emptym = operators.getOperator(operator, arg); + if (emptym != null) { + Object result = emptym.invoke(arithmetic, arg); + if (cache) { + node.jjtSetValue(emptym); + } + return result; + } + } catch (Exception xany) { + interpreter.operatorError(node, operator, xany); + } + } + return JexlEngine.TRY_FAILED; + } + + /** + * Attempts to call a diadic operator. + * <p> + * This takes care of finding and caching the operator method when appropriate + * @param node the syntactic node + * @param operator the operator + * @param lhs the left hand side argument + * @param rhs the right hand side argument + * @return the result of the operator evaluation or TRY_FAILED + */ + protected Object tryOverload(JexlNode node, JexlOperator operator, Object lhs, Object rhs) { + if (operators != null && operators.overloads(operator)) { + final JexlArithmetic arithmetic = interpreter.arithmetic; + final boolean cache = interpreter.cache; + if (cache) { + Object cached = node.jjtGetValue(); + if (cached instanceof JexlMethod) { + JexlMethod me = (JexlMethod) cached; + Object eval = me.tryInvoke(operator.getMethodName(), arithmetic, lhs, rhs); + if (!me.tryFailed(eval)) { + return eval; + } + } + } + try { + JexlMethod emptym = operators.getOperator(operator, lhs, rhs); + if (emptym != null) { + Object result = emptym.invoke(arithmetic, lhs, rhs); + if (cache) { + node.jjtSetValue(emptym); + } + return result; + } + } catch (Exception xany) { + interpreter.operatorError(node, operator, xany); + } + } + return JexlEngine.TRY_FAILED; + } + + /** + * Evaluates an assign operator. + * <p> + * This takes care of finding and caching the operator method when appropriate. + * If an overloads returns Operator.ASSIGN, it means the side-effect is complete. + * Otherwise, a += b <=> a = a + b + * </p> + * @param node the syntactic node + * @param operator the operator + * @param lhs the left hand side, target of the side-effect + * @param rhs the right hand side, argument of the (base) operator + * @return the result of the operator evaluation + */ + protected Object tryAssignOverload(JexlNode node, JexlOperator operator, Object lhs, Object rhs) { + final JexlArithmetic arithmetic = interpreter.arithmetic; + // try to call overload on side effect + Object result = tryOverload(node, operator, lhs, rhs); + if (result != JexlEngine.TRY_FAILED) { + return result; + } + // call base operator + 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 + try { + JexlMethod emptym = operators.getOperator(base, lhs, rhs); + if (emptym != null) { + result = emptym.invoke(arithmetic, lhs, rhs); + if (result != JexlEngine.TRY_FAILED) { + return result; + } + } + } catch (Exception xany) { + interpreter.operatorError(node, base, xany); + } + } + // base eval + switch (operator) { + case SELF_ADD: + return arithmetic.add(lhs, rhs); + case SELF_SUBTRACT: + return arithmetic.subtract(lhs, rhs); + case SELF_MULTIPLY: + return arithmetic.multiply(lhs, rhs); + case SELF_DIVIDE: + return arithmetic.divide(lhs, rhs); + case SELF_MOD: + return arithmetic.mod(lhs, rhs); + case SELF_AND: + return arithmetic.and(lhs, rhs); + case SELF_OR: + return arithmetic.or(lhs, rhs); + case SELF_XOR: + return arithmetic.xor(lhs, rhs); + default: + throw new JexlException.Operator(node, operator.getOperatorSymbol(), null); + } + } + + /** + * The 'startsWith' operator implementation. + * @param node the node + * @param operator the calling operator, $= or $! + * @param left the left operand + * @param right the right operand + * @return true if left starts with right, false otherwise + */ + protected boolean startsWith(JexlNode node, String operator, Object left, Object right) { + final JexlArithmetic arithmetic = interpreter.arithmetic; + final JexlUberspect uberspect = interpreter.uberspect; + try { + // try operator overload + Object result = tryOverload(node, JexlOperator.STARTSWITH, left, right); + if (result instanceof Boolean) { + return (Boolean) result; + } + // use arithmetic / pattern matching ? + Boolean matched = arithmetic.startsWith(left, right); + if (matched != null) { + return matched; + } + // try a startsWith method (duck type) + try { + Object[] argv = {right}; + JexlMethod vm = uberspect.getMethod(left, "startsWith", argv); + if (returnsBoolean(vm)) { + return (Boolean) vm.invoke(left, argv); + } else if (arithmetic.narrowArguments(argv)) { + vm = uberspect.getMethod(left, "startsWith", argv); + if (returnsBoolean(vm)) { + return (Boolean) vm.invoke(left, argv); + } + } + } catch (Exception e) { + throw new JexlException(node, operator + " error", e); + } + // defaults to equal + return arithmetic.equals(left, right) ? Boolean.TRUE : Boolean.FALSE; + } catch (ArithmeticException xrt) { + throw new JexlException(node, operator + " error", xrt); + } + } + + /** + * The 'endsWith' operator implementation. + * @param node the node + * @param operator the calling operator, ^= or ^! + * @param left the left operand + * @param right the right operand + * @return true if left ends with right, false otherwise + */ + protected boolean endsWith(JexlNode node, String operator, Object left, Object right) { + final JexlArithmetic arithmetic = interpreter.arithmetic; + final JexlUberspect uberspect = interpreter.uberspect; + try { + // try operator overload + Object result = tryOverload(node, JexlOperator.ENDSWITH, left, right); + if (result instanceof Boolean) { + return (Boolean) result; + } + // use arithmetic / pattern matching ? + Boolean matched = arithmetic.endsWith(left, right); + if (matched != null) { + return matched; + } + // try a endsWith method (duck type) + try { + Object[] argv = {right}; + JexlMethod vm = uberspect.getMethod(left, "endsWith", argv); + if (returnsBoolean(vm)) { + return (Boolean) vm.invoke(left, argv); + } else if (arithmetic.narrowArguments(argv)) { + vm = uberspect.getMethod(left, "endsWith", argv); + if (returnsBoolean(vm)) { + return (Boolean) vm.invoke(left, argv); + } + } + } catch (Exception e) { + throw new JexlException(node, operator + " error", e); + } + // defaults to equal + return arithmetic.equals(left, right) ? Boolean.TRUE : Boolean.FALSE; + } catch (ArithmeticException xrt) { + throw new JexlException(node, operator + " error", xrt); + } + } + + /** + * The 'match'/'in' operator implementation. + * <p> + * Note that 'x in y' or 'x matches y' means 'y contains x' ; + * the JEXL operator arguments order syntax is the reverse of this method call. + * </p> + * @param node the node + * @param op the calling operator, =~ or != + * @param right the left operand + * @param left the right operand + * @return true if left matches right, false otherwise + */ + protected boolean contains(JexlNode node, String op, Object left, Object right) { + final JexlArithmetic arithmetic = interpreter.arithmetic; + final JexlUberspect uberspect = interpreter.uberspect; + try { + // try operator overload + Object result = tryOverload(node, JexlOperator.CONTAINS, left, right); + if (result instanceof Boolean) { + return (Boolean) result; + } + // use arithmetic / pattern matching ? + Boolean matched = arithmetic.contains(left, right); + if (matched != null) { + return matched; + } + // try a contains method (duck type set) + try { + Object[] argv = {right}; + JexlMethod vm = uberspect.getMethod(left, "contains", argv); + if (returnsBoolean(vm)) { + return (Boolean) vm.invoke(left, argv); + } else if (arithmetic.narrowArguments(argv)) { + vm = uberspect.getMethod(left, "contains", argv); + if (returnsBoolean(vm)) { + return (Boolean) vm.invoke(left, argv); + } + } + } catch (Exception e) { + throw new JexlException(node, op + " error", e); + } + // defaults to equal + return arithmetic.equals(left, right); + } catch (ArithmeticException xrt) { + throw new JexlException(node, op + " error", xrt); + } + } + + /** + * Check for emptyness of various types: Collection, Array, Map, String, and anything that has a boolean isEmpty() + * method. + * + * @param node the node holding the object + * @param object the object to check the emptyness of. + * @return the boolean + */ + protected Boolean empty(JexlNode node, Object object) { + final JexlArithmetic arithmetic = interpreter.arithmetic; + final JexlUberspect uberspect = interpreter.uberspect; + if (object == null) { + return Boolean.TRUE; + } + Object opcall = Operators.this.tryOverload(node, JexlOperator.EMPTY, object); + if (opcall instanceof Boolean) { + return (Boolean) opcall; + } + Boolean result = arithmetic.isEmpty(object); + if (result == null) { + result = false; + // check if there is an isEmpty method on the object that returns a + // boolean and if so, just use it + JexlMethod vm = uberspect.getMethod(object, "isEmpty", Interpreter.EMPTY_PARAMS); + if (returnsBoolean(vm)) { + try { + result = (Boolean) vm.invoke(object, Interpreter.EMPTY_PARAMS); + } catch (Exception xany) { + interpreter.operatorError(node, JexlOperator.EMPTY, xany); + } + } + } + return result; + } + + /** + * Calculate the <code>size</code> of various types: + * Collection, Array, Map, String, and anything that has a int size() method. + * + * @param node the node that gave the value to size + * @param object the object to get the size of. + * @return the size of val + */ + protected Integer size(JexlNode node, Object object) { + final JexlArithmetic arithmetic = interpreter.arithmetic; + final JexlUberspect uberspect = interpreter.uberspect; + if (object == null) { + return 0; + } + Object opcall = Operators.this.tryOverload(node, JexlOperator.SIZE, object); + if (opcall instanceof Integer) { + return (Integer) opcall; + } + Integer result = arithmetic.size(object); + if (result == null) { + // check if there is a size method on the object that returns an + // integer and if so, just use it + JexlMethod vm = uberspect.getMethod(object, "size", Interpreter.EMPTY_PARAMS); + if (vm != null && (Integer.TYPE.equals(vm.getReturnType()) || Integer.class.equals(vm.getReturnType()))) { + try { + result = (Integer) vm.invoke(object, Interpreter.EMPTY_PARAMS); + } catch (Exception xany) { + interpreter.operatorError(node, JexlOperator.SIZE, xany); + } + } + } + return result; + } +}
Propchange: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Operators.java ------------------------------------------------------------------------------ svn:eol-style = native Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/Uberspect.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/Uberspect.java?rev=1692852&r1=1692851&r2=1692852&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/Uberspect.java (original) +++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/Uberspect.java Mon Jul 27 10:02:49 2015 @@ -18,8 +18,8 @@ package org.apache.commons.jexl3.interna import org.apache.commons.jexl3.JexlArithmetic; -import org.apache.commons.jexl3.JexlArithmetic.Operator; import org.apache.commons.jexl3.JexlEngine; +import org.apache.commons.jexl3.JexlOperator; import org.apache.commons.jexl3.introspection.JexlMethod; import org.apache.commons.jexl3.introspection.JexlPropertyGet; import org.apache.commons.jexl3.introspection.JexlPropertySet; @@ -66,7 +66,7 @@ public class Uberspect implements JexlUb * <p>This keeps track of which operator methods are overloaded per JexlArithemtic classes * allowing a fail fast test during interpretation by avoiding seeking a method when there is none. */ - private final Map<Class<? extends JexlArithmetic>, Set<Operator>> operatorMap; + private final Map<Class<? extends JexlArithmetic>, Set<JexlOperator>> operatorMap; /** * Creates a new Uberspect. @@ -76,7 +76,7 @@ public class Uberspect implements JexlUb rlog = runtimeLogger; ref = new SoftReference<Introspector>(null); loader = new SoftReference<ClassLoader>(getClass().getClassLoader()); - operatorMap = new ConcurrentHashMap<Class<? extends JexlArithmetic>, Set<Operator>>(); + operatorMap = new ConcurrentHashMap<Class<? extends JexlArithmetic>, Set<JexlOperator>>(); version = new AtomicInteger(0); } @@ -370,14 +370,14 @@ public class Uberspect implements JexlUb /** The arithmetic instance being analyzed. */ private final JexlArithmetic arithmetic; /** The set of overloaded operators. */ - private final EnumSet<Operator> overloads; + private final EnumSet<JexlOperator> overloads; /** * Creates an instance. * @param theArithmetic the arithmetic instance * @param theOverloads the overloaded operators */ - private ArithmeticUberspect(JexlArithmetic theArithmetic, Set<Operator> theOverloads) { + private ArithmeticUberspect(JexlArithmetic theArithmetic, Set<JexlOperator> theOverloads) { this.arithmetic = theArithmetic; this.overloads = EnumSet.copyOf(theOverloads); // register this arithmetic class in the operator map @@ -385,21 +385,21 @@ public class Uberspect implements JexlUb } @Override - public JexlMethod getOperator(JexlArithmetic.Operator operator, Object arg) { + public JexlMethod getOperator(JexlOperator operator, Object arg) { return overloads.contains(operator) && arg != null ? getMethod(arithmetic, operator.getMethodName(), arg) : null; } @Override - public JexlMethod getOperator(JexlArithmetic.Operator operator, Object lhs, Object rhs) { + public JexlMethod getOperator(JexlOperator operator, Object lhs, Object rhs) { return overloads.contains(operator) && lhs != null && rhs != null ? getMethod(arithmetic, operator.getMethodName(), lhs, rhs) : null; } @Override - public boolean overloads(Operator operator) { + public boolean overloads(JexlOperator operator) { return overloads.contains(operator); } } @@ -408,10 +408,10 @@ public class Uberspect implements JexlUb public JexlArithmetic.Uberspect getArithmetic(JexlArithmetic arithmetic) { JexlArithmetic.Uberspect jau = null; if (arithmetic != null) { - Set<Operator> ops = operatorMap.get(arithmetic.getClass()); + Set<JexlOperator> ops = operatorMap.get(arithmetic.getClass()); if (ops == null) { - ops = EnumSet.noneOf(Operator.class); - for (JexlArithmetic.Operator op : JexlArithmetic.Operator.values()) { + ops = EnumSet.noneOf(JexlOperator.class); + for (JexlOperator op : JexlOperator.values()) { Method[] methods = getMethods(arithmetic.getClass(), op.getMethodName()); if (methods != null) { for (Method method : methods) { Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/introspection/JexlUberspect.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/introspection/JexlUberspect.java?rev=1692852&r1=1692851&r2=1692852&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/introspection/JexlUberspect.java (original) +++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/introspection/JexlUberspect.java Mon Jul 27 10:02:49 2015 @@ -88,6 +88,7 @@ public interface JexlUberspect { * Gets an arithmetic operator resolver for a given arithmetic instance. * @param arithmetic the arithmetic instance * @return the arithmetic uberspect or null if no operator method were override + * @since 3.0 */ JexlArithmetic.Uberspect getArithmetic(JexlArithmetic arithmetic); Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt?rev=1692852&r1=1692851&r2=1692852&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt (original) +++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt Mon Jul 27 10:02:49 2015 @@ -157,17 +157,27 @@ PARSER_END(Parser) } <*> TOKEN : { /* OPERATORS */ - < assign : "=" > - | < mod : "%" | "mod" > - | < div : "/" | "div" > - | < not : "!" | "not" > + < plus_assign : "+=" > + | < minus_assign : "-=" > + | < mult_assign : "*=" > + | < div_assign : "/=" > + | < mod_assign : "%=" > + | < and_assign : "&=" > + | < or_assign : "|=" > + | < xor_assign : "^=" > + + | < assign : "=" > | < plus : "+" > | < minus : "-" > | < mult : "*" > - | < tilda : "~" > + | < div : "/" | "div" > + | < mod : "%" | "mod" > + | < not : "!" | "not" > | < and : "&" > | < or : "|" > | < xor : "^" > + + | < tilda : "~" > | < range : ".." > } @@ -368,7 +378,26 @@ void Expression() #void : {} void AssignmentExpression() #void : {} { - ConditionalExpression() [ LOOKAHEAD( <assign> ) <assign> Expression() #Assignment(2) ] + ConditionalExpression() + ( LOOKAHEAD(2) ( + <plus_assign> Expression() #SetAddNode(2) + | + <mult_assign> Expression() #SetMultNode(2) + | + <div_assign> Expression() #SetDivNode(2) + | + <mod_assign> Expression() #SetModNode(2) + | + <and_assign> Expression() #SetAndNode(2) + | + <or_assign> Expression() #SetOrNode(2) + | + <xor_assign> Expression() #SetXorNode(2) + | + <minus_assign> Expression() #SetSubNode(2) + | + <assign> Expression() #Assignment(2) + ) )* } /*************************************** @@ -481,17 +510,17 @@ void MultiplicativeExpression() #void : void UnaryExpression() #void : {} { - <minus> UnaryExpression() #UnaryMinusNode(1) -| - <tilda> UnaryExpression() #BitwiseComplNode(1) -| - <not> UnaryExpression() #NotNode(1) -| - <EMPTY> UnaryExpression() #EmptyFunction(1) -| - <SIZE> UnaryExpression() #SizeFunction(1) -| - ValueExpression() + <minus> UnaryExpression() #UnaryMinusNode(1) + | + <tilda> UnaryExpression() #BitwiseComplNode(1) + | + <not> UnaryExpression() #NotNode(1) + | + <EMPTY> UnaryExpression() #EmptyFunction(1) + | + <SIZE> UnaryExpression() #SizeFunction(1) + | + ValueExpression() } Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/ParserVisitor.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/ParserVisitor.java?rev=1692852&r1=1692851&r2=1692852&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/ParserVisitor.java (original) +++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/ParserVisitor.java Mon Jul 27 10:02:49 2015 @@ -159,4 +159,20 @@ public abstract class ParserVisitor { protected abstract Object visit(ASTArguments node, Object data); protected abstract Object visit(ASTReferenceExpression node, Object data); + + protected abstract Object visit(ASTSetAddNode node, Object data); + + protected abstract Object visit(ASTSetSubNode node, Object data); + + protected abstract Object visit(ASTSetMultNode node, Object data); + + protected abstract Object visit(ASTSetDivNode node, Object data); + + protected abstract Object visit(ASTSetModNode node, Object data); + + protected abstract Object visit(ASTSetAndNode node, Object data); + + protected abstract Object visit(ASTSetOrNode node, Object data); + + protected abstract Object visit(ASTSetXorNode node, Object data); } Modified: commons/proper/jexl/trunk/src/site/xdoc/changes.xml URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/site/xdoc/changes.xml?rev=1692852&r1=1692851&r2=1692852&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/site/xdoc/changes.xml (original) +++ commons/proper/jexl/trunk/src/site/xdoc/changes.xml Mon Jul 27 10:02:49 2015 @@ -26,6 +26,9 @@ </properties> <body> <release version="3.0" date="unreleased"> + <action dev="henrib" type="add" issue="JEXL-170"> + Implement assignment operators + </action> <action dev="henrib" type="fix" issue="JEXL-169" due-to="Robert NeÃelrath"> A string is wrongly identified as FloatingPointNumber </action> Modified: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArithmeticOperatorTest.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArithmeticOperatorTest.java?rev=1692852&r1=1692851&r2=1692852&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArithmeticOperatorTest.java (original) +++ commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArithmeticOperatorTest.java Mon Jul 27 10:02:49 2015 @@ -26,6 +26,7 @@ import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import org.apache.commons.jexl3.junit.Asserter; +import java.util.Arrays; import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -127,6 +128,14 @@ public class ArithmeticOperatorTest exte return values.iterator(); } + public boolean contains(int i) { + return values.contains(i); + } + + public boolean contains(int[] i) { + return values.containsAll(Arrays.asList(i)); + } + public boolean startsWith(int i) { return values.first().equals(i); } Modified: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java?rev=1692852&r1=1692851&r2=1692852&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java (original) +++ commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java Mon Jul 27 10:02:49 2015 @@ -16,6 +16,7 @@ */ package org.apache.commons.jexl3; +import static org.apache.commons.jexl3.JexlArithmetic.FLOAT_PATTERN; import org.apache.commons.jexl3.junit.Asserter; import java.io.ByteArrayInputStream; @@ -27,7 +28,6 @@ import java.math.BigDecimal; import java.math.BigInteger; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; -import static org.apache.commons.jexl3.JexlArithmetic.FLOAT_PATTERN; import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -412,7 +412,6 @@ public class ArithmeticTest extends Jexl Assert.assertTrue((Boolean) result); } - @Test public void testAddWithStringsLenient() throws Exception { JexlEngine jexl = new JexlBuilder().arithmetic(new JexlArithmetic(false)).create(); @@ -568,7 +567,7 @@ public class ArithmeticTest extends Jexl } public static class Var { - final int value; + int value; Var(int v) { value = v; @@ -630,15 +629,15 @@ public class ArithmeticTest extends Jexl return new Var(-arg.value); } - public Var bitwiseAnd(Var lhs, Var rhs) { + public Var and(Var lhs, Var rhs) { return new Var(lhs.value & rhs.value); } - public Var bitwiseOr(Var lhs, Var rhs) { + public Var or(Var lhs, Var rhs) { return new Var(lhs.value | rhs.value); } - public Var bitwiseXor(Var lhs, Var rhs) { + public Var xor(Var lhs, Var rhs) { return new Var(lhs.value ^ rhs.value); } @@ -654,7 +653,7 @@ public class ArithmeticTest extends Jexl return lhs.toString().endsWith(rhs.toString()); } - public Var bitwiseComplement(Var arg) { + public Var complement(Var arg) { return new Var(~arg.value); } @@ -683,6 +682,7 @@ public class ArithmeticTest extends Jexl JexlEngine jexl = new JexlBuilder().cache(64).arithmetic(new ArithmeticPlus(false)).create(); JexlContext jc = new EmptyTestContext(); runOverload(jexl, jc); + runOverload(jexl, jc); } @Test @@ -854,15 +854,21 @@ public class ArithmeticTest extends Jexl result = script.execute(jc, 3155, 15); Assert.assertFalse((Boolean) result); result = script.execute(jc, new Var(3155), new Var(15)); + Assert.assertFalse((Boolean) result); + result = script.execute(jc, new Var(15), new Var(3155)); Assert.assertTrue((Boolean) result); script = jexl.createScript("(x, y)->{ x !~ y }"); result = script.execute(jc, 3115, 15); Assert.assertTrue((Boolean) result); result = script.execute(jc, new Var(3155), new Var(15)); + Assert.assertTrue((Boolean) result); + result = script.execute(jc, new Var(15), new Var(3155)); Assert.assertFalse((Boolean) result); + } + public static class Arithmetic132 extends JexlArithmetic { public Arithmetic132() { super(false); Modified: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArrayAccessTest.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArrayAccessTest.java?rev=1692852&r1=1692851&r2=1692852&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArrayAccessTest.java (original) +++ commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArrayAccessTest.java Mon Jul 27 10:02:49 2015 @@ -22,7 +22,9 @@ import java.util.List; import java.util.Map; import org.apache.commons.jexl3.junit.Asserter; +import org.junit.Assert; import org.junit.Before; +import org.junit.Test; /** Modified: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/IssuesTest.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/IssuesTest.java?rev=1692852&r1=1692851&r2=1692852&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/IssuesTest.java (original) +++ commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/IssuesTest.java Mon Jul 27 10:02:49 2015 @@ -22,14 +22,26 @@ import java.math.MathContext; import java.util.HashMap; import java.util.Map; import org.apache.commons.jexl3.internal.introspection.Uberspect; +import java.io.ByteArrayInputStream; import java.io.File; +import java.io.IOException; +import java.io.InputStream; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import org.w3c.dom.Attr; +import org.w3c.dom.Document; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.xml.sax.SAXException; +//import org.apache.commons.beanutils.LazyDynaMap; /** * Test cases for reported issue . @@ -1051,16 +1063,19 @@ public class IssuesTest extends JexlTest public static class Question42 extends MapContext { public String functionA(String arg) { - return "a".equals(arg)? "A" : ""; + return "a".equals(arg) ? "A" : ""; } + public String functionB(String arg) { - return "b".equals(arg)? "B" : ""; + return "b".equals(arg) ? "B" : ""; } + public String functionC(String arg) { - return "c".equals(arg)? "C" : ""; + return "c".equals(arg) ? "C" : ""; } + public String functionD(String arg) { - return "d".equals(arg)? "D" : ""; + return "d".equals(arg) ? "D" : ""; } } @@ -1069,15 +1084,23 @@ public class IssuesTest extends JexlTest super(false); } - public Object bitwiseAnd(String lhs, String rhs) { - if (rhs.isEmpty()) { return ""; } - if (lhs.isEmpty()) { return ""; } + public Object and(String lhs, String rhs) { + if (rhs.isEmpty()) { + return ""; + } + if (lhs.isEmpty()) { + return ""; + } return lhs + rhs; } - public Object bitwiseOr(String lhs, String rhs) { - if (rhs.isEmpty()) { return lhs; } - if (lhs.isEmpty()) { return rhs; } + public Object or(String lhs, String rhs) { + if (rhs.isEmpty()) { + return lhs; + } + if (lhs.isEmpty()) { + return rhs; + } return lhs + rhs; } } @@ -1109,4 +1132,117 @@ public class IssuesTest extends JexlTest Object r1 = jexl.createExpression("463.0B * 0.1B").evaluate(jc); Assert.assertEquals(java.math.BigDecimal.class, r1.getClass()); } + +// +// +// @Test +// public void testUnderscoreInName() { +// JexlEngine jexl = new Engine(); +// String jexlExp = "(x.length_mm * x.width)"; +// JexlExpression e = jexl.createExpression( jexlExp ); +// JexlContext jc = new MapContext(); +// +// LazyDynaMap object = new LazyDynaMap(); +// object.set("length_mm", "10.0"); +// object.set("width", "5.0"); +// +// jc.set("x", object ); +// +// Assert.assertEquals(null, ((Double)e.evaluate(jc)).doubleValue(), 50d, 0d); +// } +// +// @Test +// public void testFullStopInName() { +// JexlEngine jexl = new Engine(); +// String jexlExp = "(x.length.mm * x.width)"; +// JexlExpression e = jexl.createExpression( jexlExp ); +// JexlContext jc = new MapContext(); +// +// LazyDynaMap object = new LazyDynaMap(); +// object.set("length.mm", "10.0"); +// object.set("width", "5.0"); +// +// Assert.assertEquals(null, object.get("length.mm"), "10.0"); +// +// jc.set("x", object ); +// +// Assert.assertEquals(null, ((Double)e.evaluate(jc)).doubleValue(), 50d, 0d); +// } +// +// @Test +// public void testFullStopInNameMakingSubObject() { +// JexlEngine jexl = new Engine(); +// String jexlExp = "(x.length.mm * x.width)"; +// JexlExpression e = jexl.createExpression( jexlExp ); +// JexlContext jc = new MapContext(); +// +// LazyDynaMap object = new LazyDynaMap(); +// LazyDynaMap subObject = new LazyDynaMap(); +// object.set("length", subObject); +// subObject.set("mm", "10.0"); +// object.set("width", "5.0"); +// +// jc.set("x", object ); +// +// Assert.assertEquals(null, ((Double)e.evaluate(jc)).doubleValue(), 50d, 0d); +// } + + + @Test + public void test161() throws Exception { + final JexlEngine jexl = new Engine(); + final JexlContext jc = new MapContext(); + + Document xml = getDocument("<node info='123'/>"); + NamedNodeMap nnm = xml.getLastChild().getAttributes(); + Attr info = (Attr) nnm.getNamedItem("info"); + Assert.assertEquals("123", info.getValue()); + + jc.set("x", xml.getLastChild()); + final String y = "456"; + jc.set("y", y); + JexlScript s = jexl.createScript("x.attribute.info = y"); + Object r = s.execute(jc); + nnm = xml.getLastChild().getAttributes(); + info = (Attr) nnm.getNamedItem("info"); + Assert.assertEquals(y, r); + Assert.assertEquals(y, info.getValue()); + } + + public static class XmlArithmetic extends JexlArithmetic { + public XmlArithmetic(boolean lenient) { + super(lenient); + } + public boolean empty(org.w3c.dom.Element elt) { + return !elt.hasAttributes() && !elt.hasChildNodes(); + } + } + + @Test + public void test162() throws Exception { + JexlEngine jexl = //new JexlBuilder().arithmetic(new JexlArithmetic(false)).create(); +new JexlBuilder().arithmetic(new XmlArithmetic(false)).create(); + JexlScript s0 = jexl.createScript("x.empty()", "x"); + Document xml; + Node x; + Boolean r; + xml = getDocument("<node info='123'/>"); + x = xml.getLastChild(); + r = (Boolean) s0.execute(null, x); + Assert.assertFalse(r); + xml = getDocument("<node>some content</node>"); + x = xml.getLastChild(); + r = (Boolean) s0.execute(null, x); + Assert.assertFalse(r); + xml = getDocument("<node/>"); + x = xml.getLastChild(); + r = (Boolean) s0.execute(null, x); + Assert.assertTrue(r); + } + + private static Document getDocument(String xml) throws IOException, SAXException, ParserConfigurationException { + DocumentBuilder xmlBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + InputStream stringInputStream = new ByteArrayInputStream(xml.getBytes("UTF-8")); + return xmlBuilder.parse(stringInputStream); + } } Added: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/SideEffectTest.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/SideEffectTest.java?rev=1692852&view=auto ============================================================================== --- commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/SideEffectTest.java (added) +++ commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/SideEffectTest.java Mon Jul 27 10:02:49 2015 @@ -0,0 +1,404 @@ +/* + * 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.jexl3; + +import java.util.Map; + +import org.apache.commons.jexl3.junit.Asserter; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + + +/** + * Tests for array access operator [] + * + * @since 2.0 + */ +@SuppressWarnings({"UnnecessaryBoxing", "AssertEqualsBetweenInconvertibleTypes"}) +public class SideEffectTest extends JexlTestCase { + + private Asserter asserter; + + public SideEffectTest() { + super("SideEffectTest"); + } + + @Override + @Before + public void setUp() { + asserter = new Asserter(JEXL); + } + + @Test + public void testSideEffectVar() throws Exception { + Map<String,Object> context = asserter.getVariables(); + Integer i41 = Integer.valueOf(4141); + Object foo = i41; + + context.put("foo", foo); + asserter.assertExpression("foo += 2", i41 + 2); + Assert.assertEquals(context.get("foo"), i41 + 2); + + context.put("foo", foo); + asserter.assertExpression("foo -= 2", i41 - 2); + Assert.assertEquals(context.get("foo"), i41 - 2); + + context.put("foo", foo); + asserter.assertExpression("foo *= 2", i41 * 2); + Assert.assertEquals(context.get("foo"), i41 * 2); + + context.put("foo", foo); + asserter.assertExpression("foo /= 2", i41 / 2); + Assert.assertEquals(context.get("foo"), i41 / 2); + + context.put("foo", foo); + asserter.assertExpression("foo %= 2", i41 % 2); + Assert.assertEquals(context.get("foo"), i41 % 2); + + context.put("foo", foo); + asserter.assertExpression("foo &= 3", (long) (i41 & 3)); + Assert.assertEquals(context.get("foo"), (long)(i41 & 3)); + + context.put("foo", foo); + asserter.assertExpression("foo |= 2", (long)(i41 | 2)); + Assert.assertEquals(context.get("foo"), (long)(i41 | 2)); + + context.put("foo", foo); + asserter.assertExpression("foo ^= 2", (long)(i41 ^ 2)); + Assert.assertEquals(context.get("foo"), (long)(i41 ^ 2)); + } + + @Test + public void testSideEffectArray() throws Exception { + Integer i41 = Integer.valueOf(4141); + Integer i42 = Integer.valueOf(42); + Integer i43 = Integer.valueOf(43); + String s42 = "fourty-two"; + String s43 = "fourty-three"; + Object[] foo = new Object[3]; + foo[1] = i42; + foo[2] = i43; + asserter.setVariable("foo", foo); + foo[0] = i41; + asserter.assertExpression("foo[0] += 2", i41 + 2); + Assert.assertEquals(foo[0], i41 + 2); + foo[0] = i41; + asserter.assertExpression("foo[0] -= 2", i41 - 2); + Assert.assertEquals(foo[0], i41 - 2); + foo[0] = i41; + asserter.assertExpression("foo[0] *= 2", i41 * 2); + Assert.assertEquals(foo[0], i41 * 2); + foo[0] = i41; + asserter.assertExpression("foo[0] /= 2", i41 / 2); + Assert.assertEquals(foo[0], i41 / 2); + foo[0] = i41; + asserter.assertExpression("foo[0] %= 2", i41 % 2); + Assert.assertEquals(foo[0], i41 % 2); + foo[0] = i41; + asserter.assertExpression("foo[0] &= 3", (long) (i41 & 3)); + Assert.assertEquals(foo[0], (long)(i41 & 3)); + foo[0] = i41; + asserter.assertExpression("foo[0] |= 2", (long)(i41 | 2)); + Assert.assertEquals(foo[0], (long)(i41 | 2)); + foo[0] = i41; + asserter.assertExpression("foo[0] ^= 2", (long)(i41 ^ 2)); + Assert.assertEquals(foo[0], (long)(i41 ^ 2)); + } + + @Test + public void testSideEffectAntishArray() throws Exception { + Integer i41 = Integer.valueOf(4141); + Integer i42 = Integer.valueOf(42); + Integer i43 = Integer.valueOf(43); + Object[] foo = new Object[3]; + foo[1] = i42; + foo[2] = i43; + asserter.setVariable("foo.bar", foo); + foo[0] = i41; + asserter.assertExpression("foo.bar[0] += 2", i41 + 2); + Assert.assertEquals(foo[0], i41 + 2); + foo[0] = i41; + asserter.assertExpression("foo.bar[0] -= 2", i41 - 2); + Assert.assertEquals(foo[0], i41 - 2); + foo[0] = i41; + asserter.assertExpression("foo.bar[0] *= 2", i41 * 2); + Assert.assertEquals(foo[0], i41 * 2); + foo[0] = i41; + asserter.assertExpression("foo.bar[0] /= 2", i41 / 2); + Assert.assertEquals(foo[0], i41 / 2); + foo[0] = i41; + asserter.assertExpression("foo.bar[0] %= 2", i41 % 2); + Assert.assertEquals(foo[0], i41 % 2); + foo[0] = i41; + asserter.assertExpression("foo.bar[0] &= 3", (long) (i41 & 3)); + Assert.assertEquals(foo[0], (long)(i41 & 3)); + foo[0] = i41; + asserter.assertExpression("foo.bar[0] |= 2", (long)(i41 | 2)); + Assert.assertEquals(foo[0], (long)(i41 | 2)); + foo[0] = i41; + asserter.assertExpression("foo.bar[0] ^= 2", (long)(i41 ^ 2)); + Assert.assertEquals(foo[0], (long)(i41 ^ 2)); + } + + public static class Foo { + int value; + Foo(int v) { + value = v; + } + + @Override + public String toString() { + return Integer.toString(value); + } + + public void setValue(long v) { + value = (int) v; + } + public int getValue() { + return value; + } + + public void setBar(int x, long v) { + value = (int) v + x; + } + + public int getBar(int x) { + return value + x; + } + } + + @Test + public void testSideEffectBean() throws Exception { + Integer i41 = Integer.valueOf(4141); + Foo foo = new Foo(0); + asserter.setVariable("foo", foo); + foo.value = i41; + asserter.assertExpression("foo.value += 2", i41 + 2); + Assert.assertEquals(foo.value, i41 + 2); + foo.value = i41; + asserter.assertExpression("foo.value -= 2", i41 - 2); + Assert.assertEquals(foo.value, i41 - 2); + foo.value = i41; + asserter.assertExpression("foo.value *= 2", i41 * 2); + Assert.assertEquals(foo.value, i41 * 2); + foo.value = i41; + asserter.assertExpression("foo.value /= 2", i41 / 2); + Assert.assertEquals(foo.value, i41 / 2); + foo.value = i41; + asserter.assertExpression("foo.value %= 2", i41 % 2); + Assert.assertEquals(foo.value, i41 % 2); + foo.value = i41; + asserter.assertExpression("foo.value &= 3", (long) (i41 & 3)); + Assert.assertEquals(foo.value, (long)(i41 & 3)); + foo.value = i41; + asserter.assertExpression("foo.value |= 2", (long)(i41 | 2)); + Assert.assertEquals(foo.value, (long)(i41 | 2)); + foo.value = i41; + asserter.assertExpression("foo.value ^= 2", (long)(i41 ^ 2)); + Assert.assertEquals(foo.value, (long)(i41 ^ 2)); + } + + @Test + public void testSideEffectBeanContainer() throws Exception { + Integer i41 = Integer.valueOf(4141); + Foo foo = new Foo(0); + asserter.setVariable("foo", foo); + foo.value = i41; + asserter.assertExpression("foo.bar[0] += 2", i41 + 2); + Assert.assertEquals(foo.value, i41 + 2); + foo.value = i41; + asserter.assertExpression("foo.bar[1] += 2", i41 + 3); + Assert.assertEquals(foo.value, i41 + 4); + foo.value = i41; + asserter.assertExpression("foo.bar[0] -= 2", i41 - 2); + Assert.assertEquals(foo.value, i41 - 2); + foo.value = i41; + asserter.assertExpression("foo.bar[0] *= 2", i41 * 2); + Assert.assertEquals(foo.value, i41 * 2); + foo.value = i41; + asserter.assertExpression("foo.bar[0] /= 2", i41 / 2); + Assert.assertEquals(foo.value, i41 / 2); + foo.value = i41; + asserter.assertExpression("foo.bar[0] %= 2", i41 % 2); + Assert.assertEquals(foo.value, i41 % 2); + foo.value = i41; + asserter.assertExpression("foo.bar[0] &= 3", (long) (i41 & 3)); + Assert.assertEquals(foo.value, (long)(i41 & 3)); + foo.value = i41; + asserter.assertExpression("foo.bar[0] |= 2", (long)(i41 | 2)); + Assert.assertEquals(foo.value, (long)(i41 | 2)); + foo.value = i41; + asserter.assertExpression("foo.bar[0] ^= 2", (long)(i41 ^ 2)); + Assert.assertEquals(foo.value, (long)(i41 ^ 2)); + } + + @Test + public void testArithmeticSelf() throws Exception { + JexlEngine jexl = new JexlBuilder().cache(64).arithmetic(new SelfArithmetic(false)).create(); + JexlContext jc = null; + runSelfOverload(jexl, jc); + runSelfOverload(jexl, jc); + } + + @Test + public void testArithmeticSelfNoCache() throws Exception { + JexlEngine jexl = new JexlBuilder().cache(0).arithmetic(new SelfArithmetic(false)).create(); + JexlContext jc = null; + runSelfOverload(jexl, jc); + } + + protected void runSelfOverload(JexlEngine jexl, JexlContext jc) { + JexlScript script; + Object result; + script = jexl.createScript("(x, y)->{ x += y }"); + result = script.execute(jc, 3115, 15); + Assert.assertEquals(3115 + 15, result); + Var v0 = new Var(3115); + result = script.execute(jc, v0, new Var(15)); + Assert.assertEquals(result, v0); + Assert.assertEquals(3115 + 15, v0.value); + + script = jexl.createScript("(x, y)->{ x -= y}"); + result = script.execute(jc, 3115, 15); + Assert.assertEquals(3115 - 15, result); + 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); + + script = jexl.createScript("(x, y)->{ x *= y }"); + result = script.execute(jc, 3115, 15); + Assert.assertEquals(3115 * 15, result); + Var v2 = new Var(3115); + result = script.execute(jc, v2, new Var(15)); + Assert.assertEquals(result, v2); + Assert.assertEquals(3115 * 15, v2.value); + + script = jexl.createScript("(x, y)->{ x /= y }"); + result = script.execute(jc, 3115, 15); + Assert.assertEquals(3115 / 15, result); + Var v3 = new Var(3115); + result = script.execute(jc, v3, new Var(15)); + Assert.assertEquals(result, v3); + Assert.assertEquals(3115 / 15, v3.value); + + script = jexl.createScript("(x, y)->{ x %= y }"); + result = script.execute(jc, 3115, 15); + Assert.assertEquals(3115 % 15, result); + Var v4 = new Var(3115); + result = script.execute(jc, v4, new Var(15)); + Assert.assertEquals(result, v4); + Assert.assertEquals(3115 % 15, v4.value); + + script = jexl.createScript("(x, y)->{ x &= y }"); + result = script.execute(jc, 3115, 15); + Assert.assertEquals(3115L & 15, result); + Var v5 = new Var(3115); + result = script.execute(jc, v5, new Var(15)); + Assert.assertEquals(result, v5); + Assert.assertEquals(3115 & 15, v5.value); + + script = jexl.createScript("(x, y)->{ x |= y }"); + result = script.execute(jc, 3115, 15); + Assert.assertEquals(3115L | 15, result); + Var v6 = new Var(3115); + result = script.execute(jc, v6, new Var(15)); + Assert.assertEquals(result, v6); + Assert.assertEquals(3115L | 15, v6.value); + + script = jexl.createScript("(x, y)->{ x ^= y }"); + result = script.execute(jc, 3115, 15); + Assert.assertEquals(3115L ^ 15, result); + Var v7 = new Var(3115); + result = script.execute(jc, v7, new Var(15)); + Assert.assertEquals(result, v7); + Assert.assertEquals(3115L ^ 15, v7.value); + } + + public static class Var { + int value; + + Var(int v) { + value = v; + } + + @Override + public String toString() { + return Integer.toString(value); + } + } + + // an arithmetic that performs side effects + public static class SelfArithmetic extends JexlArithmetic { + public SelfArithmetic(boolean strict) { + super(strict); + } + + public JexlOperator selfAdd(Var lhs, Var rhs) { + lhs.value += rhs.value; + return JexlOperator.ASSIGN; + } + + // for kicks, this one does not side effect but overloads nevertheless + public Var selfSubtract(Var lhs, Var rhs) { + return new Var(lhs.value - rhs.value); + } + + public JexlOperator selfDivide(Var lhs, Var rhs) { + lhs.value /= rhs.value; + return JexlOperator.ASSIGN; + } + + public JexlOperator selfMultiply(Var lhs, Var rhs) { + lhs.value *= rhs.value; + return JexlOperator.ASSIGN; + } + + public JexlOperator selfMod(Var lhs, Var rhs) { + lhs.value %= rhs.value; + return JexlOperator.ASSIGN; + } + + public Var and(Var lhs, Var rhs) { + return new Var(lhs.value & rhs.value); + } + + public JexlOperator selfAnd(Var lhs, Var rhs) { + lhs.value &= rhs.value; + return JexlOperator.ASSIGN; + } + + public Var or(Var lhs, Var rhs) { + return new Var(lhs.value | rhs.value); + } + + public JexlOperator selfOr(Var lhs, Var rhs) { + lhs.value |= rhs.value; + return JexlOperator.ASSIGN; + } + + public Var xor(Var lhs, Var rhs) { + return new Var(lhs.value ^ rhs.value); + } + + public JexlOperator selfXor(Var lhs, Var rhs) { + lhs.value ^= rhs.value; + return JexlOperator.ASSIGN; + } + } +} Propchange: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/SideEffectTest.java ------------------------------------------------------------------------------ svn:eol-style = native Modified: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/junit/Asserter.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/junit/Asserter.java?rev=1692852&r1=1692851&r2=1692852&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/junit/Asserter.java (original) +++ commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/junit/Asserter.java Mon Jul 27 10:02:49 2015 @@ -151,4 +151,21 @@ public class Asserter extends Assert { public Object removeVariable(String name) { return variables.remove(name); } + + /** + * Gets a variable of a certain name. + * + * @param name variable name + * @return value variable value + */ + public Object getVariable(String name) { + return variables.get(name); + } + + /** + * @return the variables map + */ + public Map<String, Object> getVariables() { + return variables; + } }