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);

Reply via email to