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 2021/05/31 17:41:40 UTC
[commons-jexl] branch master updated: JEXL-346: use namespace and
variable names to disambiguate ternary expressions
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 cc963c9 JEXL-346: use namespace and variable names to disambiguate ternary expressions
cc963c9 is described below
commit cc963c93c1ded0238f1cb53cea69cfc0fd659b9c
Author: henrib <he...@apache.org>
AuthorDate: Mon May 31 19:41:34 2021 +0200
JEXL-346: use namespace and variable names to disambiguate ternary expressions
---
.../org/apache/commons/jexl3/JexlFeatures.java | 25 +++++++++++++
.../org/apache/commons/jexl3/internal/Engine.java | 15 ++++++--
.../apache/commons/jexl3/parser/JexlParser.java | 42 ++++++++++++++++++++++
.../apache/commons/jexl3/ContextNamespaceTest.java | 35 ++++++++++++++++++
4 files changed, 114 insertions(+), 3 deletions(-)
diff --git a/src/main/java/org/apache/commons/jexl3/JexlFeatures.java b/src/main/java/org/apache/commons/jexl3/JexlFeatures.java
index dc79c75..9da12df 100644
--- a/src/main/java/org/apache/commons/jexl3/JexlFeatures.java
+++ b/src/main/java/org/apache/commons/jexl3/JexlFeatures.java
@@ -18,9 +18,11 @@ package org.apache.commons.jexl3;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;
import java.util.Objects;
+import java.util.function.Predicate;
/**
* A set of language feature options.
@@ -50,6 +52,10 @@ public final class JexlFeatures {
private long flags;
/** The set of reserved names, aka global variables that can not be masked by local variables or parameters. */
private Set<String> reservedNames;
+ /** The namespace names. */
+ private Predicate<String> nameSpaces;
+ /** The false predicate. */
+ public static final Predicate<String> TEST_STR_FALSE = (s)->false;
/** Te feature names (for toString()). */
private static final String[] F_NAMES = {
"register", "reserved variable", "local variable", "assign/modify",
@@ -106,6 +112,7 @@ public final class JexlFeatures {
| (1L << ANNOTATION)
| (1L << SCRIPT);
reservedNames = Collections.emptySet();
+ nameSpaces = TEST_STR_FALSE;
}
/**
@@ -115,6 +122,7 @@ public final class JexlFeatures {
public JexlFeatures(final JexlFeatures features) {
this.flags = features.flags;
this.reservedNames = features.reservedNames;
+ this.nameSpaces = features.nameSpaces;
}
@Override
@@ -187,6 +195,23 @@ public final class JexlFeatures {
}
/**
+ * Sets a test to determine namespace declaration.
+ * @param names the name predicate
+ * @return this features instance
+ */
+ public JexlFeatures namespaceTest(final Predicate<String> names) {
+ nameSpaces = names == null? TEST_STR_FALSE : names;
+ return this;
+ }
+
+ /**
+ * @return the declared namespaces test.
+ */
+ public Predicate<String> namespaceTest() {
+ return nameSpaces;
+ }
+
+ /**
* Sets a feature flag.
* @param feature the feature ordinal
* @param flag turn-on, turn off
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 cc6959c..54cee50 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Engine.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Engine.java
@@ -55,6 +55,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Predicate;
/**
* A JexlEngine implementation.
@@ -212,8 +213,13 @@ public class Engine extends JexlEngine {
this.functions = conf.namespaces() == null ? Collections.emptyMap() : conf.namespaces();
// parsing & features:
final JexlFeatures features = conf.features() == null? DEFAULT_FEATURES : conf.features();
- this.expressionFeatures = new JexlFeatures(features).script(false);
- this.scriptFeatures = new JexlFeatures(features).script(true);
+ Predicate<String> nsTest = features.namespaceTest();
+ final Set<String> nsNames = functions.keySet();
+ if (!nsNames.isEmpty()) {
+ nsTest = nsTest == JexlFeatures.TEST_STR_FALSE ?nsNames::contains : nsTest.or(nsNames::contains);
+ }
+ this.expressionFeatures = new JexlFeatures(features).script(false).namespaceTest(nsTest);
+ this.scriptFeatures = new JexlFeatures(features).script(true).namespaceTest(nsTest);
this.charset = conf.charset();
// caching:
this.cache = conf.cache() <= 0 ? null : new SoftCache<Source, ASTJexlScript>(conf.cache());
@@ -840,7 +846,10 @@ public class Engine extends JexlEngine {
*/
protected ASTJexlScript parse(final JexlInfo info, final JexlFeatures parsingf, final String src, final Scope scope) {
final boolean cached = src.length() < cacheThreshold && cache != null;
- final JexlFeatures features = parsingf != null? parsingf : DEFAULT_FEATURES;
+ JexlFeatures features = parsingf != null? parsingf : DEFAULT_FEATURES;
+ // if (features.getNameSpaces().isEmpty() && !functions.isEmpty()) {
+ // features = new JexlFeatures(features).nameSpaces(functions.keySet());
+ // }
final Source source = cached? new Source(features, src) : null;
ASTJexlScript script = null;
if (source != null) {
diff --git a/src/main/java/org/apache/commons/jexl3/parser/JexlParser.java b/src/main/java/org/apache/commons/jexl3/parser/JexlParser.java
index 396111d..b630cf4 100644
--- a/src/main/java/org/apache/commons/jexl3/parser/JexlParser.java
+++ b/src/main/java/org/apache/commons/jexl3/parser/JexlParser.java
@@ -29,12 +29,15 @@ import java.io.StringReader;
import java.lang.reflect.Constructor;
import java.util.ArrayDeque;
import java.util.Arrays;
+import java.util.Collections;
import java.util.Deque;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.function.Predicate;
/**
@@ -68,6 +71,10 @@ public abstract class JexlParser extends StringParser {
*/
protected Map<String, Object> pragmas = null;
/**
+ * The known namespaces.
+ */
+ protected Set<String> namespaces = null;
+ /**
* The number of imbricated loops.
*/
protected int loopCount = 0;
@@ -121,6 +128,7 @@ public abstract class JexlParser extends StringParser {
frame = null;
frames.clear();
pragmas = null;
+ namespaces = null;
loopCounts.clear();
loopCount = 0;
blocks.clear();
@@ -283,6 +291,10 @@ public abstract class JexlParser extends StringParser {
return false;
}
+ protected boolean isVariable(String name) {
+ return frame != null && frame.getSymbol(name) != null;
+ }
+
/**
* Checks whether an identifier is a local variable or argument, ie a symbol, stored in a register.
* @param identifier the identifier
@@ -391,6 +403,11 @@ public abstract class JexlParser extends StringParser {
}
/**
+ * The prefix of a namespace pragma.
+ */
+ protected static final String PRAGMA_JEXLNS = "jexl.namespace.";
+
+ /**
* Adds a pragma declaration.
* @param key the pragma key
* @param value the pragma value
@@ -402,10 +419,35 @@ public abstract class JexlParser extends StringParser {
if (pragmas == null) {
pragmas = new TreeMap<String, Object>();
}
+ // declaring a namespace
+ Predicate<String> ns = getFeatures().namespaceTest();
+ if (ns != null && key.startsWith(PRAGMA_JEXLNS)) {
+ // jexl.namespace.***
+ final String nsname = key.substring(PRAGMA_JEXLNS.length());
+ if (nsname != null && !nsname.isEmpty()) {
+ if (namespaces == null) {
+ namespaces = new HashSet<>();
+ }
+ namespaces.add(nsname);
+ }
+ }
pragmas.put(key, value);
}
/**
+ * Checks whether a name identifies a declared namespace.
+ * @param name the name
+ * @return true if the name qualifies a namespace
+ */
+ protected boolean isDeclaredNamespace(String name) {
+ final Set<String> ns = namespaces;
+ if (ns != null && ns.contains(name)) {
+ return true;
+ }
+ return getFeatures().namespaceTest().test(name);
+ }
+
+ /**
* Declares a local parameter.
* <p> This method creates an new entry in the symbol map. </p>
* @param token the parameter name toekn
diff --git a/src/test/java/org/apache/commons/jexl3/ContextNamespaceTest.java b/src/test/java/org/apache/commons/jexl3/ContextNamespaceTest.java
index 09ca169..fb928cb 100644
--- a/src/test/java/org/apache/commons/jexl3/ContextNamespaceTest.java
+++ b/src/test/java/org/apache/commons/jexl3/ContextNamespaceTest.java
@@ -19,6 +19,11 @@ package org.apache.commons.jexl3;
import org.junit.Assert;
import org.junit.Test;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TreeMap;
+
/**
* Tests JexlContext (advanced) features.
*/
@@ -98,6 +103,36 @@ public class ContextNamespaceTest extends JexlTestCase {
Assert.assertEquals(372., result);
}
+ public static class Context346 extends MapContext {
+ public int func(int y) { return 42 * y;}
+ }
+
+ @Test
+ public void testNamespace346a() throws Exception {
+ JexlContext ctxt = new Context346();
+ final JexlEngine jexl = new JexlBuilder().safe(false).create();
+ String src = "x != null ? x : func(y)";
+ final JexlScript script = jexl.createScript(src,"x","y");
+ Object result = script.execute(ctxt, null, 1);
+ Assert.assertEquals(42, result);
+ result = script.execute(ctxt, 169, -169);
+ Assert.assertEquals(169, result);
+ }
+
+ @Test
+ public void testNamespace346b() throws Exception {
+ JexlContext ctxt = new MapContext();
+ Map<String, Object> ns = new HashMap<String, Object>();
+ ns.put("x", Math.class);
+ ns.put(null, Math.class);
+ final JexlEngine jexl = new JexlBuilder().safe(false).namespaces(ns).create();
+ String src = "x != null ? x : abs(y)";
+ final JexlScript script = jexl.createScript(src,"x","y");
+ Object result = script.execute(ctxt, null, 42);
+ Assert.assertEquals(42, result);
+ result = script.execute(ctxt, 169, -169);
+ Assert.assertEquals(169, result);
+ }
@Test
public void testNamespacePragmaString() throws Exception {