This is an automated email from the ASF dual-hosted git repository. ggregory pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-text.git
The following commit(s) were added to refs/heads/master by this push: new 56972a8 [TEXT-178] StringSubstitutor incorrectly removes some escape characters. 56972a8 is described below commit 56972a812810fef209759917d1ef27255071db96 Author: Gary Gregory <garydgreg...@gmail.com> AuthorDate: Thu Jul 16 09:25:11 2020 -0400 [TEXT-178] StringSubstitutor incorrectly removes some escape characters. - Return method as private as it was in 1.8 (but with a different return type.) - Make a new static class private. - Drop javancss-maven-plugin as it throws javancss.parser.ParseException for Java 8's default methods. --- pom.xml | 4 - src/changes/changes.xml | 5 +- .../org/apache/commons/text/StringSubstitutor.java | 220 +++++++++++---------- .../apache/commons/text/StringSubstitutorTest.java | 2 - 4 files changed, 115 insertions(+), 116 deletions(-) diff --git a/pom.xml b/pom.xml index 0f18fa1..68e2bb9 100644 --- a/pom.xml +++ b/pom.xml @@ -316,10 +316,6 @@ </tagListOptions> </configuration> </plugin> - <plugin> - <groupId>org.codehaus.mojo</groupId> - <artifactId>javancss-maven-plugin</artifactId> - </plugin> </plugins> </reporting> diff --git a/src/changes/changes.xml b/src/changes/changes.xml index ecb8286..17f3a74 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -48,11 +48,12 @@ The <action> type attribute can be add,update,fix,remove. <release version="1.9" date="2019-MM-DD" description="Release 1.8.1"> <action issue="TEXT-166" type="fix" dev="kinow" due-to="Mikko Maunu">Removed non-existing parameter from Javadocs and spelled out parameters in throws.</action> <action issue="TEXT-149" type="fix" dev="kinow" due-to="Yuji Konishi">StringEscapeUtils.unescapeCsv doesn't remove quotes at begin and end of string.</action> - <action issue="TEXT-174" type="fix" dev="ggregory" due-to="furkilic">ScriptStringLookup does not accept ":" #126.</action> + <action issue="TEXT-174" type="fix" dev="ggregory" due-to="furkilic">ScriptStringLookup does not accept ":" #126.</action> + <action issue="TEXT-178" type="fix" dev="ggregory" due-to="Gary Gregory">StringSubstitutor incorrectly removes some escape characters.</action> <action type="update" dev="ggregory" due-to="Johan Hammar">[javadoc] Fix compiler warnings in Java code example in Javadoc #124.</action> + <action issue="TEXT-177" type="update" dev="ggregory" due-to="Gary Gregory">Update from Apache Commons Lang 3.9 to 3.10.</action> <action type="add" dev="ggregory" due-to="Gary Gregory">Add StringMatcher.size().</action> <action type="add" dev="ggregory" due-to="Gary Gregory">Refactor TextStringBuilder.readFrom(Readable), extracting readFrom(CharBuffer) and readFrom(Reader).</action> - <action issue="TEXT-177" type="update" dev="ggregory" due-to="Gary Gregory">Update from Apache Commons Lang 3.9 to 3.10.</action> <action type="add" dev="ggregory" due-to="Gary Gregory">Add BiStringLookup and implementation BiFunctionStringLookup.</action> <action type="add" dev="ggregory" due-to="Gary Gregory">Add org.apache.commons.text.lookup.StringLookupFactory.functionStringLookup(Function<String, V>).</action> <action type="add" dev="ggregory" due-to="Gary Gregory">Add org.apache.commons.text.matcher.StringMatcher.isMatch(CharSequence, int).</action> diff --git a/src/main/java/org/apache/commons/text/StringSubstitutor.java b/src/main/java/org/apache/commons/text/StringSubstitutor.java index 5057351..6b6ebae 100644 --- a/src/main/java/org/apache/commons/text/StringSubstitutor.java +++ b/src/main/java/org/apache/commons/text/StringSubstitutor.java @@ -106,7 +106,7 @@ import org.apache.commons.text.matcher.StringMatcherFactory; * Map<String, String> valuesMap = new HashMap<>(); * valuesMap.put("animal", "quick brown fox"); * valuesMap.put("target", "lazy dog"); - * String templateString = "The ${animal} jumped over the ${target}. ${undefined.number:-1234567890}."; + * String templateString = "The ${animal} jumped over the ${target} ${undefined.number:-1234567890} times."; * * // Build StringSubstitutor * StringSubstitutor sub = new StringSubstitutor(valuesMap); @@ -120,7 +120,7 @@ import org.apache.commons.text.matcher.StringMatcherFactory; * </p> * * <pre> - * "The quick brown fox jumped over the lazy dog. 1234567890." + * "The quick brown fox jumped over the lazy dog 1234567890 times." * </pre> * * <p> @@ -222,7 +222,7 @@ public class StringSubstitutor { * * @since 1.9 */ - public static final class Result { + private static final class Result { /** Whether the buffer is altered. */ public final boolean altered; @@ -230,14 +230,15 @@ public class StringSubstitutor { /** The length of change. */ public final int lengthChange; - /** The starting position of last variable seen. */ - public final int startVarPos; - - private Result(final boolean altered, final int lengthChange, final int startVarPos) { + private Result(final boolean altered, final int lengthChange) { super(); this.altered = altered; this.lengthChange = lengthChange; - this.startVarPos = startVarPos; + } + + @Override + public String toString() { + return "Result [altered=" + altered + ", lengthChange=" + lengthChange + "]"; } } @@ -1332,7 +1333,7 @@ public class StringSubstitutor { * @throws IllegalArgumentException if variable is not found when its allowed to throw exception * @since 1.9 */ - protected Result substitute(final TextStringBuilder builder, final int offset, final int length, + private Result substitute(final TextStringBuilder builder, final int offset, final int length, List<String> priorVariables) { Objects.requireNonNull(builder, "builder"); final StringMatcher prefixMatcher = getVariablePrefixMatcher(); @@ -1348,8 +1349,8 @@ public class StringSubstitutor { int lengthChange = 0; int bufEnd = offset + length; int pos = offset; - int startVarPos = -1; - while (pos < bufEnd) { + int escPos = -1; + outter: while (pos < bufEnd) { final int startMatchLen = prefixMatcher.isMatch(builder, pos, offset, bufEnd); if (startMatchLen == 0) { pos++; @@ -1362,115 +1363,118 @@ public class StringSubstitutor { pos++; continue; } - // delete escape - builder.deleteCharAt(pos - 1); - lengthChange--; - altered = true; - bufEnd--; - } else { - // find suffix - startVarPos = pos; - final int startPos = pos; - pos += startMatchLen; - int endMatchLen = 0; - int nestedVarCount = 0; - while (pos < bufEnd) { - if (substitutionInVariablesEnabled - && prefixMatcher.isMatch(builder, pos, offset, bufEnd) != 0) { - // found a nested variable start - endMatchLen = prefixMatcher.isMatch(builder, pos, offset, bufEnd); - nestedVarCount++; - pos += endMatchLen; - continue; - } + // mark esc ch for deletion if we find a complete variable + escPos = pos - 1; + } + // find suffix + int startPos = pos; + pos += startMatchLen; + int endMatchLen = 0; + int nestedVarCount = 0; + while (pos < bufEnd) { + if (substitutionInVariablesEnabled && prefixMatcher.isMatch(builder, pos, offset, bufEnd) != 0) { + // found a nested variable start + endMatchLen = prefixMatcher.isMatch(builder, pos, offset, bufEnd); + nestedVarCount++; + pos += endMatchLen; + continue; + } - endMatchLen = suffixMatcher.isMatch(builder, pos, offset, bufEnd); - if (endMatchLen == 0) { - pos++; - } else { - // found variable end marker - if (nestedVarCount == 0) { - String varNameExpr = builder.toString(startPos + startMatchLen, - pos - startPos - startMatchLen); - if (substitutionInVariablesEnabled) { - final TextStringBuilder bufName = new TextStringBuilder(varNameExpr); - substitute(bufName, 0, bufName.length()); - varNameExpr = bufName.toString(); - } - pos += endMatchLen; - final int endPos = pos; - - String varName = varNameExpr; - String varDefaultValue = null; - - if (valueDelimMatcher != null) { - final char[] varNameExprChars = varNameExpr.toCharArray(); - int valueDelimiterMatchLen = 0; - for (int i = 0; i < varNameExprChars.length; i++) { - // if there's any nested variable when nested variable substitution disabled, - // then stop resolving name and default value. - if (!substitutionInVariablesEnabled && prefixMatcher.isMatch(varNameExprChars, - i, i, varNameExprChars.length) != 0) { - break; - } - if (valueDelimMatcher.isMatch(varNameExprChars, i, 0, - varNameExprChars.length) != 0) { - valueDelimiterMatchLen = valueDelimMatcher.isMatch(varNameExprChars, i, 0, - varNameExprChars.length); - varName = varNameExpr.substring(0, i); - varDefaultValue = varNameExpr.substring(i + valueDelimiterMatchLen); - break; - } + endMatchLen = suffixMatcher.isMatch(builder, pos, offset, bufEnd); + if (endMatchLen == 0) { + pos++; + } else { + // found variable end marker + if (nestedVarCount == 0) { + if (escPos >= 0) { + // delete escape + builder.deleteCharAt(escPos); + escPos = -1; + lengthChange--; + altered = true; + bufEnd--; + pos = startPos + 1; + startPos--; + continue outter; + } + // get var name + String varNameExpr = builder.toString(startPos + startMatchLen, + pos - startPos - startMatchLen); + if (substitutionInVariablesEnabled) { + final TextStringBuilder bufName = new TextStringBuilder(varNameExpr); + substitute(bufName, 0, bufName.length()); + varNameExpr = bufName.toString(); + } + pos += endMatchLen; + final int endPos = pos; + + String varName = varNameExpr; + String varDefaultValue = null; + + if (valueDelimMatcher != null) { + final char[] varNameExprChars = varNameExpr.toCharArray(); + int valueDelimiterMatchLen = 0; + for (int i = 0; i < varNameExprChars.length; i++) { + // if there's any nested variable when nested variable substitution disabled, + // then stop resolving name and default value. + if (!substitutionInVariablesEnabled && prefixMatcher.isMatch(varNameExprChars, i, i, + varNameExprChars.length) != 0) { + break; + } + if (valueDelimMatcher.isMatch(varNameExprChars, i, 0, + varNameExprChars.length) != 0) { + valueDelimiterMatchLen = valueDelimMatcher.isMatch(varNameExprChars, i, 0, + varNameExprChars.length); + varName = varNameExpr.substring(0, i); + varDefaultValue = varNameExpr.substring(i + valueDelimiterMatchLen); + break; } } + } - // on the first call initialize priorVariables - if (priorVariables == null) { - priorVariables = new ArrayList<>(); - priorVariables.add(builder.toString(offset, length)); - } + // on the first call initialize priorVariables + if (priorVariables == null) { + priorVariables = new ArrayList<>(); + priorVariables.add(builder.toString(offset, length)); + } - // handle cyclic substitution - checkCyclicSubstitution(varName, priorVariables); - priorVariables.add(varName); + // handle cyclic substitution + checkCyclicSubstitution(varName, priorVariables); + priorVariables.add(varName); - // resolve the variable - String varValue = resolveVariable(varName, builder, startPos, endPos); - if (varValue == null) { - varValue = varDefaultValue; - } - startVarPos = -1; - if (varValue != null) { - startVarPos = startPos; - final int varLen = varValue.length(); - builder.replace(startPos, endPos, varValue); - altered = true; - int change = 0; - if (!substitutionInValuesDisabled) { // recursive replace - change = substitute(builder, startPos, varLen, priorVariables).lengthChange; - } - change = change + varLen - (endPos - startPos); - pos += change; - bufEnd += change; - lengthChange += change; - } else if (undefinedVariableException) { - throw new IllegalArgumentException(String.format( - "Cannot resolve variable '%s' (enableSubstitutionInVariables=%s).", varName, - substitutionInVariablesEnabled)); + // resolve the variable + String varValue = resolveVariable(varName, builder, startPos, endPos); + if (varValue == null) { + varValue = varDefaultValue; + } + if (varValue != null) { + final int varLen = varValue.length(); + builder.replace(startPos, endPos, varValue); + altered = true; + int change = 0; + if (!substitutionInValuesDisabled) { // recursive replace + change = substitute(builder, startPos, varLen, priorVariables).lengthChange; } - - // remove variable from the cyclic stack - priorVariables.remove(priorVariables.size() - 1); - startVarPos = -1; - break; + change = change + varLen - (endPos - startPos); + pos += change; + bufEnd += change; + lengthChange += change; + } else if (undefinedVariableException) { + throw new IllegalArgumentException( + String.format("Cannot resolve variable '%s' (enableSubstitutionInVariables=%s).", + varName, substitutionInVariablesEnabled)); } - nestedVarCount--; - pos += endMatchLen; + + // remove variable from the cyclic stack + priorVariables.remove(priorVariables.size() - 1); + break; } + nestedVarCount--; + pos += endMatchLen; } } } } - return new Result(altered, lengthChange, startVarPos); + return new Result(altered, lengthChange); } } diff --git a/src/test/java/org/apache/commons/text/StringSubstitutorTest.java b/src/test/java/org/apache/commons/text/StringSubstitutorTest.java index a8e55cd..e5cc903 100644 --- a/src/test/java/org/apache/commons/text/StringSubstitutorTest.java +++ b/src/test/java/org/apache/commons/text/StringSubstitutorTest.java @@ -269,7 +269,6 @@ public class StringSubstitutorTest { * Tests interpolation with weird boundary patterns. */ @Test - @Disabled public void testReplace_JiraText178_WeirdPattenrs2() throws IOException { doReplace("${1}", "$${${a}}", false); } @@ -287,7 +286,6 @@ public class StringSubstitutorTest { * Tests interpolation with weird boundary patterns. */ @Test - @Disabled public void testReplace_JiraText178_WeirdPatterns1() throws IOException { doNotReplace("$${"); doNotReplace("$${a");