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 <hbies...@gmail.com>
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;

Reply via email to