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