Skip to content

Commit

Permalink
JEXL-428: operators improvements;
Browse files Browse the repository at this point in the history
  • Loading branch information
henrib committed Sep 13, 2024
1 parent 715430f commit 2915b37
Show file tree
Hide file tree
Showing 7 changed files with 493 additions and 284 deletions.
29 changes: 27 additions & 2 deletions src/main/java/org/apache/commons/jexl3/JexlOperator.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@
* @since 3.0
*/
public enum JexlOperator {

/**
* Add operator.
* <br><strong>Syntax:</strong> {@code x + y}
Expand Down Expand Up @@ -379,6 +378,7 @@ public enum JexlOperator {
* Marker for side effect.
* <br>Returns this from 'self*' overload method to let the engine know the side effect has been performed and
* there is no need to assign the result.
* @deprecated 3.4.1
*/
ASSIGN("=", null, null),

Expand Down Expand Up @@ -424,7 +424,32 @@ public enum JexlOperator {
* <br><strong>Method:</strong> {@code boolean testCondition(R y);}.
* @since 3.3
*/
CONDITION("?", "testCondition", 1);
CONDITION("?", "testCondition", 1),

/**
* Compare overload as in compare(x, y).
* <br><strong>Method:</strong> {@code boolean compare(L x, R y);}.
* @since 3.4.1
*/
COMPARE("<>", "compare", 2),

/**
* Not-Contains operator.
* <p>Not overridable, calls !(contain(...))</p>
*/
NOT_CONTAINS("!~", null, CONTAINS),

/**
* Not-Starts-With operator.
* <p>Not overridable, calls !(startsWith(...))</p>
*/
NOT_STARTSWITH("!^", null, STARTSWITH),

/**
* Not-Ends-With operator.
* <p>Not overridable, calls !(endsWith(...))</p>
*/
NOT_ENDSWITH("!$", null, ENDSWITH),;

/**
* The operator symbol.
Expand Down
16 changes: 8 additions & 8 deletions src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
Original file line number Diff line number Diff line change
Expand Up @@ -1421,14 +1421,14 @@ protected Object visit(final ASTERNode node, final Object data) {
final Object right = node.jjtGetChild(1).jjtAccept(this, data);
// note the arguments inversion between 'in'/'matches' and 'contains'
// if x in y then y contains x
return operators.contains(node, "=~", right, left);
return operators.contains(node, JexlOperator.CONTAINS, right, left);
}

@Override
protected Object visit(final ASTEWNode node, final Object data) {
final Object left = node.jjtGetChild(0).jjtAccept(this, data);
final Object right = node.jjtGetChild(1).jjtAccept(this, data);
return operators.endsWith(node, "$=", left, right);
return operators.endsWith(node, JexlOperator.ENDSWITH, left, right);
}

@Override
Expand Down Expand Up @@ -1740,7 +1740,7 @@ protected Object visit(final ASTNESNode node, final Object data) {
protected Object visit(final ASTNEWNode node, final Object data) {
final Object left = node.jjtGetChild(0).jjtAccept(this, data);
final Object right = node.jjtGetChild(1).jjtAccept(this, data);
return !operators.endsWith(node, "$!", left, right);
return operators.endsWith(node, JexlOperator.NOT_ENDSWITH, left, right);
}

@Override
Expand Down Expand Up @@ -1768,14 +1768,14 @@ protected Object visit(final ASTNRNode node, final Object data) {
final Object right = node.jjtGetChild(1).jjtAccept(this, data);
// note the arguments inversion between (not) 'in'/'matches' and (not) 'contains'
// if x not-in y then y not-contains x
return !operators.contains(node, "!~", right, left);
return operators.contains(node, JexlOperator.NOT_CONTAINS, right, left);
}

@Override
protected Object visit(final ASTNSWNode node, final Object data) {
final Object left = node.jjtGetChild(0).jjtAccept(this, data);
final Object right = node.jjtGetChild(1).jjtAccept(this, data);
return !operators.startsWith(node, "^!", left, right);
return operators.startsWith(node, JexlOperator.NOT_STARTSWITH, left, right);
}

@Override
Expand Down Expand Up @@ -2099,7 +2099,7 @@ protected Object visit(final ASTSubNode node, final Object data) {
protected Object visit(final ASTSWNode node, final Object data) {
final Object left = node.jjtGetChild(0).jjtAccept(this, data);
final Object right = node.jjtGetChild(1).jjtAccept(this, data);
return operators.startsWith(node, "^=", left, right);
return operators.startsWith(node, JexlOperator.STARTSWITH, left, right);
}

@Override
Expand Down Expand Up @@ -2244,7 +2244,7 @@ protected Object visit(final ASTTryStatement node, final Object data) {
protected Object visit(final ASTUnaryMinusNode node, final Object data) {
// use cached value if literal
final Object value = node.jjtGetValue();
if (value != null && !(value instanceof JexlMethod)) {
if (value instanceof Number) {
return value;
}
final JexlNode valNode = node.jjtGetChild(0);
Expand Down Expand Up @@ -2273,7 +2273,7 @@ protected Object visit(final ASTUnaryMinusNode node, final Object data) {
protected Object visit(final ASTUnaryPlusNode node, final Object data) {
// use cached value if literal
final Object value = node.jjtGetValue();
if (value != null && !(value instanceof JexlMethod)) {
if (value instanceof Number) {
return value;
}
final JexlNode valNode = node.jjtGetChild(0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,24 +55,6 @@
* @since 3.0
*/
public abstract class InterpreterBase extends ParserVisitor {
/**
* Cached arithmetic function call.
*/
protected static class ArithmeticFuncall extends Funcall {
/**
* Constructs a new instance.
* @param jme the method
* @param flag the narrow flag
*/
protected ArithmeticFuncall(final JexlMethod jme, final boolean flag) {
super(jme, flag);
}

@Override
protected Object tryInvoke(final InterpreterBase ii, final String name, final Object target, final Object[] args) {
return me.tryInvoke(name, ii.arithmetic, ii.functionArguments(target, narrow, args));
}
}
/**
* Helping dispatch function calls.
*/
Expand Down Expand Up @@ -208,6 +190,26 @@ protected Object tryEval(final Object ntarget, final String methodName, final Ob
return JexlEngine.TRY_FAILED;
}
}

/**
* Cached arithmetic function call.
*/
protected static class ArithmeticFuncall extends Funcall {
/**
* Constructs a new instance.
* @param jme the method
* @param flag the narrow flag
*/
protected ArithmeticFuncall(final JexlMethod jme, final boolean flag) {
super(jme, flag);
}

@Override
protected Object tryInvoke(final InterpreterBase ii, final String name, final Object target, final Object[] args) {
return me.tryInvoke(name, ii.arithmetic, ii.functionArguments(target, narrow, args));
}
}

/**
* Cached context function call.
*/
Expand Down Expand Up @@ -274,8 +276,10 @@ protected Object tryInvoke(final InterpreterBase ii, final String name, final Ob
return me.tryInvoke(name, target, ii.functionArguments(null, narrow, args));
}
}

/** Empty parameters for method matching. */
protected static final Object[] EMPTY_PARAMS = {};

/**
* Pretty-prints a failing property value (de)reference.
* <p>Used by calls to unsolvableProperty(...).</p>
Expand All @@ -285,6 +289,7 @@ protected Object tryInvoke(final InterpreterBase ii, final String name, final Ob
protected static String stringifyPropertyValue(final JexlNode node) {
return node != null ? new Debugger().depth(1).data(node) : "???";
}

/** The JEXL engine. */
protected final Engine jexl;
/** The logger. */
Expand Down Expand Up @@ -774,7 +779,7 @@ protected boolean isVariableDefined(final Frame frame, final LexicalScope block,
/**
* Triggered when an operator fails.
* @param node the node where the error originated from
* @param operator the method name
* @param operator the operator symbol
* @param cause the cause of error (if any)
* @return throws JexlException if strict and not silent, null otherwise
*/
Expand Down
Loading

0 comments on commit 2915b37

Please sign in to comment.