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 43f91c1d JEXL-428 : Operators.java minor refactor & lisibility
improvements;
43f91c1d is described below
commit 43f91c1d77902a3f1ae9951b95ce6f6d72d22de7
Author: Henrib <[email protected]>
AuthorDate: Sun Sep 29 10:49:47 2024 +0200
JEXL-428 : Operators.java minor refactor & lisibility improvements;
---
.../apache/commons/jexl3/internal/Operators.java | 178 +++++++++++----------
.../commons/jexl3/ArithmeticOperatorTest.java | 14 +-
2 files changed, 98 insertions(+), 94 deletions(-)
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 84a3077c..8a5bd25d 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Operators.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Operators.java
@@ -36,12 +36,11 @@ import org.apache.commons.jexl3.parser.JexlNode;
* @since 3.0
*/
public final class Operators implements JexlArithmetic.Uberspect {
- /** The uberspect. */
- private final Uberspect uberspect;
- /** The arithmetic instance being analyzed. */
- private final JexlArithmetic arithmetic;
- /** The set of overloaded operators. */
- private final Set<JexlOperator> overloads;
+ private static final String METHOD_IS_EMPTY = "isEmpty";
+ private static final String METHOD_SIZE = "size";
+ private static final String METHOD_CONTAINS = "contains";
+ private static final String METHOD_STARTS_WITH = "startsWith";
+ private static final String METHOD_ENDS_WITH = "endsWith";
/**
* The comparison operators.
@@ -57,6 +56,15 @@ public final class Operators implements
JexlArithmetic.Uberspect {
private static final Set<JexlOperator> POSTFIX_OPS =
EnumSet.of(JexlOperator.GET_AND_INCREMENT,
JexlOperator.GET_AND_DECREMENT);
+ /** The uberspect. */
+ private final Uberspect uberspect;
+ /** The arithmetic instance being analyzed. */
+ private final JexlArithmetic arithmetic;
+ /** The set of overloaded operators. */
+ private final Set<JexlOperator> overloads;
+ /** Caching state: -1 unknown, 0 false, 1 true. */
+ private volatile int caching = -1;
+
/**
* Creates an instance.
* @param theUberspect the uberspect instance
@@ -81,6 +89,23 @@ public final class Operators implements
JexlArithmetic.Uberspect {
return overloads.contains(operator);
}
+ /**
+ * @return whether caching is enabled in the engine
+ */
+ private boolean isCaching() {
+ int c = caching;
+ if (c < 0) {
+ synchronized(this) {
+ c = caching;
+ if (c < 0) {
+ JexlEngine jexl = JexlEngine.getThreadEngine();
+ caching = c = (jexl instanceof Engine) && ((Engine)
jexl).cache != null ? 1 : 0;
+ }
+ }
+ }
+ return c > 0;
+ }
+
/**
* Tidy arguments based on operator arity.
* <p>The interpreter may add a null to the arguments of operator
expecting only one parameter.</p>
@@ -221,12 +246,12 @@ public final class Operators implements
JexlArithmetic.Uberspect {
result = false;
// check if there is an isEmpty method on the object that
returns a
// boolean and if so, just use it
- final JexlMethod vm = uberspect.getMethod(object, "isEmpty",
InterpreterBase.EMPTY_PARAMS);
+ final JexlMethod vm = uberspect.getMethod(object,
METHOD_IS_EMPTY, InterpreterBase.EMPTY_PARAMS);
if (returnsBoolean(vm)) {
try {
result = vm.invoke(object,
InterpreterBase.EMPTY_PARAMS);
} catch (final Exception xany) {
- return operatorError(node, JexlOperator.EMPTY, xany);
+ operatorError(node, JexlOperator.EMPTY, xany);
}
}
}
@@ -255,7 +280,7 @@ public final class Operators implements
JexlArithmetic.Uberspect {
if (result == null) {
// check if there is a size method on the object that returns
an
// integer and if so, just use it
- final JexlMethod vm = uberspect.getMethod(object, "size",
InterpreterBase.EMPTY_PARAMS);
+ final JexlMethod vm = uberspect.getMethod(object, METHOD_SIZE,
InterpreterBase.EMPTY_PARAMS);
if (returnsInteger(vm)) {
try {
result = vm.invoke(object,
InterpreterBase.EMPTY_PARAMS);
@@ -296,7 +321,7 @@ public final class Operators implements
JexlArithmetic.Uberspect {
contained = matched;
} else {
// try a left.contains(right) method
- final Boolean duck = booleanDuckCall("contains", left,
right);
+ final Boolean duck = booleanDuckCall(METHOD_CONTAINS,
left, right);
if (duck != null) {
contained = duck;
} else {
@@ -337,7 +362,7 @@ public final class Operators implements
JexlArithmetic.Uberspect {
starts = matched;
} else {
// try a left.startsWith(right) method
- final Boolean duck = booleanDuckCall("startsWith", left,
right);
+ final Boolean duck = booleanDuckCall(METHOD_STARTS_WITH,
left, right);
if (duck != null) {
starts = duck;
} else {
@@ -378,7 +403,7 @@ public final class Operators implements
JexlArithmetic.Uberspect {
ends = matched;
} else {
// try a left.endsWith(right) method
- final Boolean duck = booleanDuckCall("endsWith", left,
right);
+ final Boolean duck = booleanDuckCall(METHOD_ENDS_WITH,
left, right);
if (duck != null) {
ends = duck;
} else {
@@ -409,90 +434,70 @@ public final class Operators implements
JexlArithmetic.Uberspect {
* the value to use as the side effect argument otherwise
*/
Object tryAssignOverload(final JexlNode node,
- final JexlOperator operator,
- final Consumer<Object> assignFun,
- final Object...args) {
+ final JexlOperator operator,
+ final Consumer<Object> assignFun,
+ final Object... args) {
if (args.length < operator.getArity()) {
return JexlEngine.TRY_FAILED;
}
- Object result;
+ Object result = JexlEngine.TRY_FAILED;
try {
- // try to call overload with side effect; the object is modified
- if (overloads(operator)) {
- result = tryOverload(node, operator, arguments(operator, args));
- if (result != JexlEngine.TRY_FAILED) {
- return result; // 1
+ // attempt assignment operator overload
+ if (overloads(operator)) {
+ result = tryOverload(node, operator, arguments(operator,
args));
+ if (result != JexlEngine.TRY_FAILED) {
+ return result;
+ }
}
- }
- // try to call base overload (ie + for +=)
- final JexlOperator base = operator.getBaseOperator();
- if (base != null && overloads(base)) {
- result = tryOverload(node, base, arguments(base, args));
+ // let's attempt base operator overload
+ final JexlOperator base = operator.getBaseOperator();
+ if (base != null && overloads(base)) {
+ result = tryOverload(node, base, arguments(base, args));
+ }
+ // no overload or overload failed, use base operator
+ if (result == JexlEngine.TRY_FAILED) {
+ result = performBaseOperation(operator, args);
+ }
+ // on success, assign value
if (result != JexlEngine.TRY_FAILED) {
assignFun.accept(result);
- return POSTFIX_OPS.contains(operator) ? args[0] : result; // 2
+ // postfix implies return initial argument value
+ return POSTFIX_OPS.contains(operator) ? args[0] : result;
}
+ } catch (final Exception xany) {
+ operatorError(node, operator, xany);
}
- // default implementation for self-* operators
+ return JexlEngine.TRY_FAILED;
+ }
+
+ /**
+ * Performs the base operation of an assignment.
+ * @param operator the operator
+ * @param args the arguments
+ * @return the result
+ */
+ private Object performBaseOperation(final JexlOperator operator, final
Object... args) {
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 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:
- 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
- }
+ 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());
- }
- assignFun.accept(result);
- return result; // 5
- } catch (final Exception xany) {
- operatorError(node, operator, xany);
}
- return JexlEngine.TRY_FAILED;
}
/**
@@ -510,9 +515,8 @@ public final class Operators implements
JexlArithmetic.Uberspect {
*/
Object tryOverload(final JexlCache.Reference node, final JexlOperator
operator, final Object... args) {
controlNullOperands(arithmetic, operator, args);
- Engine engine = (Engine) JexlEngine.getThreadEngine();
try {
- return tryEval(engine == null || engine.cache != null ? node :
null, operator, args);
+ return tryEval(isCaching() ? node : null, operator, args);
} catch (final Exception xany) {
// ignore return if lenient, will return try_failed
operatorError(node, operator, xany);
@@ -634,8 +638,9 @@ public final class Operators implements
JexlArithmetic.Uberspect {
case LTE: return cmp <= 0;
case GT: return cmp > 0;
case GTE: return cmp >= 0;
+ default:
+ throw new ArithmeticException("unexpected operator " +
operator);
}
- throw new ArithmeticException("unexpected operator " + operator);
}
}
@@ -652,10 +657,7 @@ public final class Operators implements
JexlArithmetic.Uberspect {
@Override
public Object tryInvoke(String name, Object arithmetic, Object...
params) throws JexlException.TryFailed {
Object cmp =
compare.tryInvoke(JexlOperator.COMPARE.getMethodName(), arithmetic, params[1],
params[0]);
- if (cmp instanceof Integer) {
- return operate(-(int) cmp);
- }
- return JexlEngine.TRY_FAILED;
+ return cmp instanceof Integer? operate(-(int) cmp) :
JexlEngine.TRY_FAILED;
}
@Override
diff --git a/src/test/java/org/apache/commons/jexl3/ArithmeticOperatorTest.java
b/src/test/java/org/apache/commons/jexl3/ArithmeticOperatorTest.java
index c42c591b..969e2640 100644
--- a/src/test/java/org/apache/commons/jexl3/ArithmeticOperatorTest.java
+++ b/src/test/java/org/apache/commons/jexl3/ArithmeticOperatorTest.java
@@ -772,6 +772,8 @@ public class ArithmeticOperatorTest extends JexlTestCase {
}
}
+ static final List<Integer> LOOPS = new ArrayList<>(Arrays.asList(0, 1));
+
@Test
void test428() {
// see JEXL-428
@@ -783,12 +785,12 @@ public class ArithmeticOperatorTest extends JexlTestCase {
script = jexl.createScript("x < y", "x", "y");
final JexlScript s0 = script;
assertThrows(JexlException.class, () -> s0.execute(null, 42, rhs));
- assertTrue((boolean) script.execute(null, lhs, rhs));
- assertTrue((boolean) script.execute(null, lhs, rhs));
- assertFalse((boolean) script.execute(null, rhs, lhs));
- assertFalse((boolean) script.execute(null, rhs, lhs));
- assertTrue((boolean) script.execute(null, lhs, rhs));
- assertFalse((boolean) script.execute(null, rhs, lhs));
+ for(int i : LOOPS) { assertTrue((boolean) script.execute(null, lhs,
rhs)); }
+ for(int i : LOOPS) { assertTrue((boolean) script.execute(null, lhs,
rhs)); }
+ for(int i : LOOPS) { assertFalse((boolean) script.execute(null, rhs,
lhs)); }
+ for(int i : LOOPS) { assertFalse((boolean) script.execute(null, rhs,
lhs)); }
+ for(int i : LOOPS) { assertTrue((boolean) script.execute(null, lhs,
rhs)); }
+ for(int i : LOOPS) { assertFalse((boolean) script.execute(null, rhs,
lhs)); }
script = jexl.createScript("x <= y", "x", "y");
final JexlScript s1 = script;