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 73bb932  JEXL-359: refactored OperatorController, tidy code linked to 
strictness, clean-up test;
73bb932 is described below

commit 73bb932e6cf856d4516ec543a3335925cd4d88aa
Author: henrib <[email protected]>
AuthorDate: Mon Feb 14 18:58:46 2022 +0100

    JEXL-359: refactored OperatorController, tidy code linked to strictness, 
clean-up test;
---
 RELEASE-NOTES.txt                                  |   1 +
 .../org/apache/commons/jexl3/JexlArithmetic.java   |  18 ++-
 .../apache/commons/jexl3/internal/Interpreter.java |  40 +++---
 .../commons/jexl3/internal/InterpreterBase.java    |  24 ++--
 .../jexl3/internal/introspection/ClassMap.java     |   2 +-
 .../jexl3/internal/introspection/Permissions.java  |  26 ++--
 .../org/apache/commons/jexl3/parser/JexlNode.java  |  16 +--
 .../commons/jexl3/parser/OperatorController.java   | 146 +++++++++++----------
 .../org/apache/commons/jexl3/ArithmeticTest.java   |  13 ++
 .../org/apache/commons/jexl3/SideEffectTest.java   |   1 -
 10 files changed, 155 insertions(+), 132 deletions(-)

diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index be26679..631cd31 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -38,6 +38,7 @@ allow fine-tuning the scripting integration into any project.
 
 New Features in 3.3:
 ====================
+* JEXL-359:     Allow per-operator arithmetic handling of null arguments
 * JEXL-357:     Configure accessible packages/classes/methods/fields
 
 Bugs Fixed in 3.3:
