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 3067866 JEXL-298: de-interlace size/empty operators and size/empty method handling, simplified grammar, removed unused code, added test Task #JEXL-298 - Unable to call 'empty' and 'size' member methods with parameters 3067866 is described below commit 306786606116839960dd131c49dfbd287533bb6d Author: Henri Biestro <hbies...@gmail.com> AuthorDate: Sun May 26 21:14:58 2019 +0200 JEXL-298: de-interlace size/empty operators and size/empty method handling, simplified grammar, removed unused code, added test Task #JEXL-298 - Unable to call 'empty' and 'size' member methods with parameters --- RELEASE-NOTES.txt | 1 + .../org/apache/commons/jexl3/JexlArithmetic.java | 54 ++++++++----- .../org/apache/commons/jexl3/JexlOperator.java | 6 +- .../apache/commons/jexl3/internal/Debugger.java | 26 ++----- .../apache/commons/jexl3/internal/Interpreter.java | 40 ++++------ .../apache/commons/jexl3/internal/Operators.java | 14 ++-- .../commons/jexl3/internal/ScriptVisitor.java | 14 +--- .../org/apache/commons/jexl3/parser/Parser.jjt | 59 ++++++--------- .../apache/commons/jexl3/parser/ParserVisitor.java | 6 +- src/site/xdoc/changes.xml | 18 ++--- .../org/apache/commons/jexl3/ArithmeticTest.java | 4 +- .../org/apache/commons/jexl3/ExceptionTest.java | 8 +- .../org/apache/commons/jexl3/Issues200Test.java | 88 ++++++++++++++++------ 13 files changed, 169 insertions(+), 169 deletions(-) diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index b5f402e..b345cd7 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -78,6 +78,7 @@ Bugs Fixed in 3.2: * JEXL-301: Array access operator does not fail on null object in non-strict arithmetic mode * JEXL-300: Ant-ish variables should not use safe-access operator syntax * JEXL-299: Improve message error when method could not be found +* JEXL-298: Unable to call 'empty' and 'size' member methods with parameters * JEXL-296: Real literal in scientific format is not parsed without suffix * JEXL-291: Using sandbox prevents array-syntax lookup by number in Map * JEXL-290: Safe navigation fails on chained method calls diff --git a/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java b/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java index 9ff61c4..094f2d7 100644 --- a/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java +++ b/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java @@ -825,13 +825,13 @@ public class JexlArithmetic { } throw new ArithmeticException("Object negate:(" + val + ")"); } - + /** * Whether negate called with a given argument will always return the same result. * <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() + * @see #isNegateStable() * @return true if negate is idempotent, false otherwise */ public boolean isNegateStable() { @@ -867,7 +867,7 @@ public class JexlArithmetic { } throw new ArithmeticException("Object positivize:(" + val + ")"); } - + /** * Whether positivize called with a given argument will always return the same result. * <p>This is used to determine whether positivize results on number literals can be cached. @@ -878,7 +878,7 @@ public class JexlArithmetic { public boolean isPositivizeStable() { return true; } - + /** * Test if left contains right (right matches/in left). * <p>Beware that this method arguments are the opposite of the operator arguments. @@ -968,27 +968,41 @@ public class JexlArithmetic { /** * Check for emptiness of various types: Number, Collection, Array, Map, String. + * <p>Override or overload this method to add new signatures to the size operators. + * @param object the object to check the emptiness of + * @return the boolean or false if object is not null + * @since 3.2 + */ + public Boolean empty(Object object) { + Boolean e = isEmpty(object); + return e == null ? Boolean.TRUE : e; + } + + /** + * Check for emptiness of various types: Number, Collection, Array, Map, String. * * @param object the object to check the emptiness of * @return the boolean or null if there is no arithmetic solution */ public Boolean isEmpty(Object object) { - if (object instanceof Number) { - double d = ((Number) object).doubleValue(); - return Double.isNaN(d) || d == 0.d ? Boolean.TRUE : Boolean.FALSE; - } - if (object instanceof CharSequence) { - return ((CharSequence) object).length() == 0 ? Boolean.TRUE : Boolean.FALSE; - } - if (object.getClass().isArray()) { - return Array.getLength(object) == 0 ? Boolean.TRUE : Boolean.FALSE; - } - if (object instanceof Collection<?>) { - return ((Collection<?>) object).isEmpty() ? Boolean.TRUE : Boolean.FALSE; - } - // Map isn't a collection - if (object instanceof Map<?, ?>) { - return ((Map<?, ?>) object).isEmpty() ? Boolean.TRUE : Boolean.FALSE; + if (object != null) { + if (object instanceof Number) { + double d = ((Number) object).doubleValue(); + return Double.isNaN(d) || d == 0.d ? Boolean.TRUE : Boolean.FALSE; + } + if (object instanceof CharSequence) { + return ((CharSequence) object).length() == 0 ? Boolean.TRUE : Boolean.FALSE; + } + if (object.getClass().isArray()) { + return Array.getLength(object) == 0 ? Boolean.TRUE : Boolean.FALSE; + } + if (object instanceof Collection<?>) { + return ((Collection<?>) object).isEmpty() ? Boolean.TRUE : Boolean.FALSE; + } + // Map isn't a collection + if (object instanceof Map<?, ?>) { + return ((Map<?, ?>) object).isEmpty() ? Boolean.TRUE : Boolean.FALSE; + } } return null; } diff --git a/src/main/java/org/apache/commons/jexl3/JexlOperator.java b/src/main/java/org/apache/commons/jexl3/JexlOperator.java index f510612..7cbaaba 100644 --- a/src/main/java/org/apache/commons/jexl3/JexlOperator.java +++ b/src/main/java/org/apache/commons/jexl3/JexlOperator.java @@ -191,7 +191,7 @@ public enum JexlOperator { * @see JexlArithmetic#negate */ NEGATE("-", "negate", 1), - + /** * Positivize operator. * <br><strong>Syntax:</strong> <code>+x</code> @@ -203,8 +203,8 @@ public enum JexlOperator { /** * Empty operator. * <br><strong>Syntax:</strong> <code>empty x</code> or <code>empty(x)</code> - * <br><strong>Method:</strong> <code>boolean isEmpty(L x);</code>. - * @see JexlArithmetic#isEmpty + * <br><strong>Method:</strong> <code>boolean empty(L x);</code>. + * @see JexlArithmetic#empty */ EMPTY("empty", "empty", 1), 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 c3cfd35..43339f9 100644 --- a/src/main/java/org/apache/commons/jexl3/internal/Debugger.java +++ b/src/main/java/org/apache/commons/jexl3/internal/Debugger.java @@ -40,7 +40,6 @@ import org.apache.commons.jexl3.parser.ASTEQNode; import org.apache.commons.jexl3.parser.ASTERNode; import org.apache.commons.jexl3.parser.ASTEWNode; import org.apache.commons.jexl3.parser.ASTEmptyFunction; -import org.apache.commons.jexl3.parser.ASTEmptyMethod; import org.apache.commons.jexl3.parser.ASTExtendedLiteral; import org.apache.commons.jexl3.parser.ASTFalseNode; import org.apache.commons.jexl3.parser.ASTForeachStatement; @@ -84,7 +83,6 @@ import org.apache.commons.jexl3.parser.ASTSetOrNode; import org.apache.commons.jexl3.parser.ASTSetSubNode; import org.apache.commons.jexl3.parser.ASTSetXorNode; import org.apache.commons.jexl3.parser.ASTSizeFunction; -import org.apache.commons.jexl3.parser.ASTSizeMethod; import org.apache.commons.jexl3.parser.ASTStringLiteral; import org.apache.commons.jexl3.parser.ASTSubNode; import org.apache.commons.jexl3.parser.ASTTernaryNode; @@ -132,7 +130,7 @@ public class Debugger extends ParserVisitor implements JexlInfo.Detail { */ public Debugger() { } - + /** * Resets this debugger state. */ @@ -145,7 +143,7 @@ public class Debugger extends ParserVisitor implements JexlInfo.Detail { indent = 2; depth = Integer.MAX_VALUE; } - + /** * Position the debugger on the root of an expression. * @param jscript the expression @@ -255,7 +253,7 @@ public class Debugger extends ParserVisitor implements JexlInfo.Detail { public void setIndentation(int level) { indentation(level); } - + /** * Sets the indentation level. * @param level the number of spaces for indentation, none if less or equal to zero @@ -270,7 +268,7 @@ public class Debugger extends ParserVisitor implements JexlInfo.Detail { indentLevel = 0; return this; } - + /** * Sets this debugger relative maximum depth. * @param rdepth the maximum relative depth from the debugged node @@ -554,13 +552,6 @@ public class Debugger extends ParserVisitor implements JexlInfo.Detail { } @Override - protected Object visit(ASTEmptyMethod node, Object data) { - accept(node.jjtGetChild(0), data); - check(node, ".empty()", data); - return data; - } - - @Override protected Object visit(ASTEQNode node, Object data) { return infixChildren(node, " == ", false, data); } @@ -938,13 +929,6 @@ public class Debugger extends ParserVisitor implements JexlInfo.Detail { } @Override - protected Object visit(ASTSizeMethod node, Object data) { - accept(node.jjtGetChild(0), data); - check(node, ".size()", data); - return data; - } - - @Override protected Object visit(ASTStringLiteral node, Object data) { String img = node.getLiteral().replace("'", "\\'"); return check(node, "'" + img + "'", data); @@ -995,7 +979,7 @@ public class Debugger extends ParserVisitor implements JexlInfo.Detail { protected Object visit(ASTUnaryPlusNode node, Object data) { return prefixChild(node, "+", data); } - + @Override protected Object visit(ASTVar node, Object data) { builder.append("var "); 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 8dd9a0a..f0aaa66 100644 --- a/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java +++ b/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java @@ -49,7 +49,6 @@ import org.apache.commons.jexl3.parser.ASTEQNode; import org.apache.commons.jexl3.parser.ASTERNode; import org.apache.commons.jexl3.parser.ASTEWNode; import org.apache.commons.jexl3.parser.ASTEmptyFunction; -import org.apache.commons.jexl3.parser.ASTEmptyMethod; import org.apache.commons.jexl3.parser.ASTExtendedLiteral; import org.apache.commons.jexl3.parser.ASTFalseNode; import org.apache.commons.jexl3.parser.ASTForeachStatement; @@ -95,7 +94,6 @@ import org.apache.commons.jexl3.parser.ASTSetOrNode; import org.apache.commons.jexl3.parser.ASTSetSubNode; import org.apache.commons.jexl3.parser.ASTSetXorNode; import org.apache.commons.jexl3.parser.ASTSizeFunction; -import org.apache.commons.jexl3.parser.ASTSizeMethod; import org.apache.commons.jexl3.parser.ASTStringLiteral; import org.apache.commons.jexl3.parser.ASTSubNode; import org.apache.commons.jexl3.parser.ASTTernaryNode; @@ -147,7 +145,7 @@ public class Interpreter extends InterpreterBase { super(ii, jexla); frame = ii.frame; } - + /** * Swaps the current thread local interpreter. * @param inter the interpreter or null @@ -231,7 +229,7 @@ public class Interpreter extends InterpreterBase { } return null; } - + /** * Gets an attribute of an object. * @@ -252,7 +250,7 @@ public class Interpreter extends InterpreterBase { public void setAttribute(Object object, Object attribute, Object value) { setAttribute(object, attribute, value, null); } - + @Override protected Object visit(ASTAddNode node, Object data) { Object left = node.jjtGetChild(0).jjtAccept(this, data); @@ -527,7 +525,7 @@ public class Interpreter extends InterpreterBase { throw new JexlException(valNode, "- error", xrt); } } - + @Override protected Object visit(ASTUnaryPlusNode node, Object data) { // use cached value if literal @@ -553,7 +551,7 @@ public class Interpreter extends InterpreterBase { throw new JexlException(valNode, "- error", xrt); } } - + @Override protected Object visit(ASTBitwiseComplNode node, Object data) { Object arg = node.jjtGetChild(0).jjtAccept(this, data); @@ -898,12 +896,6 @@ public class Interpreter extends InterpreterBase { } @Override - protected Object visit(ASTSizeMethod node, Object data) { - Object val = node.jjtGetChild(0).jjtAccept(this, data); - return operators.size(node, val); - } - - @Override protected Object visit(ASTEmptyFunction node, Object data) { try { Object value = node.jjtGetChild(0).jjtAccept(this, data); @@ -914,12 +906,6 @@ public class Interpreter extends InterpreterBase { } @Override - protected Object visit(ASTEmptyMethod node, Object data) { - Object val = node.jjtGetChild(0).jjtAccept(this, data); - return operators.empty(node, val); - } - - @Override protected Object visit(ASTJexlScript node, Object data) { if (node instanceof ASTJexlLambda && !((ASTJexlLambda) node).isTopLevel()) { return new Closure(this, (ASTJexlLambda) node); @@ -951,7 +937,7 @@ public class Interpreter extends InterpreterBase { return null; } } - + @Override protected Object visit(ASTIdentifier node, Object data) { cancelCheck(node); @@ -968,7 +954,7 @@ public class Interpreter extends InterpreterBase { && !(context.has(name)) && !node.isTernaryProtected()) { return jexl.safe - ? null + ? null : unsolvableVariable(node, name, !(node.getSymbol() >= 0 || context.has(name))); } return value; @@ -1037,7 +1023,7 @@ public class Interpreter extends InterpreterBase { Object id = evalIdentifier(node); return getAttribute(data, id, node); } - + @Override protected Object visit(ASTReference node, Object data) { cancelCheck(node); @@ -1083,7 +1069,7 @@ public class Interpreter extends InterpreterBase { } else { antish = false; } - } + } // attempt to evaluate the property within the object (visit(ASTIdentifierAccess node)) object = objectNode.jjtAccept(this, object); cancelCheck(node); @@ -1158,7 +1144,7 @@ public class Interpreter extends InterpreterBase { } return object; } - + @Override protected Object visit(ASTAssignment node, Object data) { return executeAssign(node, null, data); @@ -1488,7 +1474,7 @@ public class Interpreter extends InterpreterBase { } else if (context.has(methodName)) { functor = context.get(methodName); isavar = functor != null; - } + } // name is a variable, cant be cached cacheable &= !isavar; } @@ -1509,7 +1495,7 @@ public class Interpreter extends InterpreterBase { // safe lhs return null; } - + // solving the call site CallDispatcher call = new CallDispatcher(node, cacheable); try { @@ -1517,7 +1503,7 @@ public class Interpreter extends InterpreterBase { Object eval = call.tryEval(target, methodName, argv); if (JexlEngine.TRY_FAILED != eval) { return eval; - } + } boolean functorp = false; boolean narrow = false; // pseudo loop to try acquiring methods without and with argument narrowing 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 5efd656..be52c79 100644 --- a/src/main/java/org/apache/commons/jexl3/internal/Operators.java +++ b/src/main/java/org/apache/commons/jexl3/internal/Operators.java @@ -318,21 +318,21 @@ public class Operators { if (object == null) { return Boolean.TRUE; } - final JexlArithmetic arithmetic = interpreter.arithmetic; - final JexlUberspect uberspect = interpreter.uberspect; Object result = Operators.this.tryOverload(node, JexlOperator.EMPTY, object); if (result != JexlEngine.TRY_FAILED) { return result; } + final JexlArithmetic arithmetic = interpreter.arithmetic; result = arithmetic.isEmpty(object); if (result == null) { + final JexlUberspect uberspect = interpreter.uberspect; 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); + result = vm.invoke(object, Interpreter.EMPTY_PARAMS); } catch (Exception xany) { interpreter.operatorError(node, JexlOperator.EMPTY, xany); } @@ -347,27 +347,27 @@ public class Operators { * <p>Note that the result may not be an integer. * * @param node the node that gave the value to size - * @param object the object to get the size of. + * @param object the object to get the size of * @return the evaluation result */ protected Object size(JexlNode node, Object object) { if (object == null) { return 0; } - final JexlArithmetic arithmetic = interpreter.arithmetic; - final JexlUberspect uberspect = interpreter.uberspect; Object result = Operators.this.tryOverload(node, JexlOperator.SIZE, object); if (result != JexlEngine.TRY_FAILED) { return result; } + final JexlArithmetic arithmetic = interpreter.arithmetic; result = arithmetic.size(object); if (result == null) { + final JexlUberspect uberspect = interpreter.uberspect; // 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); + result = vm.invoke(object, Interpreter.EMPTY_PARAMS); } catch (Exception xany) { interpreter.operatorError(node, JexlOperator.SIZE, xany); } 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 ca33ca3..94e946a 100644 --- a/src/main/java/org/apache/commons/jexl3/internal/ScriptVisitor.java +++ b/src/main/java/org/apache/commons/jexl3/internal/ScriptVisitor.java @@ -40,7 +40,6 @@ import org.apache.commons.jexl3.parser.ASTEQNode; import org.apache.commons.jexl3.parser.ASTERNode; import org.apache.commons.jexl3.parser.ASTEWNode; import org.apache.commons.jexl3.parser.ASTEmptyFunction; -import org.apache.commons.jexl3.parser.ASTEmptyMethod; import org.apache.commons.jexl3.parser.ASTExtendedLiteral; import org.apache.commons.jexl3.parser.ASTFalseNode; import org.apache.commons.jexl3.parser.ASTForeachStatement; @@ -84,7 +83,6 @@ import org.apache.commons.jexl3.parser.ASTSetOrNode; import org.apache.commons.jexl3.parser.ASTSetSubNode; import org.apache.commons.jexl3.parser.ASTSetXorNode; import org.apache.commons.jexl3.parser.ASTSizeFunction; -import org.apache.commons.jexl3.parser.ASTSizeMethod; import org.apache.commons.jexl3.parser.ASTStringLiteral; import org.apache.commons.jexl3.parser.ASTSubNode; import org.apache.commons.jexl3.parser.ASTTernaryNode; @@ -326,7 +324,7 @@ public class ScriptVisitor extends ParserVisitor { protected Object visit(ASTUnaryPlusNode node, Object data) { return visitNode(node, data); } - + @Override protected Object visit(ASTBitwiseComplNode node, Object data) { return visitNode(node, data); @@ -408,11 +406,6 @@ public class ScriptVisitor extends ParserVisitor { } @Override - protected Object visit(ASTEmptyMethod node, Object data) { - return visitNode(node, data); - } - - @Override protected Object visit(ASTSizeFunction node, Object data) { return visitNode(node, data); } @@ -428,11 +421,6 @@ public class ScriptVisitor extends ParserVisitor { } @Override - protected Object visit(ASTSizeMethod node, Object data) { - return visitNode(node, data); - } - - @Override protected Object visit(ASTConstructorNode node, Object data) { return visitNode(node, data); } 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 edede88..4cc2426 100644 --- a/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt +++ b/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt @@ -113,35 +113,35 @@ TOKEN_MGR_DECLS : { <*> TOKEN : /* KEYWORDS */ { - < IF : "if" > { popDot(); } - | < ELSE : "else" > { popDot(); } - | < FOR : "for" > { popDot(); } - | < WHILE : "while" > { popDot(); } - | < DO : "do" > { popDot(); } - | < NEW : "new" > { popDot(); } - | < VAR : "var" > { popDot(); } + < IF : "if" > { popDot(); } + | < ELSE : "else" > { popDot(); } + | < FOR : "for" > { popDot(); } + | < WHILE : "while" > { popDot(); } + | < DO : "do" > { popDot(); } + | < NEW : "new" > { popDot(); } + | < VAR : "var" > { popDot(); } | < EMPTY : "empty" > { popDot(); } /* Revert state to default if was DOT_ID. */ | < SIZE : "size" > { popDot(); } /* Revert state to default if was DOT_ID. */ - | < NULL : "null" > { popDot(); } - | < TRUE : "true" > { popDot(); } - | < FALSE : "false" > { popDot(); } - | < RETURN : "return" > { popDot(); } - | < FUNCTION : "function" > { popDot(); } + | < NULL : "null" > { popDot(); } + | < TRUE : "true" > { popDot(); } + | < FALSE : "false" > { popDot(); } + | < RETURN : "return" > { popDot(); } + | < FUNCTION : "function" > { popDot(); } | < LAMBDA : "->" > - | < BREAK : "break" > { popDot(); } - | < CONTINUE : "continue" > { popDot(); } - | < PRAGMA : "#pragma" > { popDot(); } + | < BREAK : "break" > { popDot(); } + | < CONTINUE : "continue" > { popDot(); } + | < PRAGMA : "#pragma" > { popDot(); } } <*> TOKEN : { /* SEPARATORS */ - < LPAREN : "("> + < LPAREN : "("> | < RPAREN : ")"> | < LCURLY : "{" > | < RCURLY : "}" > | < LBRACKET : "[" > | < RBRACKET : "]" > | < SEMICOL : ";" > - | < COLON : ":" > + | < COLON : ":" > | < COMMA : "," > | < DOT : "." > { pushDot(); } /* Lexical state is now DOT_ID */ | < QDOT : "?." > { pushDot(); } /* Lexical state is now DOT_ID */ @@ -244,7 +244,7 @@ TOKEN_MGR_DECLS : { <DEFAULT, REGISTERS> TOKEN : /* LITERALS */ { <INTEGER_LITERAL: - <DECIMAL_LITERAL> (<INT_SFX>)? + <DECIMAL_LITERAL> (<INT_SFX>)? | <HEX_LITERAL> (<INT_SFX>)? | <OCTAL_LITERAL> (<INT_SFX>)? > @@ -252,7 +252,7 @@ TOKEN_MGR_DECLS : { | <#HEX_LITERAL: "0" ["x","X"] (["0"-"9","a"-"f","A"-"F"])+> | <#OCTAL_LITERAL: "0" (["0"-"7"])*> | <#INT_SFX : ["l","L","h","H"]> -| +| <FLOAT_LITERAL: (["0"-"9"])+ "." (["0"-"9"])+ (<FLT_SFX>)? | (["0"-"9"])+ (".")? (<FLT_SFX>) @@ -328,8 +328,8 @@ void Statement() #void : {} { <SEMICOL> | LOOKAHEAD(<ANNOTATION>) AnnotatedStatement() - | LOOKAHEAD(Expression()) ExpressionStatement() - | Block() + | LOOKAHEAD(Expression()) ExpressionStatement() + | Block() | IfStatement() | ForeachStatement() | WhileStatement() @@ -768,20 +768,11 @@ void SetLiteral() : {} <LCURLY> (Expression() ( <COMMA> Expression() )*)? <RCURLY> } + /*************************************** * Functions & Methods ***************************************/ -void EmptyMethod() #EmptyMethod() : {} -{ - <EMPTY> <LPAREN> <RPAREN> -} - -void SizeMethod() #SizeMethod() : {} -{ - <SIZE> <LPAREN> <RPAREN> -} - void Arguments() #Arguments : {} { <LPAREN> (Expression() (<COMMA> Expression())* )? <RPAREN> @@ -855,7 +846,7 @@ Token dotName() #void : } { ( t = <DOT_IDENTIFIER> | t=<IF> | t=<ELSE> | t=<FOR> | t=<WHILE> | t=<DO> | t=<NEW>| t=<EMPTY> | t=<SIZE> | t=<TRUE> | t=<FALSE> | t=<NULL> - | t=<_OR> | t=<_AND>| t=<NOT> | t=<NE> | t=<EQ> | t=<GT> | t=<GE> | t=<LT> | t=<LE> + | t=<_OR> | t=<_AND>| t=<NOT> | t=<NE> | t=<EQ> | t=<GT> | t=<GE> | t=<LT> | t=<LE> | t=<VAR> | t=<FUNCTION> ) { return t ;} } @@ -927,10 +918,6 @@ void PrimaryExpression() #void : {} void MethodCall() #void : {} { - LOOKAHEAD(<DOT> <SIZE>) (<DOT> <SIZE> <LPAREN> <RPAREN>) #SizeMethod(1) - | - LOOKAHEAD(<DOT> <EMPTY>) (<DOT> <EMPTY> <LPAREN> <RPAREN>) #EmptyMethod(1) - | (MemberAccess() (LOOKAHEAD(<LPAREN>) Arguments())+) #MethodNode(>1) } 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 a3d0d59..5aa1296 100644 --- a/src/main/java/org/apache/commons/jexl3/parser/ParserVisitor.java +++ b/src/main/java/org/apache/commons/jexl3/parser/ParserVisitor.java @@ -113,7 +113,7 @@ public abstract class ParserVisitor { protected abstract Object visit(ASTModNode node, Object data); protected abstract Object visit(ASTUnaryMinusNode node, Object data); - + protected abstract Object visit(ASTUnaryPlusNode node, Object data); protected abstract Object visit(ASTBitwiseComplNode node, Object data); @@ -148,16 +148,12 @@ public abstract class ParserVisitor { protected abstract Object visit(ASTEmptyFunction node, Object data); - protected abstract Object visit(ASTEmptyMethod node, Object data); - protected abstract Object visit(ASTSizeFunction node, Object data); protected abstract Object visit(ASTFunctionNode node, Object data); protected abstract Object visit(ASTMethodNode node, Object data); - protected abstract Object visit(ASTSizeMethod node, Object data); - protected abstract Object visit(ASTConstructorNode node, Object data); protected abstract Object visit(ASTArrayAccess node, Object data); diff --git a/src/site/xdoc/changes.xml b/src/site/xdoc/changes.xml index 1bf8259..6a0b99f 100644 --- a/src/site/xdoc/changes.xml +++ b/src/site/xdoc/changes.xml @@ -26,7 +26,7 @@ </properties> <body> <release version="3.2" date="unreleased"> - <action dev="henrib" type="fix" issue="JEXL-305 due-to="Dmitri Blinov"> + <action dev="henrib" type="fix" issue="JEXL-305" due-to="Dmitri Blinov"> Script debugger produces incorrect syntax </action> <action dev="henrib" type="fix" issue="JEXL-304" due-to="Marcus Warm"> @@ -38,25 +38,25 @@ <action dev="henrib" type="fix" issue="JEXL-301" due-to="Dmitri Blinov"> Array access operator does not fail on null object in non-strict arithmetic mode </action> - <action dev="henrib" type="fix" issue="JEXL-296" due-to="Dmitri Blinov"> - Ant-ish variables should not use safe-access operator syntax - </action> <action dev="henrib" type="fix" issue="JEXL-299" due-to="Jarek Cecho"> Improve message error when method could not be found </action> + <action dev="henrib" type="fix" issue="JEXL-298" due-to="Dmitri Blinov"> + Unable to call 'empty' and 'size' member methods with parameters + </action> <action dev="henrib" type="fix" issue="JEXL-296" due-to="Dmitri Blinov"> Real literal in scientific format is not parsed without suffix </action> - <action dev="henrib" type="add" issue="JEXL-295"> + <action dev="henrib" type="add" issue="JEXL-295"> Add unary plus operator </action> <action dev="henrib" type="add" issue="JEXL-292" due-to="Dmitri Blinov"> Allow specifying custom Permissions class for Uberspect to be used later by Introspector </action> - <action dev="henrib" type="fix" issue="JEXL-291" due-to="David Costanzo"> + <action dev="henrib" type="fix" issue="JEXL-291" due-to="David Costanzo"> Using sandbox prevents array-syntax lookup by number in Map </action> - <action dev="henrib" type="fix" issue="JEXL-290"> + <action dev="henrib" type="fix" issue="JEXL-290"> Safe navigation fails on chained method calls </action> <action dev="henrib" type="fix" issue="JEXL-289" due-to="Dmitri Blinov"> @@ -68,10 +68,10 @@ <action dev="henrib" type="fix" issue="JEXL-287" due-to="Dmitri Blinov"> Wrong resolution of local variables </action> - <action dev="henrib" type="fix" issue="JEXL-286" due-to="Dmitri Blinov"> + <action dev="henrib" type="fix" issue="JEXL-286" due-to="Dmitri Blinov"> For statement without a body should update its variable </action> - <action dev="henrib" type="fix" issue="JEXL-285"> + <action dev="henrib" type="fix" issue="JEXL-285"> For statement variable may seem unaffected by iteration </action> <action dev="henrib" type="fix" issue="JEXL-282" due-to="Dmitri Blinov"> diff --git a/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java b/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java index 811073d..9ec7f75 100644 --- a/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java +++ b/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java @@ -156,7 +156,7 @@ public class ArithmeticTest extends JexlTestCase { asserter.assertExpression("-aBigDecimal", new BigDecimal("-8.8")); } } - + /** * test some simple mathematical calculations */ @@ -185,7 +185,7 @@ public class ArithmeticTest extends JexlTestCase { asserter.assertExpression("+aBigDecimal", new BigDecimal("8.8")); } } - + /** * test some simple mathematical calculations */ diff --git a/src/test/java/org/apache/commons/jexl3/ExceptionTest.java b/src/test/java/org/apache/commons/jexl3/ExceptionTest.java index e767eda..2f744a2 100644 --- a/src/test/java/org/apache/commons/jexl3/ExceptionTest.java +++ b/src/test/java/org/apache/commons/jexl3/ExceptionTest.java @@ -101,7 +101,7 @@ public class ExceptionTest extends JexlTestCase { Throwable xth = xany.getCause(); Assert.assertEquals(NullPointerException.class, xth.getClass()); } - + try { jexl.invokeMethod(npe, "foo", 42); Assert.fail("Should have thrown JexlException.Method"); @@ -262,16 +262,16 @@ public class ExceptionTest extends JexlTestCase { } } catch(JexlException xjexl) { if (!strict || silent) { - Assert.fail("should not have thrown an exception"); + Assert.fail(src + ": should not have thrown an exception"); } } if (strict) { if (silent && l.count("warn") == 0) { - Assert.fail("should have generated a warning"); + Assert.fail(src + ": should have generated a warning"); } } else { if (l.count("debug") == 0) { - Assert.fail("should have generated a debug"); + Assert.fail(src + ": should have generated a debug"); } Assert.assertEquals(42, r); } diff --git a/src/test/java/org/apache/commons/jexl3/Issues200Test.java b/src/test/java/org/apache/commons/jexl3/Issues200Test.java index 009f7fe..cc28fc3 100644 --- a/src/test/java/org/apache/commons/jexl3/Issues200Test.java +++ b/src/test/java/org/apache/commons/jexl3/Issues200Test.java @@ -389,7 +389,7 @@ public class Issues200Test extends JexlTestCase { result = script.execute(ctx); Assert.assertEquals(10, result); } - + @Test public void test230() throws Exception { JexlEngine jexl = new JexlBuilder().cache(4).create(); @@ -572,8 +572,8 @@ public class Issues200Test extends JexlTestCase { result = script.execute(ctxt); Assert.assertTrue(result instanceof JexlScript); } - - + + @Test public void test274() throws Exception { JexlEngine jexl = new JexlBuilder().strict(true).safe(true).stackOverflow(5).create(); @@ -600,8 +600,8 @@ public class Issues200Test extends JexlTestCase { String sxs = xstack.toString(); Assert.assertTrue(sxs.contains("jvm")); } - } - + } + @Test public void test278() throws Exception { String[] srcs = new String[]{ @@ -620,7 +620,7 @@ public class Issues200Test extends JexlTestCase { ctxt.set("union", "42"); Object value; JexlScript jc; - for(int i = 0; i < srcs.length; ++i) { + for(int i = 0; i < srcs.length; ++i) { String src = srcs[i]; try { jc = jexl.createScript(src); @@ -635,7 +635,7 @@ public class Issues200Test extends JexlTestCase { Assert.assertEquals(src, ctls[i], value); } } - + public static class Context279 extends MapContext { public String identity(String x) { return x; @@ -721,7 +721,7 @@ public class Issues200Test extends JexlTestCase { ctxt.set("z.y", null); } } - + @Test public void test279b() throws Exception { Object result; @@ -733,10 +733,15 @@ public class Issues200Test extends JexlTestCase { script = jexl.createScript(src); result = script.execute(ctxt, "abc"); Assert.assertEquals("a", result); - result = script.execute(ctxt, (Object) null); + result = null; + try { + result = script.execute(ctxt, (Object) null); + } catch(JexlException xany) { + Assert.assertNotNull(xany.getMessage()); + } Assert.assertNull(result); - } - + } + @Test public void test285() throws Exception { List<String> out = new ArrayList<String>(6); @@ -760,7 +765,7 @@ public class Issues200Test extends JexlTestCase { List<String> ctl = Arrays.asList("g", "h", "i", "j", "k", "l"); Assert.assertEquals(ctl, out); } - + @Test public void test285a() throws Exception { List<String> out = new ArrayList<String>(6); @@ -778,13 +783,13 @@ public class Issues200Test extends JexlTestCase { List<String> ctl = Arrays.asList("g", "h", "i", "j", "k", "l"); Assert.assertEquals(ctl, out); } - + @Test public void test285b() throws Exception { List<String> out = new ArrayList<String>(6); JexlContext ctxt = new MapContext(); ctxt.set("$out", out); - String src = + String src = "for(b: ['g','h','i']) { $out.add(b); }\n" + "for(var b: ['j','k','l']) { $out.add(b);}\n" + "$out.size()"; @@ -796,14 +801,14 @@ public class Issues200Test extends JexlTestCase { List<String> ctl = Arrays.asList("g", "h", "i", "j", "k", "l"); Assert.assertEquals(ctl, out); } - + @Test public void test286() { String s286 = "var x = 0; for(x : 1..2){}; return x"; JexlEngine jexl = new JexlBuilder().strict(true).create(); Assert.assertEquals(2, jexl.createScript(s286).execute(null)); } - + @Test public void test287() { JexlContext ctxt = new MapContext(); @@ -848,7 +853,7 @@ public class Issues200Test extends JexlTestCase { } Assert.assertNull(result); } - + @Test public void test289() { JexlContext ctxt = new MapContext(); @@ -861,7 +866,7 @@ public class Issues200Test extends JexlTestCase { result = script.execute(ctxt); Assert.assertNull(result); } - + @Test public void test290a() throws Exception { Object result; @@ -888,7 +893,7 @@ public class Issues200Test extends JexlTestCase { } else { Assert.assertTrue(src + ": " + xmethod.toString(), xmethod.toString().contains("nothing")); } - } + } } } } @@ -908,7 +913,7 @@ public class Issues200Test extends JexlTestCase { Assert.assertNull(result); } } - + @Test public void test291() throws Exception { final String str = "{1:'one'}[1]"; @@ -925,7 +930,7 @@ public class Issues200Test extends JexlTestCase { value = e.evaluate(ctxt); Assert.assertEquals("one", value); } - + @Test public void testTemplate6565a() throws Exception { JexlEngine jexl = new JexlBuilder().create(); @@ -948,7 +953,7 @@ public class Issues200Test extends JexlTestCase { Assert.assertNotNull(refactored); Assert.assertEquals(source, refactored); } - + @Test public void testTemplate6565b() throws Exception { JexlEngine jexl = new JexlBuilder().create(); @@ -971,4 +976,43 @@ public class Issues200Test extends JexlTestCase { Assert.assertNotNull(refactored); Assert.assertEquals(source, refactored); } + + public static class Cls298 { + int sz = 42; + + public int size() { + return sz; + } + + public int size(int x) { + return sz + x; + } + + public boolean isEmpty() { + return sz <= 0; + } + } + + @Test + public void test298() throws Exception { + Cls298 c298 = new Cls298(); + JexlContext ctxt = new MapContext(); + JexlEngine jexl = new JexlBuilder().create(); + + String str = "c.size()"; + JexlScript e = jexl.createScript(str, "c"); + Object value = e.execute(ctxt, c298); + Assert.assertEquals(str, 42, value); + + str = "size c"; + e = jexl.createScript(str, "c"); + value = e.execute(ctxt, c298); + Assert.assertEquals(str, 42, value); + + str = "c.size(127)"; + e = jexl.createScript(str, "c"); + value = e.execute(ctxt, c298); + Assert.assertEquals(str, 169, value); + + } }