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

garydgregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-lang.git


The following commit(s) were added to refs/heads/master by this push:
     new cb3a20eaf keep StrSubstitutor from treating a value's trailing $ as an 
escape (#1834)
cb3a20eaf is described below

commit cb3a20eafad7baf2d206bb1083e134c22021a574
Author: alhuda <[email protected]>
AuthorDate: Sat Oct 24 17:52:47 2026 +0530

    keep StrSubstitutor from treating a value's trailing $ as an escape (#1834)
    
    After a variable is replaced, substituteRecursive resumes scanning at
    the end of the inserted value, and the escape check looks at the char
    before the next prefix bounded only by the scan offset. A value ending
    in the escape char therefore escaped the variable that follows it in
    the template: the value lost its last char and the next variable was
    left unresolved, e.g. price=5$ makes "${price}${x}" return "5${x}"
    instead of "5$X".
    
    Only accept an escape char at or after the end of the last
    substitution.
---
 .../java/org/apache/commons/lang3/text/StrSubstitutor.java    |  5 ++++-
 .../org/apache/commons/lang3/text/StrSubstitutorTest.java     | 11 +++++++++++
 2 files changed, 15 insertions(+), 1 deletion(-)

diff --git a/src/main/java/org/apache/commons/lang3/text/StrSubstitutor.java 
b/src/main/java/org/apache/commons/lang3/text/StrSubstitutor.java
index a640b0313..0d87394ab 100644
--- a/src/main/java/org/apache/commons/lang3/text/StrSubstitutor.java
+++ b/src/main/java/org/apache/commons/lang3/text/StrSubstitutor.java
@@ -1178,12 +1178,14 @@ private int substituteRecursive(final StrBuilder buf, 
final int offset, final in
         char[] chars = buf.buffer;
         int bufEnd = offset + length;
         int pos = offset;
+        // an escape character must come from the text being scanned, not from 
the end of a substituted value
+        int escapeStart = offset;
         while (pos < bufEnd) {
             final int startMatchLen = pfxMatcher.isMatch(chars, pos, offset, 
bufEnd);
             if (startMatchLen == 0) {
                 pos++;
             } else // found variable start marker
-            if (pos > offset && chars[pos - 1] == escape) {
+            if (pos > escapeStart && chars[pos - 1] == escape) {
                 // escaped
                 if (preserveEscapes) {
                     pos++;
@@ -1264,6 +1266,7 @@ private int substituteRecursive(final StrBuilder buf, 
final int offset, final in
                                 int change = substitute(buf, startPos, varLen, 
priorVariables);
                                 change = change + varLen - (endPos - startPos);
                                 pos += change;
+                                escapeStart = pos;
                                 bufEnd += change;
                                 lengthChange += change;
                                 chars = buf.buffer; // in case buffer was 
altered
diff --git 
a/src/test/java/org/apache/commons/lang3/text/StrSubstitutorTest.java 
b/src/test/java/org/apache/commons/lang3/text/StrSubstitutorTest.java
index 8c3f02274..2b9c70de0 100644
--- a/src/test/java/org/apache/commons/lang3/text/StrSubstitutorTest.java
+++ b/src/test/java/org/apache/commons/lang3/text/StrSubstitutorTest.java
@@ -440,6 +440,17 @@ void testReplaceEscaping() {
         doTestReplace("The ${animal} jumps over the lazy dog.", "The 
$${animal} jumps over the ${target}.", true);
     }
 
+    /**
+     * Tests that an escape character at the end of a substituted value does 
not escape the next variable.
+     */
+    @Test
+    void testReplaceValueEndingWithEscape() {
+        values.put("price", "5$");
+        doTestReplace("The price is 5$lazy dog.", "The price is 
${price}${target}.", true);
+        values.put("animal", "$");
+        doTestReplace("The $lazy dog jumps over the $${target}.", "The 
${animal}${target} jumps over the ${animal}$${target}.", true);
+    }
+
     /**
      * Tests when no incomplete prefix.
      */

Reply via email to