diff --git a/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java 
b/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java
index 4a8e364..9c3033a 100644
--- a/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java
+++ b/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java
@@ -373,6 +373,22 @@ public class JexlArithmetic {
     }
 
     /**
+     * Checks whether this arithmetic considers a given operator as strict or 
null-safe.
+     * <p>When an operator is strict, it does <em>not</em> accept null 
arguments when the arithmetic is strict.
+     * If null-safe (ie not-strict), the operator does accept null arguments 
even if the arithmetic itself is strict.</p>
+     * <p>The default implementation considers equal/not-equal operators as 
null-safe so one can check for null as in
+     * <code>if (myvar == null) {...}</code>. Note that this operator is used 
for equal and not-equal syntax.</p>
+     * <p>An arithmetic refining its strict behavior handling for more 
operators must declare which by overriding
+     * this method.</p>
+     * @param operator the operator to check for null-argument(s) handling
+     * @return true if operator considers null arguments as errors, false if 
operator has appropriate semantics
+     * for null argument(s)
+     */
+    public boolean isStrict(JexlOperator operator) {
+        return operator == JexlOperator.EQ? false : isStrict();
+    }
+
+    /**
      * The MathContext instance used for +,-,/,*,% operations on big decimals.
      *
      * @return the math context
@@ -746,7 +762,7 @@ public class JexlArithmetic {
                 }
             }
         }
-        return toString(left).concat(toString(right));
+        return (left == null? "" : toString(left)).concat(right == null ? "" : 
toString(right));
     }
 
     /**
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 fbd47a4..3590d9c 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
@@ -264,7 +264,7 @@ public class Interpreter extends InterpreterBase {
             final Object result = operators.tryOverload(node, 
JexlOperator.ADD, left, right);
             return result != JexlEngine.TRY_FAILED ? result : 
arithmetic.add(left, right);
         } catch (final ArithmeticException xrt) {
-            throw new JexlException(node, "+ error", xrt);
+            throw new JexlException(findNullOperand(node, left, right), "+ 
error", xrt);
         }
     }
 
@@ -276,7 +276,7 @@ public class Interpreter extends InterpreterBase {
             final Object result = operators.tryOverload(node, 
JexlOperator.SUBTRACT, left, right);
             return result != JexlEngine.TRY_FAILED ? result : 
arithmetic.subtract(left, right);
         } catch (final ArithmeticException xrt) {
-            throw new JexlException(node, "- error", xrt);
+            throw new JexlException(findNullOperand(node, left, right), "- 
error", xrt);
         }
     }
 
@@ -288,8 +288,7 @@ public class Interpreter extends InterpreterBase {
             final Object result = operators.tryOverload(node, 
JexlOperator.MULTIPLY, left, right);
             return result != JexlEngine.TRY_FAILED ? result : 
arithmetic.multiply(left, right);
         } catch (final ArithmeticException xrt) {
-            final JexlNode xnode = findNullOperand(xrt, node, left, right);
-            throw new JexlException(xnode, "* error", xrt);
+            throw new JexlException(findNullOperand(node, left, right), "* 
error", xrt);
         }
     }
 
@@ -304,8 +303,7 @@ public class Interpreter extends InterpreterBase {
             if (!arithmetic.isStrict()) {
                 return 0.0d;
             }
-            final JexlNode xnode = findNullOperand(xrt, node, left, right);
-            throw new JexlException(xnode, "/ error", xrt);
+            throw new JexlException(findNullOperand(node, left, right), "/ 
error", xrt);
         }
     }
 
@@ -320,8 +318,7 @@ public class Interpreter extends InterpreterBase {
             if (!arithmetic.isStrict()) {
                 return 0.0d;
             }
-            final JexlNode xnode = findNullOperand(xrt, node, left, right);
-            throw new JexlException(xnode, "% error", xrt);
+            throw new JexlException(findNullOperand(node, left, right), "% 
error", xrt);
         }
     }
 
@@ -333,7 +330,7 @@ public class Interpreter extends InterpreterBase {
             final Object result = operators.tryOverload(node, 
JexlOperator.AND, left, right);
             return result != JexlEngine.TRY_FAILED ? result : 
arithmetic.and(left, right);
         } catch (final ArithmeticException xrt) {
-            throw new JexlException(node, "& error", xrt);
+            throw new JexlException(findNullOperand(node, left, right), "& 
error", xrt);
         }
     }
 
@@ -341,11 +338,14 @@ public class Interpreter extends InterpreterBase {
     protected Object visit(final ASTBitwiseOrNode node, final Object data) {
         final Object left = node.jjtGetChild(0).jjtAccept(this, data);
         final Object right = node.jjtGetChild(1).jjtAccept(this, data);
+        if (arithmetic.isStrict(JexlOperator.OR) && left == null || right == 
null) {
+            // boum
+        }
         try {
             final Object result = operators.tryOverload(node, JexlOperator.OR, 
left, right);
             return result != JexlEngine.TRY_FAILED ? result : 
arithmetic.or(left, right);
         } catch (final ArithmeticException xrt) {
-            throw new JexlException(node, "| error", xrt);
+            throw new JexlException(findNullOperand(node, left, right), "| 
error", xrt);
         }
     }
 
@@ -357,7 +357,7 @@ public class Interpreter extends InterpreterBase {
             final Object result = operators.tryOverload(node, 
JexlOperator.XOR, left, right);
             return result != JexlEngine.TRY_FAILED ? result : 
arithmetic.xor(left, right);
         } catch (final ArithmeticException xrt) {
-            throw new JexlException(node, "^ error", xrt);
+            throw new JexlException(findNullOperand(node, left, right), "^ 
error", xrt);
         }
     }
 
@@ -369,7 +369,7 @@ public class Interpreter extends InterpreterBase {
             final Object result = operators.tryOverload(node, JexlOperator.EQ, 
left, right);
             return result != JexlEngine.TRY_FAILED ? result : 
arithmetic.equals(left, right);
         } catch (final ArithmeticException xrt) {
-            throw new JexlException(node, "== error", xrt);
+            throw new JexlException(findNullOperand(node, left, right), "== 
error", xrt);
         }
     }
 
@@ -383,8 +383,7 @@ public class Interpreter extends InterpreterBase {
                    ? !arithmetic.toBoolean(result)
                    : !arithmetic.equals(left, right);
         } catch (final ArithmeticException xrt) {
-            final JexlNode xnode = findNullOperand(xrt, node, left, right);
-            throw new JexlException(xnode, "!= error", xrt);
+            throw new JexlException(findNullOperand(node, left, right), "!= 
error", xrt);
         }
     }
 
@@ -398,7 +397,7 @@ public class Interpreter extends InterpreterBase {
                    ? result
                    : arithmetic.greaterThanOrEqual(left, right);
         } catch (final ArithmeticException xrt) {
-            throw new JexlException(node, ">= error", xrt);
+            throw new JexlException(findNullOperand(node, left, right), ">= 
error", xrt);
         }
     }
 
@@ -412,7 +411,7 @@ public class Interpreter extends InterpreterBase {
                    ? result
                    : arithmetic.greaterThan(left, right);
         } catch (final ArithmeticException xrt) {
-            throw new JexlException(node, "> error", xrt);
+            throw new JexlException(findNullOperand(node, left, right), "> 
error", xrt);
         }
     }
 
@@ -426,7 +425,7 @@ public class Interpreter extends InterpreterBase {
                    ? result
                    : arithmetic.lessThanOrEqual(left, right);
         } catch (final ArithmeticException xrt) {
-            throw new JexlException(node, "<= error", xrt);
+            throw new JexlException(findNullOperand(node, left, right), "<= 
error", xrt);
         }
     }
 
@@ -440,7 +439,7 @@ public class Interpreter extends InterpreterBase {
                    ? result
                    : arithmetic.lessThan(left, right);
         } catch (final ArithmeticException xrt) {
-            throw new JexlException(node, "< error", xrt);
+            throw new JexlException(findNullOperand(node, left, right), "< 
error", xrt);
         }
     }
 
@@ -493,8 +492,7 @@ public class Interpreter extends InterpreterBase {
         try {
             return arithmetic.createRange(left, right);
         } catch (final ArithmeticException xrt) {
-            final JexlNode xnode = findNullOperand(xrt, node, left, right);
-            throw new JexlException(xnode, ".. error", xrt);
+            throw new JexlException(findNullOperand(node, left, right), ".. 
error", xrt);
         }
     }
 
@@ -1231,7 +1229,7 @@ public class Interpreter extends InterpreterBase {
                 final String aname = ant != null ? ant.toString() : "?";
                 final boolean defined = isVariableDefined(frame, block, aname);
                 // defined but null; arg of a strict operator?
-                if (defined && (!arithmetic.isStrict() || 
!node.jjtGetParent().isStrictOperator())) {
+                if (defined && 
!node.jjtGetParent().isStrictOperator(arithmetic)) {
                     return null;
                 }
                 return unsolvableVariable(node, aname, !defined);
diff --git 
a/src/main/java/org/apache/commons/jexl3/internal/InterpreterBase.java 
b/src/main/java/org/apache/commons/jexl3/internal/InterpreterBase.java
index 0e759f3..6b2d7dc 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/InterpreterBase.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/InterpreterBase.java
@@ -309,7 +309,7 @@ public abstract class InterpreterBase extends ParserVisitor 
{
             // not out of scope with no lexical shade ?
             if (value != Scope.UNDEFINED) {
                 // null argument of an arithmetic operator ?
-                if (value == null && arithmetic.isStrict() && 
identifier.jjtGetParent().isStrictOperator()) {
+                if (value == null && 
identifier.jjtGetParent().isStrictOperator(arithmetic)) {
                     return unsolvableVariable(identifier, name, false); // 
defined but null
                 }
                 return value;
@@ -328,7 +328,7 @@ public abstract class InterpreterBase extends ParserVisitor 
{
                 if (!ignore) {
                     return undefinedVariable(identifier, name); // undefined
                 }
-            } else if (arithmetic.isStrict() && 
identifier.jjtGetParent().isStrictOperator()) {
+            } else if (identifier.jjtGetParent().isStrictOperator(arithmetic)) 
{
                 return unsolvableVariable(identifier, name, false); // defined 
but null
             }
         }
@@ -385,22 +385,24 @@ public abstract class InterpreterBase extends 
ParserVisitor {
         return options.isCancellable();
     }
 
+    @Deprecated
+    protected JexlNode findNullOperand(final RuntimeException xrt, final 
JexlNode node, final Object left, final Object right) {
+        return findNullOperand(node, left, right);
+    }
+
     /**
      * Finds the node causing a NPE for diadic operators.
-     * @param xrt   the RuntimeException
      * @param node  the parent node
      * @param left  the left argument
      * @param right the right argument
      * @return the left, right or parent node
      */
