8291598: Matcher.appendReplacement should not create new StringBuilder instances
Reviewed-by: rriggs
This commit is contained in:
parent
1683a63a7d
commit
ca745cb426
@ -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,93 +1029,94 @@ 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);
|
||||||
if (nextChar == '\\') {
|
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 == '{') {
|
|
||||||
cursor++;
|
cursor++;
|
||||||
StringBuilder gsb = new StringBuilder();
|
if (cursor == replacement.length())
|
||||||
while (cursor < replacement.length()) {
|
throw new IllegalArgumentException(
|
||||||
nextChar = replacement.charAt(cursor);
|
"character to be escaped is missing");
|
||||||
if (ASCII.isLower(nextChar) ||
|
nextChar = replacement.charAt(cursor);
|
||||||
ASCII.isUpper(nextChar) ||
|
app.append(nextChar);
|
||||||
ASCII.isDigit(nextChar)) {
|
cursor++;
|
||||||
gsb.append(nextChar);
|
} else if (nextChar == '$') {
|
||||||
cursor++;
|
// Skip past $
|
||||||
} else {
|
cursor++;
|
||||||
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (gsb.length() == 0)
|
// Append group
|
||||||
throw new IllegalArgumentException(
|
if (start(refNum) != -1 && end(refNum) != -1)
|
||||||
"named capturing group has 0 length name");
|
app.append(text, start(refNum), end(refNum));
|
||||||
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))
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"No group with name {" + gname + "}");
|
|
||||||
refNum = namedGroups().get(gname);
|
|
||||||
cursor++;
|
|
||||||
} else {
|
} else {
|
||||||
// The first number is always a group
|
app.append(nextChar);
|
||||||
refNum = nextChar - '0';
|
|
||||||
if ((refNum < 0) || (refNum > 9))
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"Illegal group reference");
|
|
||||||
cursor++;
|
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)
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user