Skip to content

Commit 0443b44

Browse files
committed
(fixes http://code.google.com/p/mybatis/issues/detail?id=760 ) GenericTokenParaser#parse() could cause memory/performance issue on Java 7u6+ when the parsed string contains many variables to replace.
1 parent deb8eee commit 0443b44

File tree

2 files changed

+45
-28
lines changed

2 files changed

+45
-28
lines changed

src/main/java/org/apache/ibatis/parsing/GenericTokenParser.java

Lines changed: 22 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -29,39 +29,33 @@ public GenericTokenParser(String openToken, String closeToken, TokenHandler hand
2929

3030
public String parse(String text) {
3131
StringBuilder builder = new StringBuilder();
32-
if (text != null) {
33-
String after = text;
34-
int start = after.indexOf(openToken);
35-
int end = after.indexOf(closeToken);
32+
if (text != null && text.length() > 0) {
33+
char[] src = text.toCharArray();
34+
int offset = 0;
35+
int start = text.indexOf(openToken, offset);
3636
while (start > -1) {
37-
if (end > start) {
38-
String before = after.substring(0, start);
39-
String content = after.substring(start + openToken.length(), end);
40-
String substitution;
41-
42-
// check if variable has to be skipped
43-
if (start > 0 && text.charAt(start - 1) == '\\') {
44-
before = before.substring(0, before.length() - 1);
45-
substitution = new StringBuilder(openToken).append(content).append(closeToken).toString();
37+
if (start > 0 && src[start - 1] == '\\') {
38+
// the variable is escaped. remove the backslash.
39+
builder.append(src, offset, start - 1).append(openToken);
40+
offset = start + openToken.length();
41+
} else {
42+
int end = text.indexOf(closeToken, start);
43+
if (end == -1) {
44+
builder.append(src, offset, src.length - offset);
45+
offset = src.length;
4646
} else {
47-
substitution = handler.handleToken(content);
47+
builder.append(src, offset, start - offset);
48+
offset = start + openToken.length();
49+
String content = new String(src, offset, end - offset);
50+
builder.append(handler.handleToken(content));
51+
offset = end + closeToken.length();
4852
}
49-
50-
builder.append(before);
51-
builder.append(substitution);
52-
after = after.substring(end + closeToken.length());
53-
} else if (end > -1) {
54-
String before = after.substring(0, end);
55-
builder.append(before);
56-
builder.append(closeToken);
57-
after = after.substring(end + closeToken.length());
58-
} else {
59-
break;
6053
}
61-
start = after.indexOf(openToken);
62-
end = after.indexOf(closeToken);
54+
start = text.indexOf(openToken, offset);
55+
}
56+
if (offset < src.length) {
57+
builder.append(src, offset, src.length - offset);
6358
}
64-
builder.append(after);
6559
}
6660
return builder.toString();
6761
}

src/test/java/org/apache/ibatis/parsing/GenericTokenParserTest.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,4 +75,27 @@ public void shallNotInterpolateSkippedVaiables() {
7575
assertEquals("This is a ${skipped} variable", parser.parse("This is a \\${skipped} variable"));
7676
}
7777

78+
@Test(timeout = 1000)
79+
public void shouldParseFastOnJdk7u6() {
80+
// issue #760
81+
GenericTokenParser parser = new GenericTokenParser("${", "}", new VariableTokenHandler(new HashMap<String, String>() {
82+
{
83+
put("first_name", "James");
84+
put("initial", "T");
85+
put("last_name", "Kirk");
86+
put("", "");
87+
}
88+
}));
89+
90+
StringBuilder input = new StringBuilder();
91+
for (int i = 0; i < 10000; i++) {
92+
input.append("${first_name} ${initial} ${last_name} reporting. ");
93+
}
94+
StringBuilder expected = new StringBuilder();
95+
for (int i = 0; i < 10000; i++) {
96+
expected.append("James T Kirk reporting. ");
97+
}
98+
assertEquals(expected.toString(), parser.parse(input.toString()));
99+
}
100+
78101
}

0 commit comments

Comments
 (0)