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 44c4345 JEXL-348: added a syntactic kludge to allow user
disambiguation in the x? ns:f() : y case, refined logic of namespace
declaration check, more tests
44c4345 is described below
commit 44c434532f654d3a11230a66d0f488cb9ee434b0
Author: henrib <[email protected]>
AuthorDate: Wed Jun 2 22:04:23 2021 +0200
JEXL-348: added a syntactic kludge to allow user disambiguation in the x?
ns:f() : y case, refined logic of namespace declaration check, more tests
---
.../apache/commons/jexl3/parser/JexlParser.java | 32 ++++++++----
.../org/apache/commons/jexl3/parser/Parser.jjt | 31 ++++++-----
.../apache/commons/jexl3/ContextNamespaceTest.java | 61 ++++++++++++++++------
3 files changed, 81 insertions(+), 43 deletions(-)
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 aaea256..3116916 100644
--- a/src/main/java/org/apache/commons/jexl3/parser/JexlParser.java
+++ b/src/main/java/org/apache/commons/jexl3/parser/JexlParser.java
@@ -29,14 +29,12 @@ 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;
@@ -65,7 +63,7 @@ public abstract class JexlParser extends StringParser {
/**
* When parsing inner functions/lambda, need to stack the scope (sic).
*/
- protected final Deque<Scope> frames = new ArrayDeque<Scope>();
+ protected final Deque<Scope> frames = new ArrayDeque<>();
/**
* The list of pragma declarations.
*/
@@ -81,7 +79,7 @@ public abstract class JexlParser extends StringParser {
/**
* Stack of parsing loop counts.
*/
- protected final Deque<Integer> loopCounts = new ArrayDeque<Integer>();
+ protected final Deque<Integer> loopCounts = new ArrayDeque<>();
/**
* The current lexical block.
*/
@@ -89,7 +87,7 @@ public abstract class JexlParser extends StringParser {
/**
* Stack of lexical blocks.
*/
- protected final Deque<LexicalUnit> blocks = new ArrayDeque<LexicalUnit>();
+ protected final Deque<LexicalUnit> blocks = new ArrayDeque<>();
/**
* A lexical unit is the container defining local symbols and their
@@ -134,6 +132,7 @@ public abstract class JexlParser extends StringParser {
blocks.clear();
block = null;
}
+
/**
* Utility function to create '.' separated string from a list of string.
* @param lstr the list of strings
@@ -441,15 +440,28 @@ public abstract class JexlParser extends StringParser {
/**
* Checks whether a name identifies a declared namespace.
- * @param name the name
+ * @param token the namespace token
* @return true if the name qualifies a namespace
*/
- protected boolean isDeclaredNamespace(String name) {
- final Set<String> ns = namespaces;
- if (ns != null && ns.contains(name)) {
+ protected boolean isDeclaredNamespace(final Token token, final Token
colon) {
+ // syntactic hint, the namespace sticks to the colon
+ if (colon != null && ":".equals(colon.image) && colon.beginColumn - 1
== token.endColumn) {
return true;
}
- return getFeatures().namespaceTest().test(name);
+ // if name is shared with a variable name, use syntactic hint
+ String name = token.image;
+ if (!isVariable(name)) {
+ final Set<String> ns = namespaces;
+ // declared through local pragma ?
+ if (ns != null && ns.contains(name)) {
+ return true;
+ }
+ // declared through engine features ?
+ if (getFeatures().namespaceTest().test(name)) {
+ return true;
+ }
+ }
+ return false;
}
/**
diff --git a/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt
b/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt
index cb782e8..4314dc0 100644
--- a/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt
+++ b/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt
@@ -28,7 +28,7 @@ options
TRACK_TOKENS=true;
CACHE_TOKENS=true;
ERROR_REPORTING=false;
- //DEBUG_PARSER=true;
+ // DEBUG_PARSER=true;
//DEBUG_TOKEN_MANAGER=true;
}
@@ -64,8 +64,8 @@ public final class Parser extends JexlParser
script.jjtSetValue(info.detach());
script.setFeatures(jexlFeatures);
script.setPragmas(pragmas != null
- ?
Collections.<String,Object>unmodifiableMap(pragmas)
- : Collections.<String,Object>emptyMap());
+ ? Collections.unmodifiableMap(pragmas)
+ : Collections.emptyMap());
return script;
} catch (TokenMgrError xtme) {
throw new JexlException.Tokenization(info, xtme).clean();
@@ -302,7 +302,7 @@ ASTJexlScript JexlScript(Scope frame) : {
{
pushUnit(jjtThis);
}
- ( ( Statement() )*) <EOF>
+ (Statement())* <EOF>
{
popUnit(jjtThis);
return jjtThis.script();
@@ -331,7 +331,7 @@ void Annotation() #Annotation :
t=<ANNOTATION> (LOOKAHEAD(<LPAREN>) Arguments() )? {
jjtThis.setName(t.image); }
}
-void AnnotatedStatement() #AnnotatedStatement() : {}
+void AnnotatedStatement() #AnnotatedStatement : {}
{
(LOOKAHEAD(<ANNOTATION>) Annotation())+ (LOOKAHEAD(1) Block() |
Statement())
}
@@ -507,12 +507,11 @@ void ConditionalExpression() #void : {}
{
ConditionalOrExpression()
(
- <QMARK> (LOOKAHEAD(<IDENTIFIER> <COLON>,
- { isVariable(getToken(1).image) ||
!isDeclaredNamespace(getToken(1).image) })
- Identifier(true)
- |
- Expression()
- ) <COLON> Expression() #TernaryNode(3)
+ <QMARK> (LOOKAHEAD(<IDENTIFIER> <COLON> , {
!isDeclaredNamespace(getToken(1), getToken(2)) })
+ Identifier(true)
+ |
+ Expression()
+ ) <COLON> Expression() #TernaryNode(3)
|
<ELVIS> Expression() #TernaryNode(2)
|
@@ -752,7 +751,7 @@ void RegexLiteral() :
}
-void ExtendedLiteral() #ExtendedLiteral() : {}
+void ExtendedLiteral() #ExtendedLiteral : {}
{
<ELIPSIS>
}
@@ -814,7 +813,7 @@ void FunctionCall() #void : {}
LOOKAHEAD(2) Identifier(true) Arguments() #FunctionNode(2)
}
-void Constructor() #ConstructorNode() : {}
+void Constructor() #ConstructorNode : {}
{
<NEW> <LPAREN> Expression() ( <COMMA> Expression() )* <RPAREN>
}
@@ -833,7 +832,7 @@ void Parameters() #void : {}
}
-void LambdaLookahead() #void() : {}
+void LambdaLookahead() #void : {}
{
<FUNCTION> Parameters()
|
@@ -842,7 +841,7 @@ void LambdaLookahead() #void() : {}
Parameter() <LAMBDA>
}
-void Lambda() #JexlLambda() :
+void Lambda() #JexlLambda :
{
pushFrame();
}
@@ -908,7 +907,7 @@ void MemberAccess() #void : {}
void ReferenceExpression() #MethodNode(>1) : {}
{
- ( <LPAREN> Expression() <RPAREN> #ReferenceExpression(1) ) (
LOOKAHEAD(<LPAREN>) Arguments() )*
+ <LPAREN> Expression() <RPAREN> #ReferenceExpression(1) (
LOOKAHEAD(<LPAREN>) Arguments() )*
}
void PrimaryExpression() #void : {}
diff --git a/src/test/java/org/apache/commons/jexl3/ContextNamespaceTest.java
b/src/test/java/org/apache/commons/jexl3/ContextNamespaceTest.java
index 43f2bfd..c06b8bb 100644
--- a/src/test/java/org/apache/commons/jexl3/ContextNamespaceTest.java
+++ b/src/test/java/org/apache/commons/jexl3/ContextNamespaceTest.java
@@ -154,17 +154,20 @@ public class ContextNamespaceTest extends JexlTestCase {
Map<String, Object> ns = new HashMap<String, Object>();
ns.put("ns", Ns348.class);
final JexlEngine jexl = new
JexlBuilder().safe(false).namespaces(ns).create();
- run348ab(jexl, ctxt);
- run348cd(jexl, ctxt);
+ run348a(jexl, ctxt);
+ run348b(jexl, ctxt);
+ run348c(jexl, ctxt);
+ run348d(jexl, ctxt);
}
- @Ignore
@Test
public void testNamespace348b() throws Exception {
JexlContext ctxt = new ContextNs348();
final JexlEngine jexl = new JexlBuilder().safe(false).create();
- run348ab(jexl, ctxt);
- run348cd(jexl, ctxt);
+ run348a(jexl, ctxt, "ns:"); // no space for ns name
+ run348b(jexl, ctxt, "ns:"); // no space for ns name
+ run348c(jexl, ctxt);
+ run348d(jexl, ctxt);
}
@Test
@@ -175,8 +178,10 @@ public class ContextNamespaceTest extends JexlTestCase {
JexlFeatures f = new JexlFeatures();
f.namespaceTest((n)->true);
final JexlEngine jexl = new
JexlBuilder().namespaces(ns).features(f).safe(false).create();
- run348ab(jexl, ctxt);
- run348cd(jexl, ctxt);
+ run348a(jexl, ctxt);
+ run348b(jexl, ctxt);
+ run348c(jexl, ctxt);
+ run348d(jexl, ctxt);
}
@Test
@@ -185,44 +190,66 @@ public class ContextNamespaceTest extends JexlTestCase {
JexlFeatures f = new JexlFeatures();
f.namespaceTest((n)->true);
final JexlEngine jexl = new
JexlBuilder().features(f).safe(false).create();
- run348ab(jexl, ctxt);
- run348cd(jexl, ctxt);
+ run348a(jexl, ctxt);
+ run348b(jexl, ctxt);
+ run348c(jexl, ctxt);
+ run348d(jexl, ctxt);
}
- private void run348ab(JexlEngine jexl, JexlContext ctxt) {
- String src = "empty(x) ? ns:func(y) : z";
+ private void run348a(JexlEngine jexl, JexlContext ctxt) {
+ run348a(jexl, ctxt, "ns : ");
+ }
+ private void run348a(JexlEngine jexl, JexlContext ctxt, String ns) {
+ String src = "empty(x) ? "+ns+"func(y) : z";
// local vars
JexlScript script = jexl.createScript(src, "x", "y", "z");
Object result = script.execute(ctxt, null, 1, 169);
Assert.assertEquals(42, result);
result = script.execute(ctxt, "42", 1, 169);
Assert.assertEquals(169, result);
+ }
+
+ private void run348b(JexlEngine jexl, JexlContext ctxt) {
+ run348b(jexl, ctxt, "ns : ");
+ }
+ private void run348b(JexlEngine jexl, JexlContext ctxt, String ns) {
+ String src = "empty(x) ? "+ns+"func(y) : z";
// global vars
- script = jexl.createScript(src);
+ JexlScript script = jexl.createScript(src);
ctxt.set("x", null);
ctxt.set("y", 1);
ctxt.set("z", 169);
- result = script.execute(ctxt);
+ Object result = script.execute(ctxt);
Assert.assertEquals(42, result);
ctxt.set("x", "42");
result = script.execute(ctxt);
Assert.assertEquals(169, result);
}
- private void run348cd(JexlEngine jexl, JexlContext ctxt) {
- String src = "empty(x) ? z : ns:func(y)";
+ private void run348c(JexlEngine jexl, JexlContext ctxt) {
+ run348c(jexl, ctxt, "ns : ");
+ }
+ private void run348c(JexlEngine jexl, JexlContext ctxt, String ns) {
+ String src = "empty(x) ? z : "+ns+"func(y)";
// local vars
JexlScript script = jexl.createScript(src, "x", "z", "y");
Object result = script.execute(ctxt, null, 169, 1);
Assert.assertEquals(169, result);
result = script.execute(ctxt, "42", 169, 1);
Assert.assertEquals(42, result);
+ }
+
+ private void run348d(JexlEngine jexl, JexlContext ctxt) {
+ run348d(jexl, ctxt, "ns : ");
+ }
+ private void run348d(JexlEngine jexl, JexlContext ctxt, String ns) {
+ String src = "empty(x) ? z : "+ns+"func(y)";
// global vars
- script = jexl.createScript(src);
+ JexlScript script = jexl.createScript(src);
ctxt.set("x", null);
ctxt.set("z", 169);
ctxt.set("y", 1);
- result = script.execute(ctxt);
+ Object result = script.execute(ctxt);
Assert.assertEquals(169, result);
ctxt.set("x", "42");
result = script.execute(ctxt);