This is an automated email from the ASF dual-hosted git repository.

henrib pushed a commit to branch JEXL-360
in repository https://gitbox.apache.org/repos/asf/commons-jexl.git

commit 43fdeed59b161f9846b6746e63000be0e7b767a3
Author: henrib <hen...@apache.org>
AuthorDate: Thu Feb 17 18:03:25 2022 +0100

    JEXL-360: try a more refined arithmetic;
    - refactored tests;
---
 .../org/apache/commons/jexl3/Arithmetic360.java    | 221 +++++++++++++++++++++
 .../apache/commons/jexl3/ShiftOperatorsTest.java   | 185 +++++++++++++++++
 .../org/apache/commons/jexl3/junit/Asserter.java   |   4 +-
 3 files changed, 408 insertions(+), 2 deletions(-)

diff --git a/src/test/java/org/apache/commons/jexl3/Arithmetic360.java 
b/src/test/java/org/apache/commons/jexl3/Arithmetic360.java
new file mode 100644
index 0000000..aacde4b
--- /dev/null
+++ b/src/test/java/org/apache/commons/jexl3/Arithmetic360.java
@@ -0,0 +1,221 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.jexl3;
+
+import java.math.BigInteger;
+import java.math.MathContext;
+
+/**
+ * An arithmetic that tries to keep argument types for bit-twiddling operators.
+ */
+public class Arithmetic360 extends JexlArithmetic {
+    public Arithmetic360(boolean astrict) {
+        super(astrict);
+    }
+
+    public Arithmetic360(boolean astrict, MathContext bigdContext, int 
bigdScale) {
+        super(astrict, bigdContext, bigdScale);
+    }
+
+    /**
+     * Given a long, attempt to narrow it to an int.
+     * <p>Narrowing will only occur if no operand is a Long.
+     * @param lhs  the left-hand side operand that lead to the long result
+     * @param rhs  the right-hand side operand that lead to the long result
+     * @param result the long to narrow
+     * @return an Integer if narrowing is possible, the original Long otherwise
+     */
+    protected Number narrowLong(final Object lhs, final Object rhs, final long 
result) {
+        if (!(lhs instanceof Long || rhs instanceof Long)) {
+            final int ir = (int) result;
+            if (result == (long) ir) {
+                return ir;
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Given a long, attempt to narrow it to an int.
+     * <p>Narrowing will only occur if the initial operand is not a Long.
+     * @param operand  the operand that lead to the long result
+     * @param result the long result to narrow
+     * @return an Integer if narrowing is possible, the original Long otherwise
+     */
+    protected Number narrowLong(final Object operand, final long result) {
+        if (!(operand instanceof Long)) {
+            final int ir = (int) result;
+            if (result == (long) ir) {
+                return ir;
+            }
+        }
+        return result;
+    }
+    /**
+     * Checks if value class is a number that can be represented exactly in a 
long.
+     *
+     * @param value  argument
+     * @return true if argument can be represented by a long
+     */
+    protected Number asIntNumber(final Object value) {
+        return value instanceof Integer
+                || value instanceof Short
+                || value instanceof Byte
+                ? (Number) value
+                : null;
+    }
+
+    /**
+     * Casts to Long if possible.
+     * @param value the Long or else
+     * @return the Long or null
+     */
+    protected Long castLongNumber(final Object value) {
+        return value instanceof Long ? (Long) value : null;
+    }
+
+    /**
+     * Performs a bitwise and.
+     *
+     * @param left  the left operand
+     * @param right the right operator
+     * @return left &amp; right
+     */
+    public Object and(final Object left, final Object right) {
+        final Number l = asLongNumber(left);
+        final Number r = asLongNumber(right);
+        if (l != null && r != null) {
+            return narrowLong(left, right, l.longValue() & r.longValue());
+        }
+        return toBigInteger(left).and(toBigInteger(right));
+    }
+
+    /**
+     * Performs a bitwise or.
+     *
+     * @param left  the left operand
+     * @param right the right operator
+     * @return left | right
+     */
+    public Object or(final Object left, final Object right) {
+        final Number l = asLongNumber(left);
+        final Number r = asLongNumber(right);
+        if (l != null && r != null) {
+            return narrowLong(left, right, l.longValue() | r.longValue());
+        }
+        return toBigInteger(left).or(toBigInteger(right));
+    }
+
+    /**
+     * Performs a bitwise xor.
+     *
+     * @param left  the left operand
+     * @param right the right operator
+     * @return left ^ right
+     */
+    public Object xor(final Object left, final Object right) {
+        final Number l = asLongNumber(left);
+        final Number r = asLongNumber(right);
+        if (l != null && r != null) {
+            return narrowLong(left, right, l.longValue() ^ r.longValue());
+        }
+        return toBigInteger(left).xor(toBigInteger(right));
+    }
+
+    /**
+     * Performs a bitwise complement.
+     *
+     * @param val the operand
+     * @return ~val
+     */
+    public Object complement(final Object val) {
+        final long l = toLong(val);
+        return narrowLong(val, ~l);
+    }
+
+    /**
+     * Shifts a bit pattern to the right.
+     *
+     * @param left  left argument
+     * @param right  right argument
+     * @return left &lt;&lt; right.
+     */
+    public Object shiftLeft(Object left, Object right) {
+        if (left == null && right == null) {
+            return controlNullNullOperands();
+        }
+        final int r = toInteger(right);
+        Number l = asIntNumber(left);
+        if (l != null) {
+            return l.intValue() << r;
+        }
+        l = castLongNumber(left);
+        if (l != null) {
+            return l.longValue() << r;
+        }
+        return toBigInteger(left).shiftLeft(r);
+    }
+
+    /**
+     * Shifts a bit pattern to the right.
+     *
+     * @param left  left argument
+     * @param right  right argument
+     * @return left &gt;&gt; right.
+     */
+    public Object shiftRight(Object left, Object right) {
+        if (left == null && right == null) {
+            return controlNullNullOperands();
+        }
+        final int r = toInteger(right);
+        Number l = asIntNumber(left);
+        if (l != null) {
+            return l.intValue() >> r;
+        }
+        l = castLongNumber(left);
+        if (l != null) {
+            return l.longValue() >> r;
+        }
+        return toBigInteger(left).shiftRight(r);
+    }
+
+    /**
+     * Shifts a bit pattern to the right unsigned.
+     *
+     * @param left  left argument
+     * @param right  right argument
+     * @return left &gt;&gt;&gt; right.
+     */
+    public Object shiftRightUnsigned(Object left, Object right) {
+        if (left == null && right == null) {
+            return controlNullNullOperands();
+        }
+        final int r = toInteger(right);
+        Number l = asIntNumber(left);
+        if (l != null) {
+            return l.intValue() >>> r;
+        }
+        l = castLongNumber(left);
+        if (l != null) {
+            return l.longValue() >>> r;
+        }
+        BigInteger bl = toBigInteger(left);
+        return bl.signum() < 0? bl.negate().shiftRight(r) : bl.shiftRight(r);
+    }
+
+}
diff --git a/src/test/java/org/apache/commons/jexl3/ShiftOperatorsTest.java 
b/src/test/java/org/apache/commons/jexl3/ShiftOperatorsTest.java
new file mode 100644
index 0000000..2ee14b9
--- /dev/null
+++ b/src/test/java/org/apache/commons/jexl3/ShiftOperatorsTest.java
@@ -0,0 +1,185 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl3;
+
+import java.math.BigInteger;
+
+import org.apache.commons.jexl3.junit.Asserter;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Tests shift operators.
+ */
+@SuppressWarnings({"UnnecessaryBoxing", 
"AssertEqualsBetweenInconvertibleTypes"})
+public class ShiftOperatorsTest extends JexlTestCase {
+    private Asserter asserter;
+
+    private Asserter a360;
+
+    public ShiftOperatorsTest() {
+        super("ShiftOperatorsTest");
+        asserter = new Asserter(JEXL);
+        asserter.setStrict(false, false);
+
+        JexlEngine j360 = new JexlBuilder().arithmetic(new 
Arithmetic360(true)).strict(true).create();
+        a360 = new Asserter(j360);
+        a360.setStrict(false, false);
+    }
+
+    @Test
+    public void testLeftShiftIntValue() throws Exception {
+        final String expr = "(x, y)->{ x << y }";
+        asserter.assertExpression(expr, 1L << 2, 1L, 2);
+        asserter.assertExpression(expr, 1L << -2, 1L, -2);
+        asserter.assertExpression(expr, -1L << 2, -1L, 2);
+        asserter.assertExpression(expr, -1L << -2, -1L, -2);
+
+        a360.assertExpression(expr, 1L << 2, 1L, 2);
+        a360.assertExpression(expr, 1L << -2, 1L, -2);
+        a360.assertExpression(expr, -1L << 2, -1L, 2);
+        a360.assertExpression(expr, -1L << -2, -1L, -2);
+
+        a360.assertExpression(expr, 1 << 2, 1, 2);
+        a360.assertExpression(expr, 1 << -2, 1, -2);
+        a360.assertExpression(expr, -1 << 2, -1, 2);
+        a360.assertExpression(expr, -1 << -2, -1, -2);
+    }
+
+    @Test
+    public void testRightShiftIntValue() throws Exception {
+        final String expr = "(x, y)->{ x >> y }";
+        asserter.assertExpression(expr, 42L >> 2, 42L, 2);
+        asserter.assertExpression(expr, 42L >> -2, 42L, -2);
+        asserter.assertExpression(expr, -42L >> 2, -42L, 2);
+        asserter.assertExpression(expr, -42L >> -2, -42L, -2);
+
+        a360.assertExpression(expr, 42L >> 2, 42L, 2);
+        a360.assertExpression(expr, 42L >> -2, 42L, -2);
+        a360.assertExpression(expr, -42L >> 2, -42L, 2);
+        a360.assertExpression(expr, -42L >> -2, -42L, -2);
+
+        a360.assertExpression(expr, 42 >> 2, 42, 2);
+        a360.assertExpression(expr, 42 >> -2, 42, -2);
+        a360.assertExpression(expr, -42 >> 2, -42, 2);
+        a360.assertExpression(expr, -42 >> -2, -42, -2);
+    }
+
+    @Test
+    public void testRightShiftUnsignedIntValue() throws Exception {
+        final String expr = "(x, y)->{ x >>> y }";
+        asserter.assertExpression(expr, 42L >>> 2, 42L, 2);
+        asserter.assertExpression(expr, 42L >>> -2, 42L, -2);
+        asserter.assertExpression(expr, -42L >>> 2, -42L, 2);
+        asserter.assertExpression(expr, -42L >>> -2, -42L, -2);
+    }
+
+    @Test
+    public void testLeftShiftLongValue() throws Exception {
+        a360.assertExpression("2147483648 << 2", 2147483648L << 2);
+        a360.assertExpression("2147483648 << -2", 2147483648L << -2);
+        a360.assertExpression("-2147483649 << 2", -2147483649L << 2);
+        a360.assertExpression("-2147483649 << -2", -2147483649L << -2);
+    }
+
+    @Test
+    public void testRightShiftLongValue() throws Exception {
+        a360.assertExpression("8589934592 >> 2", 8589934592L >> 2);
+        a360.assertExpression("8589934592 >> -2", 8589934592L >> -2);
+        a360.assertExpression("-8589934592 >> 2", -8589934592L >> 2);
+        a360.assertExpression("-8589934592 >> -2", -8589934592L >> -2);
+    }
+
+    @Test
+    public void testRightShiftBigValue() throws Exception {
+        a360.assertExpression( "9223372036854775808 >> 2", new 
BigInteger("9223372036854775808").shiftRight(2));
+        a360.assertExpression("9223372036854775808 >> -2", new 
BigInteger("9223372036854775808").shiftRight(-2));
+        a360.assertExpression("-9223372036854775809 >> 2", new 
BigInteger("-9223372036854775809").shiftRight(2));
+        a360.assertExpression("-9223372036854775809 >> -2", new 
BigInteger("-9223372036854775809").shiftRight(-2));
+    }
+
+    static BigInteger shiftRightUnsigned(String bl, int r) {
+        return shiftRightUnsigned(new BigInteger(bl), r);
+    }
+    static BigInteger shiftRightUnsigned(BigInteger bl, int r) {
+        return bl.signum() < 0 ? bl.negate().shiftRight(r) : bl.shiftRight(r);
+    }
+
+    @Test
+    public void testRightShiftUnsignedBigValue() throws Exception {
+        a360.assertExpression( "9223372036854775808 >>> 2", 
shiftRightUnsigned("9223372036854775808", 2));
+        a360.assertExpression("9223372036854775808 >>> -2", 
shiftRightUnsigned("9223372036854775808",-2));
+        a360.assertExpression("-9223372036854775809 >>> 2", 
shiftRightUnsigned("-9223372036854775809", 2));
+        a360.assertExpression("-9223372036854775809 >>> -2", 
shiftRightUnsigned("-9223372036854775809",-2));
+    }
+
+    public static class ShiftArithmetic extends JexlArithmetic {
+        ShiftArithmetic(boolean flag) {
+            super(flag);
+        }
+
+        public Object shiftLeft(StringBuilder c, String value) {
+            c.append(value);
+            return c;
+        }
+
+        public Object shiftRight(String value, StringBuilder c) {
+            c.append(value);
+            return c;
+        }
+
+        public Object shiftRightUnsigned(String value, StringBuilder c) {
+            c.append(value.toLowerCase());
+            return c;
+        }
+    }
+
+    @Test
+    public void testOverloadedShift() throws Exception {
+        JexlEngine jexl = new JexlBuilder().arithmetic(new 
ShiftArithmetic(true)).create();
+        JexlScript e = jexl.createScript("x << 'Left'", "x");
+        StringBuilder x = new StringBuilder("1");
+        Object o = e.execute(null, x);
+        Assert.assertEquals(e.getSourceText(), "1Left", x.toString());
+
+        e = jexl.createScript("'Right' >> x", "x");
+        x = new StringBuilder("1");
+        o = e.execute(null, x);
+        Assert.assertEquals(e.getSourceText(), "1Right", x.toString());
+
+        e = jexl.createScript("'Right' >>> x", "x");
+        x = new StringBuilder("1");
+        o = e.execute(null, x);
+        Assert.assertEquals(e.getSourceText(), "1right", x.toString());
+    }
+
+    @Test
+    public void testPrecedence() throws Exception {
+        a360.assertExpression("40 + 2 << 1 + 1", 40 + 2 << 1 + 1);
+        a360.assertExpression("40 + (2 << 1) + 1", 40 + (2 << 1) + 1);
+        a360.assertExpression("(40 + 2) << (1 + 1)", (40 + 2) << (1 + 1));
+
+        a360.assertExpression("40 + 2L << 1 + 1", 40 + 2L << 1 + 1);
+        a360.assertExpression("40 + (2L << 1) + 1", 40 + (2L << 1) + 1);
+        a360.assertExpression("(40 + 2L) << (1 + 1)", (40 + 2L) << (1 + 1));
+
+        a360.assertExpression("40L + 2 << 1 + 1", 40L + 2L << 1 + 1);
+        a360.assertExpression("40L + (2 << 1) + 1", 40L + (2L << 1) + 1);
+        a360.assertExpression("(40L + 2) << (1 + 1)", (40L + 2L) << (1 + 1));
+    }
+
+}
diff --git a/src/test/java/org/apache/commons/jexl3/junit/Asserter.java 
b/src/test/java/org/apache/commons/jexl3/junit/Asserter.java
index 7530ff4..2aaf29a 100644
--- a/src/test/java/org/apache/commons/jexl3/junit/Asserter.java
+++ b/src/test/java/org/apache/commons/jexl3/junit/Asserter.java
@@ -92,9 +92,9 @@ public class Asserter extends Assert {
      * @throws Exception if the expression could not be evaluationed or an 
assertion
      * fails
      */
-    public void assertExpression(final String expression, final Object 
expected) throws Exception {
+    public void assertExpression(final String expression, final Object 
expected, Object... args) throws Exception {
         final JexlScript exp = engine.createScript(expression);
-        final Object value = exp.execute(context);
+        final Object value = exp.execute(context, args);
         if (expected instanceof BigDecimal) {
             final JexlArithmetic jexla = engine.getArithmetic();
             Assert.assertEquals("expression: " + expression, 0,

Reply via email to