8291598: Matcher.appendReplacement should not create new StringBuilder instances

Reviewed-by: rriggs
This commit is contained in:
Raffaello Giulietti 2023-03-28 17:55:23 +00:00
parent 1683a63a7d
commit ca745cb426

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -25,6 +25,7 @@
package java.util.regex; package java.util.regex;
import java.io.IOException;
import java.util.ConcurrentModificationException; import java.util.ConcurrentModificationException;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map; import java.util.Map;
@ -917,12 +918,16 @@ public final class Matcher implements MatchResult {
*/ */
public Matcher appendReplacement(StringBuffer sb, String replacement) { public Matcher appendReplacement(StringBuffer sb, String replacement) {
checkMatch(); checkMatch();
StringBuilder result = new StringBuilder(); int curLen = sb.length();
appendExpandedReplacement(replacement, result); try {
// Append the intervening text // Append the intervening text
sb.append(text, lastAppendPosition, first); sb.append(text, lastAppendPosition, first);
// Append the match substitution // Append the match substitution
sb.append(result); appendExpandedReplacement(sb, replacement);
} catch (IllegalArgumentException e) {
sb.setLength(curLen);
throw e;
}
lastAppendPosition = last; lastAppendPosition = last;
modCount++; modCount++;
return this; return this;
@ -1004,14 +1009,17 @@ public final class Matcher implements MatchResult {
* @since 9 * @since 9
*/ */
public Matcher appendReplacement(StringBuilder sb, String replacement) { public Matcher appendReplacement(StringBuilder sb, String replacement) {
// If no match, return error
checkMatch(); checkMatch();
StringBuilder result = new StringBuilder(); int curLen = sb.length();
appendExpandedReplacement(replacement, result); try {
// Append the intervening text // Append the intervening text
sb.append(text, lastAppendPosition, first); sb.append(text, lastAppendPosition, first);
// Append the match substitution // Append the match substitution
sb.append(result); appendExpandedReplacement(sb, replacement);
} catch (IllegalArgumentException e) {
sb.setLength(curLen);
throw e;
}
lastAppendPosition = last; lastAppendPosition = last;
modCount++; modCount++;
return this; return this;
@ -1021,8 +1029,8 @@ public final class Matcher implements MatchResult {
* Processes replacement string to replace group references with * Processes replacement string to replace group references with
* groups. * groups.
*/ */
private StringBuilder appendExpandedReplacement( private void appendExpandedReplacement(Appendable app, String replacement) {
String replacement, StringBuilder result) { try {
int cursor = 0; int cursor = 0;
while (cursor < replacement.length()) { while (cursor < replacement.length()) {
char nextChar = replacement.charAt(cursor); char nextChar = replacement.charAt(cursor);
@ -1032,7 +1040,7 @@ public final class Matcher implements MatchResult {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"character to be escaped is missing"); "character to be escaped is missing");
nextChar = replacement.charAt(cursor); nextChar = replacement.charAt(cursor);
result.append(nextChar); app.append(nextChar);
cursor++; cursor++;
} else if (nextChar == '$') { } else if (nextChar == '$') {
// Skip past $ // Skip past $
@ -1045,25 +1053,24 @@ public final class Matcher implements MatchResult {
int refNum = -1; int refNum = -1;
if (nextChar == '{') { if (nextChar == '{') {
cursor++; cursor++;
StringBuilder gsb = new StringBuilder(); int begin = cursor;
while (cursor < replacement.length()) { while (cursor < replacement.length()) {
nextChar = replacement.charAt(cursor); nextChar = replacement.charAt(cursor);
if (ASCII.isLower(nextChar) || if (ASCII.isLower(nextChar) ||
ASCII.isUpper(nextChar) || ASCII.isUpper(nextChar) ||
ASCII.isDigit(nextChar)) { ASCII.isDigit(nextChar)) {
gsb.append(nextChar);
cursor++; cursor++;
} else { } else {
break; break;
} }
} }
if (gsb.length() == 0) if (begin == cursor)
throw new IllegalArgumentException( throw new IllegalArgumentException(
"named capturing group has 0 length name"); "named capturing group has 0 length name");
if (nextChar != '}') if (nextChar != '}')
throw new IllegalArgumentException( throw new IllegalArgumentException(
"named capturing group is missing trailing '}'"); "named capturing group is missing trailing '}'");
String gname = gsb.toString(); String gname = replacement.substring(begin, cursor);
if (ASCII.isDigit(gname.charAt(0))) if (ASCII.isDigit(gname.charAt(0)))
throw new IllegalArgumentException( throw new IllegalArgumentException(
"capturing group name {" + gname + "capturing group name {" + gname +
@ -1101,13 +1108,15 @@ public final class Matcher implements MatchResult {
} }
// Append group // Append group
if (start(refNum) != -1 && end(refNum) != -1) if (start(refNum) != -1 && end(refNum) != -1)
result.append(text, start(refNum), end(refNum)); app.append(text, start(refNum), end(refNum));
} else { } else {
result.append(nextChar); app.append(nextChar);
cursor++; cursor++;
} }
} }
return result; } catch (IOException e) { // cannot happen on String[Buffer|Builder]
throw new AssertionError(e.getMessage());
}
} }
/** /**