-    protected JexlNode findNullOperand(final RuntimeException xrt, final 
JexlNode node, final Object left, final Object right) {
-        if (xrt instanceof JexlArithmetic.NullOperand) {
-            if (left == null) {
-                return node.jjtGetChild(0);
-            }
-            if (right == null) {
-                return node.jjtGetChild(1);
-            }
+    protected JexlNode findNullOperand(final JexlNode node, final Object left, 
final Object right) {
+        if (left == null) {
+            return node.jjtGetChild(0);
+        }
+        if (right == null) {
+            return node.jjtGetChild(1);
         }
         return node;
     }
diff --git 
a/src/main/java/org/apache/commons/jexl3/internal/introspection/ClassMap.java 
b/src/main/java/org/apache/commons/jexl3/internal/introspection/ClassMap.java
index 770342b..7e36ce9 100644
--- 
a/src/main/java/org/apache/commons/jexl3/internal/introspection/ClassMap.java
+++ 
b/src/main/java/org/apache/commons/jexl3/internal/introspection/ClassMap.java
@@ -62,7 +62,7 @@ final class ClassMap {
     /**
      * The cache miss marker method.
      */
-    private static final Method CACHE_MISS = cacheMiss();
+    static final Method CACHE_MISS = cacheMiss();
     /**
      * This is the cache to store and look up the method information.
      * <p>
diff --git 
a/src/main/java/org/apache/commons/jexl3/internal/introspection/Permissions.java
 
b/src/main/java/org/apache/commons/jexl3/internal/introspection/Permissions.java
index a2cebdf..15f897f 100644
--- 
a/src/main/java/org/apache/commons/jexl3/internal/introspection/Permissions.java
+++ 
b/src/main/java/org/apache/commons/jexl3/internal/introspection/Permissions.java
@@ -111,7 +111,7 @@ public class Permissions implements JexlPermissions {
      * @param clazz the clazz
      * @return the clazz key
      */
-    private static String classKey(final Class<?> clazz) {
+    static String classKey(final Class<?> clazz) {
         return classKey(clazz, null);
     }
 
@@ -122,7 +122,7 @@ public class Permissions implements JexlPermissions {
      * @param strb the buffer to compose the key
      * @return the clazz key
      */
-    private static String classKey(final Class<?> clazz, final StringBuilder 
strb) {
+    static String classKey(final Class<?> clazz, final StringBuilder strb) {
         StringBuilder keyb = strb;
         Class<?> outer = clazz.getEnclosingClass();
         if (outer != null) {
@@ -175,50 +175,44 @@ public class Permissions implements JexlPermissions {
 
     /** Marker for whole NoJexl class. */
     static final NoJexlClass NOJEXL_CLASS = new 
NoJexlClass(Collections.emptySet(), Collections.emptySet()) {
-        @Override
-        boolean deny(Field field) {
+        @Override boolean deny(Field field) {
             return true;
         }
 
-        @Override
-        boolean deny(Method method) {
+        @Override boolean deny(Method method) {
             return true;
         }
 
-        @Override
-        boolean deny(Constructor<?> method) {
+        @Override boolean deny(Constructor<?> method) {
             return true;
         }
     };
 
     /** Marker for allowed class. */
     static final NoJexlClass JEXL_CLASS = new 
NoJexlClass(Collections.emptySet(), Collections.emptySet()) {
-        @Override
-        boolean deny(Field field) {
+        @Override boolean deny(Field field) {
             return false;
         }
 
-        @Override
-        boolean deny(Method method) {
+        @Override  boolean deny(Method method) {
             return false;
         }
 
-        @Override
-        boolean deny(Constructor<?> method) {
+        @Override boolean deny(Constructor<?> method) {
             return false;
         }
     };
 
     /** Marker for @NoJexl package. */
     static final NoJexlPackage NOJEXL_PACKAGE = new 
NoJexlPackage(Collections.emptyMap()) {
-        NoJexlClass getNoJexl(Class<?> clazz) {
+        @Override NoJexlClass getNoJexl(Class<?> clazz) {
             return NOJEXL_CLASS;
         }
     };
 
     /** Marker for fully allowed package. */
     static final NoJexlPackage JEXL_PACKAGE = new 
NoJexlPackage(Collections.emptyMap()) {
-        NoJexlClass getNoJexl(Class<?> clazz) {
+        @Override NoJexlClass getNoJexl(Class<?> clazz) {
             return JEXL_CLASS;
         }
     };
diff --git a/src/main/java/org/apache/commons/jexl3/parser/JexlNode.java 
b/src/main/java/org/apache/commons/jexl3/parser/JexlNode.java
index 0922712..9240a82 100644
--- a/src/main/java/org/apache/commons/jexl3/parser/JexlNode.java
+++ b/src/main/java/org/apache/commons/jexl3/parser/JexlNode.java
@@ -16,6 +16,7 @@
  */
 package org.apache.commons.jexl3.parser;
 
+import org.apache.commons.jexl3.JexlArithmetic;
 import org.apache.commons.jexl3.JexlException;
 import org.apache.commons.jexl3.JexlInfo;
 import org.apache.commons.jexl3.internal.ScriptVisitor;
@@ -116,23 +117,14 @@ public abstract class JexlNode extends SimpleNode {
     }
 
     /**
-     * Checks whether this node is an operator.
-     *
-     * @return true if node is an operator node, false otherwise
-     */
-    public boolean isOperator() {
-        return OperatorController.INSTANCE.control(this, Boolean.TRUE);
-    }
-
-    /**
      * Checks whether this node is an operator that accepts a null argument
      * even when arithmetic is in strict mode.
-     * The sole cases are equals and not equals.
+     * The default cases are equals and not equals.
      *
      * @return true if node accepts null arguments, false otherwise
      */
-    public boolean isStrictOperator() {
-        return OperatorController.INSTANCE.control(this, Boolean.FALSE);
+    public boolean isStrictOperator(JexlArithmetic arithmetic) {
+        return OperatorController.INSTANCE.isStrict(arithmetic, this);
     }
 
     /**
diff --git 
a/src/main/java/org/apache/commons/jexl3/parser/OperatorController.java 
b/src/main/java/org/apache/commons/jexl3/parser/OperatorController.java
index 90d0f3c..ffe7017 100644
--- a/src/main/java/org/apache/commons/jexl3/parser/OperatorController.java
+++ b/src/main/java/org/apache/commons/jexl3/parser/OperatorController.java
@@ -16,7 +16,8 @@
  */
 package org.apache.commons.jexl3.parser;
 
-import org.apache.commons.jexl3.JexlException;
+import org.apache.commons.jexl3.JexlArithmetic;
+import org.apache.commons.jexl3.JexlOperator;
 import org.apache.commons.jexl3.internal.ScriptVisitor;
 
 /**
@@ -24,168 +25,175 @@ import org.apache.commons.jexl3.internal.ScriptVisitor;
  **/
 class OperatorController extends ScriptVisitor {
     static final OperatorController INSTANCE  = new OperatorController();
+
     /**
-     * Controls the operator.
-     * @param node the node
-     * @param safe whether we are checking for any or only null-unsafe 
operators
-     * @return true if node is (null-unsafe) operator
+     * Checks whether an operator is strict for a given arithmetic.
+     * @param node the node which should delegate to an operator
+     * @return true if node points to a (null-unsafe) operator
      */
-    boolean control(final JexlNode node, Boolean safe) {
-        return Boolean.TRUE.equals(node.jjtAccept(this, safe));
+    boolean isStrict(JexlArithmetic arithmetic, final JexlNode node) {
+        if (arithmetic.isStrict()) {
+            Object ctl = node.jjtAccept(this, arithmetic);
+            if (ctl instanceof JexlOperator) {
+                JexlOperator operator = (JexlOperator) ctl;
+                return arithmetic.isStrict(operator);
+            }
+        }
+        return false;
     }
 
     @Override
-    protected Object visitNode(final JexlNode node, final Object data) {
-        return false;
+    protected JexlOperator visitNode(final JexlNode node, final Object data) {
+        return null;
     }
 
     @Override
-    protected Object visit(final ASTNotNode node, final Object data) {
-        return true;
+    protected JexlOperator visit(final ASTNotNode node, final Object data) {
+        return JexlOperator.NOT;
     }
 
     @Override
-    protected Object visit(final ASTAddNode node, final Object data) {
-        return true;
+    protected JexlOperator visit(final ASTAddNode node, final Object data) {
+        return JexlOperator.ADD;
     }
 
     @Override
-    protected Object visit(final ASTSetAddNode node, final Object data) {
-        return true;
+    protected JexlOperator visit(final ASTSetAddNode node, final Object data) {
+        return JexlOperator.SELF_ADD;
     }
 
     @Override
-    protected Object visit(final ASTMulNode node, final Object data) {
-        return true;
+    protected JexlOperator visit(final ASTMulNode node, final Object data) {
+        return JexlOperator.MULTIPLY;
     }
 
     @Override
-    protected Object visit(final ASTSetMultNode node, final Object data) {
-        return true;
+    protected JexlOperator visit(final ASTSetMultNode node, final Object data) 
{
+        return JexlOperator.SELF_MULTIPLY;
     }
 
     @Override
-    protected Object visit(final ASTModNode node, final Object data) {
-        return true;
+    protected JexlOperator visit(final ASTModNode node, final Object data) {
+        return JexlOperator.MOD;
     }
 
     @Override
-    protected Object visit(final ASTSetModNode node, final Object data) {
-        return true;
+    protected JexlOperator visit(final ASTSetModNode node, final Object data) {
+        return JexlOperator.SELF_MOD;
     }
 
     @Override
-    protected Object visit(final ASTDivNode node, final Object data) {
-        return true;
+    protected JexlOperator visit(final ASTDivNode node, final Object data) {
+        return JexlOperator.DIVIDE;
     }
 
     @Override
-    protected Object visit(final ASTSetDivNode node, final Object data) {
-        return true;
+    protected JexlOperator visit(final ASTSetDivNode node, final Object data) {
+        return JexlOperator.SELF_DIVIDE;
     }
 
     @Override
-    protected Object visit(final ASTBitwiseAndNode node, final Object data) {
-        return true;
+    protected JexlOperator visit(final ASTBitwiseAndNode node, final Object 
data) {
+        return JexlOperator.AND;
     }
 
     @Override
-    protected Object visit(final ASTSetAndNode node, final Object data) {
-        return true;
+    protected JexlOperator visit(final ASTSetAndNode node, final Object data) {
+        return JexlOperator.SELF_AND;
     }
 
     @Override
-    protected Object visit(final ASTBitwiseOrNode node, final Object data) {
-        return true;
+    protected JexlOperator visit(final ASTBitwiseOrNode node, final Object 
data) {
+        return JexlOperator.OR;
     }
 
     @Override
-    protected Object visit(final ASTSetOrNode node, final Object data) {
-        return true;
+    protected JexlOperator visit(final ASTSetOrNode node, final Object data) {
+        return JexlOperator.SELF_OR;
     }
 
     @Override
-    protected Object visit(final ASTBitwiseXorNode node, final Object data) {
-        return true;
+    protected JexlOperator visit(final ASTBitwiseXorNode node, final Object 
data) {
+        return JexlOperator.XOR;
     }
 
     @Override
-    protected Object visit(final ASTSetXorNode node, final Object data) {
-        return true;
+    protected JexlOperator visit(final ASTSetXorNode node, final Object data) {
+        return JexlOperator.SELF_OR;
     }
 
     @Override
-    protected Object visit(final ASTBitwiseComplNode node, final Object data) {
-        return true;
+    protected JexlOperator visit(final ASTBitwiseComplNode node, final Object 
data) {
+        return JexlOperator.COMPLEMENT;
     }
 
     @Override
-    protected Object visit(final ASTSubNode node, final Object data) {
-        return true;
+    protected JexlOperator visit(final ASTSubNode node, final Object data) {
+        return JexlOperator.SUBTRACT;
     }
 
     @Override
-    protected Object visit(final ASTSetSubNode node, final Object data) {
-        return true;
+    protected JexlOperator visit(final ASTSetSubNode node, final Object data) {
+        return JexlOperator.SELF_SUBTRACT;
     }
 
     @Override
-    protected Object visit(final ASTEQNode node, final Object data) {
-        return data;
+    protected JexlOperator visit(final ASTEQNode node, final Object data) {
+        return JexlOperator.EQ;
     }
 
     @Override
-    protected Object visit(final ASTNENode node, final Object data) {
-        return data;
+    protected JexlOperator visit(final ASTNENode node, final Object data) {
+        return JexlOperator.EQ;
     }
 
     @Override
-    protected Object visit(final ASTGTNode node, final Object data) {
-        return true;
+    protected JexlOperator visit(final ASTGTNode node, final Object data) {
+        return JexlOperator.GT;
     }
 
     @Override
-    protected Object visit(final ASTGENode node, final Object data) {
-        return true;
+    protected JexlOperator visit(final ASTGENode node, final Object data) {
+        return JexlOperator.GTE;
     }
 
     @Override
-    protected Object visit(final ASTLTNode node, final Object data) {
-        return true;
+    protected JexlOperator visit(final ASTLTNode node, final Object data) {
+        return JexlOperator.LT;
     }
 
     @Override
-    protected Object visit(final ASTLENode node, final Object data) {
-        return true;
+    protected JexlOperator visit(final ASTLENode node, final Object data) {
+        return JexlOperator.LTE;
     }
 
     @Override
-    protected Object visit(final ASTSWNode node, final Object data) {
-        return true;
+    protected JexlOperator visit(final ASTSWNode node, final Object data) {
+        return JexlOperator.STARTSWITH;
     }
 
     @Override
-    protected Object visit(final ASTNSWNode node, final Object data) {
-        return true;
+    protected JexlOperator visit(final ASTNSWNode node, final Object data) {
+        return JexlOperator.STARTSWITH;
     }
 
     @Override
-    protected Object visit(final ASTEWNode node, final Object data) {
-        return true;
+    protected JexlOperator visit(final ASTEWNode node, final Object data) {
+        return JexlOperator.ENDSWITH;
     }
 
     @Override
-    protected Object visit(final ASTNEWNode node, final Object data) {
-        return true;
+    protected JexlOperator visit(final ASTNEWNode node, final Object data) {
+        return JexlOperator.ENDSWITH;
     }
 
     @Override
-    protected Object visit(final ASTERNode node, final Object data) {
-        return true;
+    protected JexlOperator visit(final ASTERNode node, final Object data) {
+        return JexlOperator.CONTAINS;
     }
 
     @Override
-    protected Object visit(final ASTNRNode node, final Object data) {
-        return true;
+    protected JexlOperator visit(final ASTNRNode node, final Object data) {
+        return JexlOperator.CONTAINS;
     }
 }
diff --git a/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java 
b/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java
index 3cc31b6..91f704c 100644
--- a/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java
+++ b/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java
@@ -777,6 +777,19 @@ public class ArithmeticTest extends JexlTestCase {
         script = jexl.createScript("'1.2' + '1.2' ");
         result = script.execute(null);
         Assert.assertEquals("1.21.2", result);
+
+    }
+
+    @Test
+    public void testNullArgs() throws Exception {
+        JexlEngine jexl =  new JexlBuilder().arithmetic(new 
JexlArithmetic(true) {
+            @Override public boolean isStrict(JexlOperator op) {
+                return JexlOperator.ADD == op? false: super.isStrict(op);
+            }
+        }).create();
+        JexlScript script = jexl.createScript("'1.2' + x ", "x");
+        Object result = script.execute(null, null);
+        Assert.assertEquals("1.2", result);
     }
 
     @Test
diff --git a/src/test/java/org/apache/commons/jexl3/SideEffectTest.java 
b/src/test/java/org/apache/commons/jexl3/SideEffectTest.java
index adbb053..c258b7d 100644
--- a/src/test/java/org/apache/commons/jexl3/SideEffectTest.java
+++ b/src/test/java/org/apache/commons/jexl3/SideEffectTest.java
@@ -592,7 +592,6 @@ public class SideEffectTest extends JexlTestCase {
         Assert.assertSame(zz, z);
         Assert.assertEquals(1, z.size());
         z.clear();
-        ctx.clear();
 
         boolean t246 = false;
         // call with null

Reply via email to