Author: henrib
Date: Thu Jun  9 20:15:02 2016
New Revision: 1747591

URL: http://svn.apache.org/viewvc?rev=1747591&view=rev
Log:
JEXL-194: 
added overload handling of forEach operator in arithmetic to allow customized 
iterator, added examples of synchronized arithmetic & context

Added:
    
commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/SynchronizedArithmetic.java
   (with props)
    
commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/SynchronizedContext.java
   (with props)
    
commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/SynchronizedOverloadsTest.java
   (with props)
Modified:
    
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlOperator.java
    
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
    
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Operators.java

Modified: 
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlOperator.java
URL: 
http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlOperator.java?rev=1747591&r1=1747590&r2=1747591&view=diff
==============================================================================
--- 
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlOperator.java
 (original)
+++ 
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlOperator.java
 Thu Jun  9 20:15:02 2016
@@ -21,16 +21,16 @@ package org.apache.commons.jexl3;
  * The JEXL operators.
  *
  * These are the operators that are executed by JexlArithmetic methods.
- * 
+ *
  * <p>Each of them  associates a symbol to a method signature.
  * For instance, '+' is associated to 'T add(L x, R y)'.</p>
- * 
+ *
  * <p>The default JexlArithmetic implements generic versions of these methods 
using Object as arguments.
  * You can use your own derived JexlArithmetic that override and/or overload 
those operator methods; these methods
  * must be public,
  * must respect the return type when primitive
  * and may be overloaded multiple times with different signatures.</p>
