You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by he...@apache.org on 2020/06/11 18:24:56 UTC
[commons-jexl] branch master updated: JEXL-333: added namespace
handling to pragma processing;
added test Task #JEXL-333 - Allow declaration of namespace within script
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 826d3d3 JEXL-333: added namespace handling to pragma processing; added test Task #JEXL-333 - Allow declaration of namespace within script
826d3d3 is described below
commit 826d3d3b29c9ad5507d50135c4f9dd1804dac96d
Author: henrib <he...@apache.org>
AuthorDate: Thu Jun 11 20:24:20 2020 +0200
JEXL-333: added namespace handling to pragma processing; added test
Task #JEXL-333 - Allow declaration of namespace within script
---
RELEASE-NOTES.txt | 1 +
.../java/org/apache/commons/jexl3/JexlBuilder.java | 7 +--
.../java/org/apache/commons/jexl3/JexlOptions.java | 21 +++++++++
.../org/apache/commons/jexl3/internal/Engine.java | 25 +++++++++-
.../commons/jexl3/internal/InterpreterBase.java | 7 +--
.../jexl3/internal/introspection/Introspector.java | 6 +--
src/site/xdoc/changes.xml | 3 ++
.../apache/commons/jexl3/ContextNamespaceTest.java | 53 +++++++++++++++++++---
8 files changed, 104 insertions(+), 19 deletions(-)
diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index 6d1bca8..ae6f5e3 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -48,6 +48,7 @@ What's new in 3.2:
New Features in 3.2:
====================
+* JEXL-333: Allow declaration of namespace within script
* JEXL-317: Support script cancellation through less invasive API
* JEXL-307: Variable redeclaration option
* JEXL-295: Add unary plus operator
diff --git a/src/main/java/org/apache/commons/jexl3/JexlBuilder.java b/src/main/java/org/apache/commons/jexl3/JexlBuilder.java
index c9212e5..2a5b006 100644
--- a/src/main/java/org/apache/commons/jexl3/JexlBuilder.java
+++ b/src/main/java/org/apache/commons/jexl3/JexlBuilder.java
@@ -85,9 +85,6 @@ public class JexlBuilder {
/** Whether getVariables considers all potential equivalent syntactic forms. */
private int collectMode = 1;
- /** The map of 'prefix:function' to object implementing the namespaces. */
- private Map<String, Object> namespaces = null;
-
/** The {@link JexlArithmetic} instance. */
private JexlArithmetic arithmetic = null;
@@ -475,7 +472,7 @@ public class JexlBuilder {
* @return this builder
*/
public JexlBuilder namespaces(Map<String, Object> ns) {
- this.namespaces = ns;
+ options.setNamespaces(ns);
return this;
}
@@ -483,7 +480,7 @@ public class JexlBuilder {
* @return the map of namespaces.
*/
public Map<String, Object> namespaces() {
- return this.namespaces;
+ return options.getNamespaces();
}
/**
diff --git a/src/main/java/org/apache/commons/jexl3/JexlOptions.java b/src/main/java/org/apache/commons/jexl3/JexlOptions.java
index 943aa72..cf316e1 100644
--- a/src/main/java/org/apache/commons/jexl3/JexlOptions.java
+++ b/src/main/java/org/apache/commons/jexl3/JexlOptions.java
@@ -18,6 +18,8 @@
package org.apache.commons.jexl3;
import java.math.MathContext;
+import java.util.Collections;
+import java.util.Map;
import org.apache.commons.jexl3.internal.Engine;
/**
@@ -68,6 +70,8 @@ public final class JexlOptions {
private boolean strictArithmetic = true;
/** The default flags, all but safe. */
private int flags = DEFAULT;
+ /** The namespaces .*/
+ private Map<String, Object> namespaces = Collections.emptyMap();
/**
* Sets the value of a flag in a mask.
@@ -376,10 +380,27 @@ public final class JexlOptions {
mathScale = src.mathScale;
strictArithmetic = src.strictArithmetic;
flags = src.flags;
+ namespaces = src.namespaces;
return this;
}
+
+ /**
+ * Gets the optional map of namespaces.
+ * @return the map of namespaces, may be empty, not null
+ */
+ public Map<String, Object> getNamespaces() {
+ return namespaces;
+ }
/**
+ * Sets the optional map of namespaces
+ * @param ns a namespaces map
+ */
+ public void setNamespaces(Map<String, Object> ns) {
+ this.namespaces = ns == null? Collections.emptyMap() : ns;
+ }
+
+ /**
* Creates a copy of this instance.
* @return a copy
*/
diff --git a/src/main/java/org/apache/commons/jexl3/internal/Engine.java b/src/main/java/org/apache/commons/jexl3/internal/Engine.java
index ae00865..d458139 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Engine.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Engine.java
@@ -49,6 +49,7 @@ import java.io.StringReader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
@@ -81,6 +82,10 @@ public class Engine extends JexlEngine {
*/
protected static final String PRAGMA_OPTIONS = "jexl.options";
/**
+ * The prefix of a namespace pragma.
+ */
+ protected static final String PRAGMA_JEXLNS = "jexl.namespace.";
+ /**
* The Log to which all JexlEngine messages will be logged.
*/
protected final Log logger;
@@ -380,19 +385,35 @@ public class Engine extends JexlEngine {
context instanceof JexlContext.PragmaProcessor
? (JexlContext.PragmaProcessor) context
: null;
+ Map<String, Object> ns = null;
for(Map.Entry<String, Object> pragma : pragmas.entrySet()) {
String key = pragma.getKey();
Object value = pragma.getValue();
if (PRAGMA_OPTIONS.equals(key)) {
+ // jexl.options
if (value instanceof String) {
String[] vs = ((String) value).split(" ");
opts.setFlags(vs);
}
}
+ else if (key.startsWith(PRAGMA_JEXLNS) && value instanceof String) {
+ // jexl.namespace.***
+ String nsname = key.substring(PRAGMA_JEXLNS.length());
+ if (nsname != null && !nsname.isEmpty()) {
+ String nsclass = value.toString();
+ if (ns == null) {
+ ns = new HashMap<>(functions);
+ }
+ ns.put(nsname, nsclass);
+ }
+ }
if (processor != null) {
processor.processPragma(key, value);
}
}
+ if (ns != null) {
+ opts.setNamespaces(ns);
+ }
}
}
@@ -479,7 +500,7 @@ public class Engine extends JexlEngine {
final ASTJexlScript script = parse(null, PROPERTY_FEATURES, src, scope);
final JexlNode node = script.jjtGetChild(0);
final Frame frame = script.createFrame(bean);
- final Interpreter interpreter = createInterpreter(context, frame, null);
+ final Interpreter interpreter = createInterpreter(context, frame, options);
return interpreter.visitLexicalNode(node, null);
} catch (JexlException xjexl) {
if (silent) {
@@ -508,7 +529,7 @@ public class Engine extends JexlEngine {
final ASTJexlScript script = parse(null, PROPERTY_FEATURES, src, scope);
final JexlNode node = script.jjtGetChild(0);
final Frame frame = script.createFrame(bean, value);
- final Interpreter interpreter = createInterpreter(context, frame, null);
+ final Interpreter interpreter = createInterpreter(context, frame, options);
interpreter.visitLexicalNode(node, null);
} catch (JexlException xjexl) {
if (silent) {
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 0b05b4d..b56245a 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/InterpreterBase.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/InterpreterBase.java
@@ -69,7 +69,7 @@ public abstract class InterpreterBase extends ParserVisitor {
protected final AtomicBoolean cancelled;
/** Empty parameters for method matching. */
protected static final Object[] EMPTY_PARAMS = new Object[0];
- /** The context to store/retrieve variables. */
+ /** The namespace resolver. */
protected final JexlContext.NamespaceResolver ns;
/** The operators evaluation delegate. */
protected final Operators operators;
@@ -108,7 +108,8 @@ public abstract class InterpreterBase extends ParserVisitor {
acancel = ((JexlContext.CancellationHandle) context).getCancellation();
}
this.cancelled = acancel != null? acancel : new AtomicBoolean(false);
- this.functions = jexl.functions;
+ Map<String,Object> ons = options.getNamespaces();
+ this.functions = ons.isEmpty()? jexl.functions : ons;
this.functors = null;
this.operators = new Operators(this);
}
@@ -228,7 +229,7 @@ public abstract class InterpreterBase extends ParserVisitor {
}
return namespace;
}
-
+
/**
* Defines a variable.
* @param var the variable to define
diff --git a/src/main/java/org/apache/commons/jexl3/internal/introspection/Introspector.java b/src/main/java/org/apache/commons/jexl3/internal/introspection/Introspector.java
index b442aa3..88c460a 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/introspection/Introspector.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/introspection/Introspector.java
@@ -226,8 +226,8 @@ public final class Introspector {
*/
public Constructor<?> getConstructor(final Class<?> c, final MethodKey key) {
Constructor<?> ctor;
+ lock.readLock().lock();
try {
- lock.readLock().lock();
ctor = constructorsMap.get(key);
if (ctor != null) {
// miss or not?
@@ -237,8 +237,8 @@ public final class Introspector {
lock.readLock().unlock();
}
// let's introspect...
+ lock.writeLock().lock();
try {
- lock.writeLock().lock();
// again for kicks
ctor = constructorsMap.get(key);
if (ctor != null) {
@@ -259,7 +259,7 @@ public final class Introspector {
// add it to list of known loaded classes
constructibleClasses.put(cname, clazz);
}
- List<Constructor<?>> l = new ArrayList<Constructor<?>>();
+ List<Constructor<?>> l = new ArrayList<>();
for (Constructor<?> ictor : clazz.getConstructors()) {
if (permissions.allow(ictor)) {
l.add(ictor);
diff --git a/src/site/xdoc/changes.xml b/src/site/xdoc/changes.xml
index fbf12b5..ef34341 100644
--- a/src/site/xdoc/changes.xml
+++ b/src/site/xdoc/changes.xml
@@ -26,6 +26,9 @@
</properties>
<body>
<release version="3.2" date="unreleased">
+ <action dev="henrib" type="add" issue="JEXL-333">
+ Allow declaration of namespace within script
+ </action>
<action dev="henrib" type="fix" issue="JEXL-331" due-to="David Costanzo">
Please document \uXXXX escape sequence
</action>
diff --git a/src/test/java/org/apache/commons/jexl3/ContextNamespaceTest.java b/src/test/java/org/apache/commons/jexl3/ContextNamespaceTest.java
index 48f41d6..6f38994 100644
--- a/src/test/java/org/apache/commons/jexl3/ContextNamespaceTest.java
+++ b/src/test/java/org/apache/commons/jexl3/ContextNamespaceTest.java
@@ -16,7 +16,6 @@
*/
package org.apache.commons.jexl3;
-import org.apache.commons.jexl3.internal.Engine;
import org.junit.Assert;
import org.junit.Test;
@@ -34,9 +33,18 @@ public class ContextNamespaceTest extends JexlTestCase {
* Accesses the thread context and cast it.
*/
public static class Taxes {
+ private final double vat;
+
+ public Taxes(TaxesContext ctxt) {
+ vat = ctxt.getVAT();
+ }
+
+ public Taxes(double d) {
+ vat = d;
+ }
+
public double vat(double n) {
- TaxesContext context = (TaxesContext) JexlEngine.getThreadContext();
- return n * context.getVAT() / 100.;
+ return (n * vat) / 100.;
}
}
@@ -44,7 +52,7 @@ public class ContextNamespaceTest extends JexlTestCase {
* A thread local context carrying a namespace and some inner constants.
*/
public static class TaxesContext extends MapContext implements JexlContext.ThreadLocal, JexlContext.NamespaceResolver {
- private final Taxes taxes = new Taxes();
+ private Taxes taxes = null;
private final double vat;
TaxesContext(double vat) {
@@ -53,7 +61,13 @@ public class ContextNamespaceTest extends JexlTestCase {
@Override
public Object resolveNamespace(String name) {
- return "taxes".equals(name) ? taxes : null;
+ if ("taxes".equals(name)) {
+ if (taxes == null) {
+ taxes = new Taxes(vat);
+ }
+ return taxes;
+ }
+ return null;
}
public double getVAT() {
@@ -63,13 +77,40 @@ public class ContextNamespaceTest extends JexlTestCase {
@Test
public void testThreadedContext() throws Exception {
- JexlEngine jexl = new Engine();
+ JexlEngine jexl = new JexlBuilder().create();
TaxesContext context = new TaxesContext(18.6);
String strs = "taxes:vat(1000)";
JexlScript staxes = jexl.createScript(strs);
Object result = staxes.execute(context);
Assert.assertEquals(186., result);
}
+
+ @Test
+ public void testNamespacePragma() throws Exception {
+ JexlEngine jexl = new JexlBuilder().create();
+ JexlContext context = new TaxesContext(18.6);
+ // local namespace tax declared
+ String strs =
+ "#pragma jexl.namespace.tax org.apache.commons.jexl3.ContextNamespaceTest$Taxes\n"
+ + "tax:vat(2000)";
+ JexlScript staxes = jexl.createScript(strs);
+ Object result = staxes.execute(context);
+ Assert.assertEquals(372., result);
+ }
+
+
+ @Test
+ public void testNamespacePragmaString() throws Exception {
+ JexlEngine jexl = new JexlBuilder().create();
+ JexlContext context = new MapContext();
+ // local namespace str declared
+ String strs =
+ "#pragma jexl.namespace.str java.lang.String\n"
+ + "str:format('%04d', 42)";
+ JexlScript staxes = jexl.createScript(strs);
+ Object result = staxes.execute(context);
+ Assert.assertEquals("0042", result);
+ }
public static class Vat {
private double vat;