You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@velocity.apache.org by cb...@apache.org on 2026/04/18 13:27:29 UTC

(velocity-engine) branch velocity-988 created (now 15a969fd)

This is an automated email from the ASF dual-hosted git repository.

asf-gitbox-commits pushed a change to branch velocity-988
in repository https://gitbox.apache.org/repos/asf/velocity-engine.git


      at 15a969fd Allow an extra dollar after '{' as version 1.7 did: ${$foo}, $!{$foo}

This branch includes the following new commits:

     new 15a969fd Allow an extra dollar after '{' as version 1.7 did: ${$foo}, $!{$foo}

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



(velocity-engine) 01/01: Allow an extra dollar after '{' as version 1.7 did: ${$foo}, $!{$foo}

Posted by cb...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

asf-gitbox-commits pushed a commit to branch velocity-988
in repository https://gitbox.apache.org/repos/asf/velocity-engine.git

commit 15a969fd410a6c6da2ef6ca1cbe1ce26bdfbbfb5
Author: Claude Brisson <cl...@renegat.net>
AuthorDate: Sat Nov 1 21:39:48 2025 +0100

    Allow an extra dollar after '{' as version 1.7 did: ${$foo}, $!{$foo}
---
 .../velocity/runtime/parser/node/ASTReference.java | 11 +++-
 velocity-engine-core/src/main/parser/Parser.jjt    |  2 +-
 .../velocity/test/issues/Velocity988TestCase.java  | 70 ++++++++++++++++++++++
 3 files changed, 79 insertions(+), 4 deletions(-)

diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/ASTReference.java b/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/ASTReference.java
index 08f6a74b..cca12e7e 100644
--- a/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/ASTReference.java
+++ b/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/ASTReference.java
@@ -1045,10 +1045,15 @@ private String getRoot()
         /*
          *  Look for preceding stuff like '#' and '$'
          *  and snip it off, except for the
-         *  last $
+         *  last $ (or the last $ before '{' if it is present)
          */
 
-        int loc1 = t.image.lastIndexOf(rsvc.getParserConfiguration().getDollarChar());
+        int lastLeftCurl = t.image.lastIndexOf('{');
+        if (lastLeftCurl == -1)
+        {
+            lastLeftCurl = t.image.length();
+        }
+        int loc1 = t.image.lastIndexOf(rsvc.getParserConfiguration().getDollarChar(), lastLeftCurl);
 
         /*
          *  if we have extra stuff, loc > 0
@@ -1098,7 +1103,7 @@ private String getRoot()
                 return t.image.substring(2);
             }
         }
-        else if (t.image.equals("${"))
+        else if (t.image.equals("${") || t.image.equals("${$"))
         {
             /*
              *  ex: ${provider.Title}
diff --git a/velocity-engine-core/src/main/parser/Parser.jjt b/velocity-engine-core/src/main/parser/Parser.jjt
index 4e790735..b10b3922 100644
--- a/velocity-engine-core/src/main/parser/Parser.jjt
+++ b/velocity-engine-core/src/main/parser/Parser.jjt
@@ -1472,7 +1472,7 @@ TOKEN:
 <PRE_REFERENCE,PRE_OLD_REFERENCE,REFERENCE,REFMODIFIER,OLD_REFMODIFIER,REFMOD3>
 TOKEN :
 {
-    <LCURLY: "{">
+      <LCURLY: "{" ( "${parser.char.dollar}" )? > /* Support an extra '$' inside {}, as in ${$foo}, for B.C. with 1.7 */
     {
         ++curlyLevel;
     }
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity988TestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity988TestCase.java
new file mode 100644
index 00000000..ed14258c
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity988TestCase.java
@@ -0,0 +1,70 @@
+package org.apache.velocity.test.issues;
+
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.test.BaseTestCase;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class Velocity988TestCase extends BaseTestCase {
+    public Velocity988TestCase(String name) {
+        super(name);
+    }
+
+    @Override
+    protected void setUpContext(VelocityContext context)
+    {
+        context.put("foo", "bar");
+
+        Map<String, String> submap = new HashMap<>();
+        submap.put("@StudyEventRepeatKey", "123");
+        Map<String, Object> map = new HashMap<>();
+        map.put("StudyEventData", submap);
+        context.put("SubjectData", map);
+    }
+
+    public void testSimpleNoSilent()
+    {
+        assertEvalEquals("bar", "${$foo}");
+    }
+
+    public void testSimpleSilent()
+    {
+        assertEvalEquals("bar", "$!{$foo}");
+    }
+
+    public void testSimplePrefix()
+    {
+        assertEvalEquals("#bar", "#${$foo}");
+    }
+
+    private static final String EXPECTED =
+            "{\n" +
+            " \"eventRepeatKey\": \"123\",\n" +
+            " \"otherField\": \"someValue\"\n" +
+            "}";
+
+    public void testComplexWithoutExtraDollar()
+    {
+        String template =
+                "{\n" +
+                        " #if($!{SubjectData.StudyEventData['@StudyEventRepeatKey']})\n" +
+                        " \"eventRepeatKey\": \"$SubjectData.StudyEventData['@StudyEventRepeatKey']\",\n" +
+                        " #end\n" +
+                        " \"otherField\": \"someValue\"\n" +
+                        "}";
+        assertEvalEquals(EXPECTED, template);
+    }
+
+    public void testComplexWithExtraDollar()
+    {
+        String template =
+                "{\n" +
+                " #if($!{$SubjectData.StudyEventData['@StudyEventRepeatKey']})\n" +
+                " \"eventRepeatKey\": \"$SubjectData.StudyEventData['@StudyEventRepeatKey']\",\n" +
+                " #end\n" +
+                " \"otherField\": \"someValue\"\n" +
+                "}";
+        assertEvalEquals(EXPECTED, template);
+    }
+}