- * 
+ *
  * @since 3.0
  */
 public enum JexlOperator {
@@ -239,23 +239,40 @@ public enum JexlOperator {
 
     /**
      * Property get operator as in: x.y.
+     * <strong>Syntax:</strong> <code>x.y</code>
+     * <br><strong>Method:</strong> <code>Object propertyGet(L x, R y);</code>.
      */
     PROPERTY_GET(".", "propertyGet", 2),
 
     /**
      * Property set operator as in: x.y = z.
+     * <strong>Syntax:</strong> <code>x.y = z</code>
+     * <br><strong>Method:</strong> <code>void propertySet(L x, R y, V 
z);</code>.
      */
     PROPERTY_SET(".=", "propertySet", 3),
 
     /**
      * Array get operator as in: x[y].
+     * <strong>Syntax:</strong> <code>x.y</code>
+     * <br><strong>Method:</strong> <code>Object arrayyGet(L x, R y);</code>.
      */
     ARRAY_GET("[]", "arrayGet", 2),
 
     /**
      * Array set operator as in: x[y] = z.
+     * <strong>Syntax:</strong> <code>x[y] = z</code>
+     * <br><strong>Method:</strong> <code>void arraySet(L x, R y, V z);</code>.
+     */
+    ARRAY_SET("[]=", "arraySet", 3),
+
+    /**
+     * Iterator generator as in for(var x : y).
+     * <strong>Syntax:</strong> <code>for(var x : y){...} </code>
+     * <br><strong>Method:</strong> <code>Iterator<Object> forEach(R 
y);</code>.
+     *
+     * If the returned Iterator is AutoCloseable, close will be called after 
the last execution of the loop block.
      */
-    ARRAY_SET("[]=", "arraySet", 3);
+    FOR_EACH("for(...)", "forEach", 1);
 
     /**
      * The operator symbol.
@@ -279,7 +296,7 @@ public enum JexlOperator {
 
     /**
      * Creates a base operator.
-     * 
+     *
      * @param o    the operator name
      * @param m    the method name associated to this operator in a 
JexlArithmetic
      * @param argc the number of parameters for the method
@@ -293,7 +310,7 @@ public enum JexlOperator {
 
     /**
      * Creates a side-effect operator.
-     * 
+     *
      * @param o the operator name
      * @param m the method name associated to this operator in a JexlArithmetic
      * @param b the base operator, ie + for +=
@@ -307,7 +324,7 @@ public enum JexlOperator {
 
     /**
      * Gets this operator symbol.
-     * 
+     *
      * @return the symbol
      */
     public final String getOperatorSymbol() {
@@ -316,7 +333,7 @@ public enum JexlOperator {
 
     /**
      * Gets this operator method name in a JexlArithmetic.
-     * 
+     *
      * @return the method name
      */
     public final String getMethodName() {
@@ -325,7 +342,7 @@ public enum JexlOperator {
 
     /**
      * Gets this operator number of parameters.
-     * 
+     *
      * @return the method arity
      */
     public int getArity() {
@@ -334,7 +351,7 @@ public enum JexlOperator {
 
     /**
      * Gets the base operator.
-     * 
+     *
      * @return the base operator
      */
     public final JexlOperator getBaseOperator() {

Modified: 
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
URL: 
http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java?rev=1747591&r1=1747590&r2=1747591&view=diff
==============================================================================
--- 
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
 (original)
+++ 
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
 Thu Jun  9 20:15:02 2016
@@ -249,7 +249,7 @@ public class Interpreter extends ParserV
      */
     protected void closeIfSupported(Object closeable) {
         if (closeable != null) {
-            if (AUTOCLOSEABLE == null || 
AUTOCLOSEABLE.isAssignableFrom(closeable.getClass())) {
+            //if (AUTOCLOSEABLE == null || 
AUTOCLOSEABLE.isAssignableFrom(closeable.getClass())) {
                 JexlMethod mclose = uberspect.getMethod(closeable, "close", 
EMPTY_PARAMS);
                 if (mclose != null) {
                     try {
@@ -258,7 +258,7 @@ public class Interpreter extends ParserV
                         logger.warn(xignore);
                     }
                 }
-            }
+            //}
         }
     }
 
@@ -827,7 +827,7 @@ public class Interpreter extends ParserV
             // get an iterator for the collection/array etc via the 
introspector.
             Object forEach = null;
             try {
-                forEach = null;//operators.tryForeachOverload(node, 
iterableValue);
+                forEach = operators.tryForeachOverload(node, iterableValue);
                 Iterator<?> itemsIterator = forEach instanceof Iterator
                                 ? (Iterator<?>) forEach
                                 : uberspect.getIterator(iterableValue);

Modified: 
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Operators.java
URL: 
http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Operators.java?rev=1747591&r1=1747590&r2=1747591&view=diff
==============================================================================
--- 
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Operators.java
 (original)
+++ 
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Operators.java
 Thu Jun  9 20:15:02 2016
@@ -24,6 +24,7 @@ import org.apache.commons.jexl3.introspe
 import org.apache.commons.jexl3.introspection.JexlUberspect;
 import org.apache.commons.jexl3.parser.JexlNode;
 
+import java.util.Iterator;
 /**
  * Helper class to deal with operator overloading and specifics.
  * @since 3.0
@@ -95,6 +96,44 @@ public class Operators {
             }
         }
         return JexlEngine.TRY_FAILED;
+    }
+
+    /**
+     * Attempts to call an overloaded forEach operator.
+     * <p>
+     * This takes care of finding and caching the operator method when 
appropriate
+     * @param node     the syntactic node
+     * @param arg      the argument
+     * @return the result of the operator evaluation or TRY_FAILED
+     */
+    protected Object tryForeachOverload(JexlNode node, Object arg) {
+        if (operators != null && operators.overloads(JexlOperator.FOR_EACH)) {
+            final JexlArithmetic arithmetic = interpreter.arithmetic;
+            final boolean cache = interpreter.cache;
+            if (cache) {
+                Object cached = node.jjtGetValue();
+                if (cached instanceof JexlMethod) {
+                    JexlMethod me = (JexlMethod) cached;
+                    Object eval = 
me.tryInvoke(JexlOperator.FOR_EACH.getMethodName(), arithmetic, arg);
+                    if (!me.tryFailed(eval)) {
+                        return eval;
+                    }
+                }
+            }
+            try {
+                JexlMethod vm = operators.getOperator(JexlOperator.FOR_EACH, 
arg);
+                if (vm != null && 
Iterator.class.isAssignableFrom(vm.getReturnType())) {
+                    Object result = vm.invoke(arithmetic, arg);
+                    if (cache) {
+                        node.jjtSetValue(vm);
+                    }
+                    return result;
+                }
+            } catch (Exception xany) {
+                interpreter.operatorError(node, JexlOperator.FOR_EACH, xany);
+            }
+        }
+        return JexlEngine.TRY_FAILED;
     }
 
     /**

Added: 
commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/SynchronizedArithmetic.java
URL: 
http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/SynchronizedArithmetic.java?rev=1747591&view=auto
==============================================================================
--- 
commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/SynchronizedArithmetic.java
 (added)
+++ 
commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/SynchronizedArithmetic.java
 Thu Jun  9 20:15:02 2016
@@ -0,0 +1,256 @@
+/*
+ * 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.lang.reflect.Field;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.concurrent.atomic.AtomicInteger;
+import sun.misc.Unsafe;
+
+/**
+ *
+ * An example arithmetic that uses object intrinsic monitors to synchronize 
get/set/iteration on Maps.
+ */
+public class SynchronizedArithmetic extends JexlArithmetic {
+    /**
+     * Monitor/synchronized protected access to gets/set/iterator on maps.
+     */
+    private final Monitor monitor;
+
+    /**
+     * A base synchronized arithemtic.
+     * @param monitor the synchronizatiobn monitor
+     * @param strict  whether the aruthmetic is strict or not
+     */
+    protected SynchronizedArithmetic(Monitor monitor, boolean strict) {
+        super(strict);
+        this.monitor = monitor;
+    }
+
+
+    /**
+     * An indirect way to determine we are actually calling what is needed.
+     * <p>
+     * This class counts how many times we called enter & exit; they should be 
balanced
+     */
+    public static abstract class Monitor{
+        /* Counts the number of times enter is called. */
+        private final AtomicInteger enters = new AtomicInteger(0);
+        /* Counts the number of times exit is called. */
+        private final AtomicInteger exits = new AtomicInteger(0);
+
+        /**
+         * Enter an object monitor.
+         * @param o the monitored object
+         */
+        protected void monitorEnter(Object o) {
+            UNSAFE.monitorEnter(o);
+            enters.incrementAndGet();
+        }
+
+        /**
+         * Exits an object monitor.
+         * @param o the monitored object
+         */
+        protected void monitorExit(Object o) {
+            UNSAFE.monitorExit(o);
+            exits.incrementAndGet();
+        }
+
+        /**
+         * Whether the number of monitor enter is equals to the number of 
exits.
+         * @return true if balanced, false otherwise
+         */
+        public boolean isBalanced() {
+            return enters.get() == exits.get();
+        }
+
+        /**
+         * The number of enter calls.
+         * @return how many enter were executed
+         */
+        public int getCount() {
+            return enters.get();
+        }
+
+    }
+
+    /**
+     * You should know better than to use this...
+     */
+    private static Unsafe UNSAFE;
+    static {
+        try {
+            Field f = Unsafe.class.getDeclaredField("theUnsafe");
+            f.setAccessible(true);
+            UNSAFE = (Unsafe) f.get(null);
+        } catch (Exception e) {
+            UNSAFE = null;
+        }
+    }
+
+    /**
+     * Using the unsafe to enter & exit object intrinsic monitors.
+     */
+    static class UnsafeMonitor extends Monitor {
+        @Override protected void monitorEnter(Object o) {
+            UNSAFE.monitorEnter(o);
+            super.monitorEnter(o);
+        }
+
+        @Override protected void monitorExit(Object o) {
+            UNSAFE.monitorExit(o);
+            super.monitorExit(o);
+        }
+    }
+
+    /**
+     * An iterator that implements Closeable (at least implements a close 
method)
+     * and uses monitors to protect iteration.
+     */
+    public class SynchronizedIterator implements /*Closeable,*/ 
Iterator<Object> {
+        private final Object monitored;
+        private Iterator<Object> iterator;
+
+        SynchronizedIterator(Object locked, Iterator<Object> ii) {
+            monitored = locked;
+            monitor.monitorEnter(monitored);
+            try {
+                iterator = ii;
+            } finally {
+                if (iterator == null) {
+                    monitor.monitorExit(monitored);
+                }
+            }
+        }
+
+        @Override
+        protected void finalize() throws Throwable {
+            close();
+            super.finalize();
+        }
+
+        //@Override
+        public void close() {
+            if (iterator != null) {
+                monitor.monitorExit(monitored);
+                iterator = null;
+            }
+        }
+
+        @Override
+        public boolean hasNext() {
+            if (iterator == null) {
+                return false;
+            }
+            boolean n = iterator.hasNext();
+            if (!n) {
+                close();
+            }
+            return n;
+        }
+
+        @Override
+        public Object next() {
+            if (iterator == null) {
+                throw new NoSuchElementException();
+            }
+            return iterator.next();
+        }
+
+        @Override
+        public void remove() {
+            if (iterator != null) {
+                iterator.remove();
+            }
+        }
+    }
+
+    /**
+     * Jexl pseudo-overload for array-like access get operator (map[key]) for 
maps.
+     * <p>synchronized(map) { return map.get(key); }
+     * @param map the map
+     * @param key the key
+     * @return the value associated to the key in the map
+     */
+    public Object arrayGet(Map<?, ?> map, Object key) {
+        monitor.monitorEnter(map);
+        try {
+            return map.get(key);
+        } finally {
+            monitor.monitorExit(map);
+        }
+    }
+    /**
+     * Jexl pseudo-overload for array-like access set operator (map[key] = 
value) for maps.
+     * <p>synchronized(map) { map.put(key, value); }
+     * @param map the map
+     * @param key the key
+     * @param value the value
+     */
+    public void arraySet(Map<Object, Object> map, Object key, Object value) {
+        monitor.monitorEnter(map);
+        try {
+            map.put(key, value);
+        } finally {
+            monitor.monitorExit(map);
+        }
+    }
+    /**
+     * Jexl pseudo-overload for property access get operator (map.key) for 
maps.
+     * <p>synchronized(map) { return map.get(key); }
+     *
+     * @param map the map
+     * @param key the key
+     * @return the value associated to the key in the map
+     */
+    public Object propertyGet(Map<?, ?> map, Object key) {
+        monitor.monitorEnter(map);
+        try {
+            return map.get(key);
+        } finally {
+            monitor.monitorExit(map);
+        }
+    }
+
+   /**
+     * Jexl pseudo-overload for array-like access set operator (map.key = 
value) for maps.
+     * <p>synchronized(map) { map.put(key, value); }
+     * @param map the map
+     * @param key the key
+     * @param value the value
+     */
+    public void propertySet(Map<Object, Object> map, Object key, Object value) 
{
+        monitor.monitorEnter(map);
+        try {
+            map.put(key, value);
+        } finally {
+            monitor.monitorExit(map);
+        }
+    }
+
+    /**
+     * Creates an iterator on the map values that executes under the map 
monitor.
+     * @param map the map
+     * @return the iterator
+     */
+    public Iterator<Object> forEach(Map<Object, Object> map) {
+        return new SynchronizedIterator(map, map.values().iterator());
+    }
+}

Propchange: 
commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/SynchronizedArithmetic.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 
commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/SynchronizedContext.java
URL: 
http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/SynchronizedContext.java?rev=1747591&view=auto
==============================================================================
--- 
commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/SynchronizedContext.java
 (added)
+++ 
commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/SynchronizedContext.java
 Thu Jun  9 20:15:02 2016
@@ -0,0 +1,61 @@
+/*
+ * 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;
+
+/**
+ * Exposes a synchronized call to a script and synchronizes access to get/set 
methods.
+ */
+public class SynchronizedContext extends MapContext {
+    private final JexlContext context;
+
+    public SynchronizedContext(JexlContext ctxt) {
+        this.context = ctxt;
+    }
+
+    /**
+     * Calls a script synchronized by an object monitor.
+     * @param var the object used for sync
+     * @param script the script
+     * @return the script value
+     */
+    public Object call(Object var, JexlScript script) {
+        String[] parms = script.getParameters();
+        boolean varisarg = parms != null && parms.length == 1;
+        if (var == null) {
+            return varisarg ? script.execute(context, var) : 
script.execute(context);
+        } else {
+            synchronized (var) {
+                return varisarg ? script.execute(context, var) : 
script.execute(context);
+            }
+        }
+    }
+
+    @Override
+    public Object get(String name) {
+        synchronized (this) {
+            return super.get(name);
+        }
+    }
+
+    @Override
+    public void set(String name, Object value) {
+        synchronized (this) {
+            super.set(name, value);
+        }
+    }
+
+}

Propchange: 
commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/SynchronizedContext.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 
commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/SynchronizedOverloadsTest.java
URL: 
http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/SynchronizedOverloadsTest.java?rev=1747591&view=auto
==============================================================================
--- 
commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/SynchronizedOverloadsTest.java
 (added)
+++ 
commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/SynchronizedOverloadsTest.java
 Thu Jun  9 20:15:02 2016
@@ -0,0 +1,70 @@
+/*
+ * 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.util.Map;
+import java.util.TreeMap;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+
+/**
+ * Test cases for synchronized calls.
+ * <p>May be a base for synchronized calls.
+ */
+@SuppressWarnings({"boxing", "UnnecessaryBoxing", 
"AssertEqualsBetweenInconvertibleTypes"})
+public class SynchronizedOverloadsTest extends JexlTestCase {
+    public SynchronizedOverloadsTest() {
+        super("SynchronizedOverloadsTest", null);
+    }
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        // ensure jul logging is only error to avoid warning in silent mode
+        
java.util.logging.Logger.getLogger(JexlEngine.class.getName()).setLevel(java.util.logging.Level.SEVERE);
+    }
+
+
+    @Test
+    public void testSynchronizer() throws Exception {
+        Map<String, Object> ns = new TreeMap<String, Object>();
+        ns.put("synchronized", SynchronizedContext.class);
+        JexlContext jc = new MapContext();
+        JexlEngine jexl = new JexlBuilder().namespaces(ns).create();
+        JexlScript js0 = jexl.createScript("synchronized:call(x, 
(y)->{y.size()})", "x");
+        Object size = js0.execute(jc, "foobar");
+        Assert.assertEquals(6, size);
+    }
+
+    @Test
+    public void testUnsafeMonitor() throws Exception {
+        SynchronizedArithmetic.Monitor monitor = new 
SynchronizedArithmetic.UnsafeMonitor();
+        Map<String, Object> foo = new TreeMap<String, Object>();
+        foo.put("one", 1);
+        foo.put("two", 2);
+        foo.put("three", 3);
+        JexlContext jc = new SynchronizedContext(new MapContext());
+        JexlEngine jexl = new JexlBuilder().arithmetic(new 
SynchronizedArithmetic(monitor, true)).create();
+        JexlScript js0 = jexl.createScript("x['four'] = 4; var t = 0.0; 
for(var z: x) { t += z; }; call(t, (y)->{return y});", "x");
+        Object t = js0.execute(jc, foo);
+        Assert.assertEquals(10.0d, t);
+        Assert.assertTrue(monitor.isBalanced());
+        Assert.assertEquals(2, monitor.getCount());
+    }
+}

Propchange: 
commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/SynchronizedOverloadsTest.java
------------------------------------------------------------------------------
    svn:eol-style = native


Reply via email to