Skip to content

Commit

Permalink
8291598: Matcher.appendReplacement should not create new StringBuilde…
Browse files Browse the repository at this point in the history
…r instances

Reviewed-by: rriggs
  • Loading branch information
rgiulietti committed Mar 28, 2023
1 parent 1683a63 commit ca745cb
Showing 1 changed file with 100 additions and 91 deletions.
191 changes: 100 additions & 91 deletions src/java.base/share/classes/java/util/regex/Matcher.java
Original file line number Diff line number Diff line change
@@ -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.
*
* This code is free software; you can redistribute it and/or modify it
Expand All @@ -25,6 +25,7 @@

package java.util.regex;

import java.io.IOException;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.Map;
Expand Down Expand Up @@ -917,12 +918,16 @@ public static String quoteReplacement(String s) {
*/
public Matcher appendReplacement(StringBuffer sb, String replacement) {
checkMatch();
StringBuilder result = new StringBuilder();
appendExpandedReplacement(replacement, result);
// Append the intervening text
sb.append(text, lastAppendPosition, first);
// Append the match substitution
sb.append(result);
int curLen = sb.length();
try {
// Append the intervening text
sb.append(text, lastAppendPosition, first);
// Append the match substitution
appendExpandedReplacement(sb, replacement);
} catch (IllegalArgumentException e) {
sb.setLength(curLen);
throw e;
}
lastAppendPosition = last;
modCount++;
return this;
Expand Down Expand Up @@ -1004,14 +1009,17 @@ public Matcher appendReplacement(StringBuffer sb, String replacement) {
* @since 9
*/
public Matcher appendReplacement(StringBuilder sb, String replacement) {
// If no match, return error
checkMatch();
StringBuilder result = new StringBuilder();
appendExpandedReplacement(replacement, result);
// Append the intervening text
sb.append(text, lastAppendPosition, first);
// Append the match substitution
sb.append(result);
int curLen = sb.length();
try {
// Append the intervening text
sb.append(text, lastAppendPosition, first);
// Append the match substitution
appendExpandedReplacement(sb, replacement);
} catch (IllegalArgumentException e) {
sb.setLength(curLen);
throw e;
}
lastAppendPosition = last;
modCount++;
return this;
Expand All @@ -1021,93 +1029,94 @@ public Matcher appendReplacement(StringBuilder sb, String replacement) {
* Processes replacement string to replace group references with
* groups.
*/
private StringBuilder appendExpandedReplacement(
String replacement, StringBuilder result) {
int cursor = 0;
while (cursor < replacement.length()) {
char nextChar = replacement.charAt(cursor);
if (nextChar == '\\') {
cursor++;
if (cursor == replacement.length())
throw new IllegalArgumentException(
"character to be escaped is missing");
nextChar = replacement.charAt(cursor);
result.append(nextChar);
cursor++;
} else if (nextChar == '$') {
// Skip past $
cursor++;
// Throw IAE if this "$" is the last character in replacement
if (cursor == replacement.length())
throw new IllegalArgumentException(
"Illegal group reference: group index is missing");
nextChar = replacement.charAt(cursor);
int refNum = -1;
if (nextChar == '{') {
private void appendExpandedReplacement(Appendable app, String replacement) {
try {
int cursor = 0;
while (cursor < replacement.length()) {
char nextChar = replacement.charAt(cursor);
if (nextChar == '\\') {
cursor++;
StringBuilder gsb = new StringBuilder();
while (cursor < replacement.length()) {
nextChar = replacement.charAt(cursor);
if (ASCII.isLower(nextChar) ||
ASCII.isUpper(nextChar) ||
ASCII.isDigit(nextChar)) {
gsb.append(nextChar);
cursor++;
} else {
break;
}
}
if (gsb.length() == 0)
throw new IllegalArgumentException(
"named capturing group has 0 length name");
if (nextChar != '}')
throw new IllegalArgumentException(
"named capturing group is missing trailing '}'");
String gname = gsb.toString();
if (ASCII.isDigit(gname.charAt(0)))
throw new IllegalArgumentException(
"capturing group name {" + gname +
"} starts with digit character");
if (!namedGroups().containsKey(gname))
if (cursor == replacement.length())
throw new IllegalArgumentException(
"No group with name {" + gname + "}");
refNum = namedGroups().get(gname);
"character to be escaped is missing");
nextChar = replacement.charAt(cursor);
app.append(nextChar);
cursor++;
} else {
// The first number is always a group
refNum = nextChar - '0';
if ((refNum < 0) || (refNum > 9))
throw new IllegalArgumentException(
"Illegal group reference");
} else if (nextChar == '$') {
// Skip past $
cursor++;
// Capture the largest legal group string
boolean done = false;
while (!done) {
if (cursor >= replacement.length()) {
break;
}
int nextDigit = replacement.charAt(cursor) - '0';
if ((nextDigit < 0) || (nextDigit > 9)) { // not a number
break;
// Throw IAE if this "$" is the last character in replacement
if (cursor == replacement.length())
throw new IllegalArgumentException(
"Illegal group reference: group index is missing");
nextChar = replacement.charAt(cursor);
int refNum = -1;
if (nextChar == '{') {
cursor++;
int begin = cursor;
while (cursor < replacement.length()) {
nextChar = replacement.charAt(cursor);
if (ASCII.isLower(nextChar) ||
ASCII.isUpper(nextChar) ||
ASCII.isDigit(nextChar)) {
cursor++;
} else {
break;
}
}
int newRefNum = (refNum * 10) + nextDigit;
if (groupCount() < newRefNum) {
done = true;
} else {
refNum = newRefNum;
cursor++;
if (begin == cursor)
throw new IllegalArgumentException(
"named capturing group has 0 length name");
if (nextChar != '}')
throw new IllegalArgumentException(
"named capturing group is missing trailing '}'");
String gname = replacement.substring(begin, cursor);
if (ASCII.isDigit(gname.charAt(0)))
throw new IllegalArgumentException(
"capturing group name {" + gname +
"} starts with digit character");
if (!namedGroups().containsKey(gname))
throw new IllegalArgumentException(
"No group with name {" + gname + "}");
refNum = namedGroups().get(gname);
cursor++;
} else {
// The first number is always a group
refNum = nextChar - '0';
if ((refNum < 0) || (refNum > 9))
throw new IllegalArgumentException(
"Illegal group reference");
cursor++;
// Capture the largest legal group string
boolean done = false;
while (!done) {
if (cursor >= replacement.length()) {
break;
}
int nextDigit = replacement.charAt(cursor) - '0';
if ((nextDigit < 0) || (nextDigit > 9)) { // not a number
break;
}
int newRefNum = (refNum * 10) + nextDigit;
if (groupCount() < newRefNum) {
done = true;
} else {
refNum = newRefNum;
cursor++;
}
}
}
// Append group
if (start(refNum) != -1 && end(refNum) != -1)
app.append(text, start(refNum), end(refNum));
} else {
app.append(nextChar);
cursor++;
}
// Append group
if (start(refNum) != -1 && end(refNum) != -1)
result.append(text, start(refNum), end(refNum));
} else {
result.append(nextChar);
cursor++;
}
} catch (IOException e) { // cannot happen on String[Buffer|Builder]
throw new AssertionError(e.getMessage());
}
return result;
}

/**
Expand Down

1 comment on commit ca745cb

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.