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 fa3e3db7 JEXL-342: extend arithmetic to dereference references (sic);
fa3e3db7 is described below

commit fa3e3db789e85beb7bafcfa882af43a5ae5026c8
Author: henrib <hen...@apache.org>
AuthorDate: Fri May 6 00:22:22 2022 +0200

    JEXL-342: extend arithmetic to dereference references (sic);
---
 .../apache/commons/jexl3/internal/Interpreter.java |  17 +-
 .../commons/jexl3/jexl342/OptionalArithmetic.java  | 231 +++++++++++++++++++++
 .../apache/commons/jexl3/jexl342/OptionalTest.java |  52 +++++
 3 files changed, 293 insertions(+), 7 deletions(-)

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 469e184d..b3c1ed84 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
@@ -114,7 +114,7 @@ public class Interpreter extends InterpreterBase {
                 throw new JexlException.StackOverflow(node.jexlInfo(), "jexl 
(" + jexl.stackOverflow + ")", null);
             }
             cancelCheck(node);
-            return node.jjtAccept(this, null);
+            return arithmetic.controlReturn(node.jjtAccept(this, null));
         } catch(final StackOverflowError xstack) {
             final JexlException xjexl = new 
JexlException.StackOverflow(node.jexlInfo(), "jvm", xstack);
             if (!isSilent()) {
@@ -139,13 +139,16 @@ public class Interpreter extends InterpreterBase {
                 logger.warn(xjexl.getMessage(), xjexl.getCause());
             }
         } finally {
-            synchronized(this) {
-                if (functors != null) {
-                    for (final Object functor : functors.values()) {
-                        closeIfSupported(functor);
+            // clean functors at top level
+            if (fp == 0) {
+                synchronized (this) {
+                    if (functors != null) {
+                        for (final Object functor : functors.values()) {
+                            closeIfSupported(functor);
+                        }
+                        functors.clear();
+                        functors = null;
                     }
-                    functors.clear();
-                    functors = null;
                 }
             }
             jexl.putThreadEngine(tjexl);
diff --git 
a/src/test/java/org/apache/commons/jexl3/jexl342/OptionalArithmetic.java 
b/src/test/java/org/apache/commons/jexl3/jexl342/OptionalArithmetic.java
new file mode 100644
index 00000000..dcb11914
--- /dev/null
+++ b/src/test/java/org/apache/commons/jexl3/jexl342/OptionalArithmetic.java
@@ -0,0 +1,231 @@
+/*
+ * 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.jexl342;
+
+import org.apache.commons.jexl3.JexlArithmetic;
+
+import java.lang.ref.Reference;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Unwraps Optional/Reference/AtomicReference on public and key methods.
+ */
+public class OptionalArithmetic extends JexlArithmetic {
+    public OptionalArithmetic(boolean astrict) {
+        super(astrict);
+    }
+
+    /**
+     * Dereferences an Optional, a Reference or an AtomicReference, leave 
other as is.
+     * @param ref the reference
+     * @return the referenced object
+     */
+    protected Object star(Object ref) {
+        if (ref instanceof Optional<?>) {
+            Optional<?> o = (Optional<?>) ref;
+            return o.orElse(null);
+        }
+        if (ref instanceof Reference<?>) {
+            Optional<?> r = (Optional<?>) ref;
+            return r.get();
+        }
+        if (ref instanceof AtomicReference<?>) {
+            AtomicReference<?> r = (AtomicReference<?>) ref;
+            return r.get();
+        }
+        return ref;
+    }
+
+    @Override
+    public Object controlReturn(Object returned) {
+        return star(returned);
+    }
+
+    @Override
+    public boolean toBoolean(final Object val) {
+        return super.toBoolean(star(val));
+    }
+
+    @Override
+    public String toString(final Object val) {
+        return super.toString(star(val));
+    }
+
+    @Override
+    public int toInteger(final Object val) {
+        return super.toInteger(star(val));
+    }
+
+    @Override
+    public long toLong(final Object val) {
+        return super.toLong(star(val));
+    }
+
+    @Override
+    public double toDouble(final Object val) {
+        return super.toDouble(star(val));
+    }
+
+    @Override
+    public BigInteger toBigInteger(final Object val) {
+        return super.toBigInteger(star(val));
+    }
+
+    @Override
+    public BigDecimal toBigDecimal(final Object val) {
+        return super.toBigDecimal(star(val));
+    }
+
+    @Override
+    public Integer size(final Object object, final Integer def) {
+        return super.size(star(object), def);
+    }
+
+    @Override
+    public Boolean empty(Object o) {
+        return super.empty(star(o));
+    }
+
+    @Override
+    public Boolean isEmpty(final Object object, final Boolean def) {
+        return super.isEmpty(star(object), def);
+    }
+
+    @Override
+    public Object positivize(Object o) {
+        return super.positivize(star(o));
+    }
+
+    @Override
+    public Object negate(Object o) {
+        return super.negate(star(o));
+    }
+
+    @Override
+    public Object complement(Object o) {
+        return super.complement(star(o));
+    }
+
+    @Override
+    public Boolean contains(Object lhs, Object rhs) {
+        return super.contains(star(lhs), star(rhs));
+    }
+
+    @Override
+    public Object add(Object lhs, Object rhs) {
+        return super.add(star(lhs), star(rhs));
+    }
+
+    @Override
+    public Object subtract(Object lhs, Object rhs) {
+        return super.subtract(star(lhs), star(rhs));
+    }
+
+    @Override
+    public Object multiply(Object lhs, Object rhs) {
+        return super.multiply(star(lhs), star(rhs));
+    }
+
+    @Override
+    public Object divide(Object lhs, Object rhs) {
+        return super.divide(star(lhs), star(rhs));
+    }
+
+    @Override
+    public Object mod(Object lhs, Object rhs) {
+        return super.mod(star(lhs), star(rhs));
+    }
+
+    @Override
+    public Object and(final Object left, final Object right) { return 
super.and(star(left), star(right)); }
+
+    @Override
+    public Object or(final Object left, final Object right) { return 
super.or(star(left), star(right)); }
+
+    @Override
+    public Object xor(final Object left, final Object right) { return 
super.xor(star(left), star(right)); }
+
+    @Override
+    public Object shiftLeft(final Object left, final Object right) { return 
super.shiftLeft(star(left), star(right)); }
+
+    @Override
+    public Object shiftRight(final Object left, final Object right) { return 
super.shiftRight(star(left), star(right)); }
+
+    @Override
+    public Object shiftRightUnsigned(final Object left, final Object right) { 
return super.shiftRightUnsigned(star(left), star(right)); }
+
+    @Override
+    public Boolean startsWith(final Object left, final Object right) {
+        return super.startsWith(star(left), star(right));
+    }
+
+    @Override
+    public Boolean endsWith(final Object left, final Object right) {
+        return super.endsWith(star(left), star(right));
+    }
+
+    @Override
+    public boolean equals(final Object left, final Object right) {
+        return equals(star(left), star(right));
+    }
+
+    @Override
+    public boolean greaterThan(final Object left, final Object right) {
+        return greaterThan(star(left), star(right));
+    }
+
+    @Override
+    public boolean greaterThanOrEqual(final Object left, final Object right) {
+        return greaterThanOrEqual(star(left), star(right));
+    }
+
+    @Override
+    public boolean lessThan(final Object left, final Object right) {
+        return lessThan(star(left), star(right));
+    }
+
+    @Override
+    public boolean lessThanOrEqual(final Object left, final Object right) {
+        return lessThanOrEqual(star(left), star(right));
+    }
+
+    @Override
+    public boolean narrowArguments(final Object[] args) {
+        boolean narrowed = false;
+        if (args != null) {
+            for (int a = 0; a < args.length; ++a) {
+                final Object arg = args[a];
+                Object sarg = star(arg);
+                if (sarg != arg) {
+                    narrowed = true;
+                }
+                if (arg instanceof Number) {
+                    final Number narg = (Number) arg;
+                    final Number narrow = narrow(narg);
+                    if (!narg.equals(narrow)) {
+                        args[a] = narrow;
+                        narrowed = true;
+                    }
+                }
+            }
+        }
+        return narrowed;
+    }
+}
diff --git a/src/test/java/org/apache/commons/jexl3/jexl342/OptionalTest.java 
b/src/test/java/org/apache/commons/jexl3/jexl342/OptionalTest.java
index ea28e115..068cce98 100644
--- a/src/test/java/org/apache/commons/jexl3/jexl342/OptionalTest.java
+++ b/src/test/java/org/apache/commons/jexl3/jexl342/OptionalTest.java
@@ -16,18 +16,26 @@
  */
 package org.apache.commons.jexl3.jexl342;
 
+import org.apache.commons.jexl3.JexlArithmetic;
 import org.apache.commons.jexl3.JexlBuilder;
+import org.apache.commons.jexl3.JexlContext;
 import org.apache.commons.jexl3.JexlEngine;
 import org.apache.commons.jexl3.JexlException;
+import org.apache.commons.jexl3.JexlFeatures;
 import org.apache.commons.jexl3.JexlInfo;
 import org.apache.commons.jexl3.JexlScript;
+import org.apache.commons.jexl3.MapContext;
 import org.apache.commons.jexl3.introspection.JexlUberspect;
 import org.junit.Assert;
 import org.junit.Test;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Optional;
+import java.util.stream.Stream;
 
 public class OptionalTest {
 
@@ -45,6 +53,50 @@ public class OptionalTest {
         }
     }
 
+    public static class StreamContext extends MapContext {
+        public Stream map(Collection<Object> c, JexlScript s) {
+            JexlContext context = JexlEngine.getThreadContext();
+            return c.stream().map(a->s.execute(context, a));
+        }
+        public Object reduce(Stream<Object> stream, JexlScript script) {
+            return stream.reduce((identity, element)->{
+                JexlContext context = JexlEngine.getThreadContext();
+                return script.execute(context, identity, element);
+            });
+        }
+    }
+
+    @Test
+    public void testStream() {
+        String src = "[1, 2, 3, ...].map(x -> x * x).reduce((acc, x)->acc + 
x)";
+        JexlBuilder builder = new JexlBuilder();
+        JexlUberspect uber = builder.create().getUberspect();
+        JexlArithmetic jexla = new OptionalArithmetic(true);
+        JexlEngine jexl = builder.uberspect(new 
ReferenceUberspect(uber)).arithmetic(jexla).safe(false).create();
+        JexlInfo info = new JexlInfo("testStream", 1, 1);
+        MapContext context = new StreamContext();
+        JexlScript script = jexl.createScript(src, "list");
+        Object result = script.execute(context, Arrays.asList(1, 2, 3));
+        Assert.assertEquals(14, result);
+        //Optional<?> result = (Optional<?>) script.execute(context, 
Arrays.asList(1, 2, 3));
+        //Assert.assertEquals(14, result.get());
+    }
+
+    @Test
+    public void testOptionalArgs() {
+        JexlBuilder builder = new JexlBuilder();
+        JexlArithmetic jexla = new OptionalArithmetic(true);
+        JexlUberspect uber = builder.create().getUberspect();
+        JexlEngine jexl = builder.uberspect(new 
ReferenceUberspect(uber)).arithmetic(jexla).safe(false).create();
+        JexlInfo info = new JexlInfo("testStream", 1, 1);
+        MapContext context = new StreamContext();
+        String src = "x + x";
+        JexlScript script = jexl.createScript(src, "x");
+        Optional<Integer> x = Optional.of(21);
+        Object result = script.execute(context, x);
+        Assert.assertEquals(42, result);
+    }
+
     @Test
     public void test342() {
         JexlBuilder builder = new JexlBuilder();

Reply via email to