Skip to content

Commit 7209632

Browse files
committed
fixes #494 Allow escaping '}' character in ${} expression. e.g. ${num in {1, 2, 3\}}
1 parent 4149edf commit 7209632

File tree

3 files changed

+39
-9
lines changed

3 files changed

+39
-9
lines changed

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

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,26 +31,42 @@ public GenericTokenParser(String openToken, String closeToken, TokenHandler hand
3131
}
3232

3333
public String parse(String text) {
34-
StringBuilder builder = new StringBuilder();
34+
final StringBuilder builder = new StringBuilder();
35+
final StringBuilder expression = new StringBuilder();
3536
if (text != null && text.length() > 0) {
3637
char[] src = text.toCharArray();
3738
int offset = 0;
39+
// search open token
3840
int start = text.indexOf(openToken, offset);
3941
while (start > -1) {
4042
if (start > 0 && src[start - 1] == '\\') {
41-
// the variable is escaped. remove the backslash.
43+
// this open token is escaped. remove the backslash and continue.
4244
builder.append(src, offset, start - offset - 1).append(openToken);
4345
offset = start + openToken.length();
4446
} else {
45-
int end = text.indexOf(closeToken, start);
47+
// found open token. let's search close token.
48+
expression.setLength(0);
49+
builder.append(src, offset, start - offset);
50+
offset = start + openToken.length();
51+
int end = text.indexOf(closeToken, offset);
52+
while (end > -1) {
53+
if (end > offset && src[end - 1] == '\\') {
54+
// this close token is escaped. remove the backslash and continue.
55+
expression.append(src, offset, end - offset - 1).append(closeToken);
56+
offset = end + closeToken.length();
57+
end = text.indexOf(closeToken, offset);
58+
} else {
59+
expression.append(src, offset, end - offset);
60+
offset = end + closeToken.length();
61+
break;
62+
}
63+
}
4664
if (end == -1) {
47-
builder.append(src, offset, src.length - offset);
65+
// close token was not found.
66+
builder.append(src, start, src.length - start);
4867
offset = src.length;
4968
} else {
50-
builder.append(src, offset, start - offset);
51-
offset = start + openToken.length();
52-
String content = new String(src, offset, end - offset);
53-
builder.append(handler.handleToken(content));
69+
builder.append(handler.handleToken(expression.toString()));
5470
offset = end + closeToken.length();
5571
}
5672
}
@@ -62,5 +78,4 @@ public String parse(String text) {
6278
}
6379
return builder.toString();
6480
}
65-
6681
}

src/test/java/org/apache/ibatis/builder/xml/dynamic/DynamicSqlSourceTest.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,17 @@ public void shouldIterateOnceForEachItemInCollection() throws Exception {
317317
assertEquals("__frch_item_2", boundSql.getParameterMappings().get(2).getProperty());
318318
}
319319

320+
@Test
321+
public void shouldHandleOgnlExpression() throws Exception {
322+
final HashMap<String, String> parameterObject = new HashMap<String, String>() {{
323+
put("name", "Steve");
324+
}};
325+
final String expected = "Expression test: 3 / yes.";
326+
DynamicSqlSource source = createDynamicSqlSource(new TextSqlNode("Expression test: ${name.indexOf('v')} / ${name in {'Bob', 'Steve'\\} ? 'yes' : 'no'}."));
327+
BoundSql boundSql = source.getBoundSql(parameterObject);
328+
assertEquals(expected, boundSql.getSql());
329+
}
330+
320331
@Test
321332
public void shouldSkipForEachWhenCollectionIsEmpty() throws Exception {
322333
final HashMap<String, Integer[]> parameterObject = new HashMap<String, Integer[]>() {{

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ public void shouldDemonstrateGenericTokenReplacement() {
4343
put("first_name", "James");
4444
put("initial", "T");
4545
put("last_name", "Kirk");
46+
put("var{with}brace", "Hiya");
4647
put("", "");
4748
}
4849
}));
@@ -62,6 +63,9 @@ public void shouldDemonstrateGenericTokenReplacement() {
6263

6364
assertEquals("{$$something}JamesTKirk", parser.parse("{$$something}${first_name}${initial}${last_name}"));
6465
assertEquals("${", parser.parse("${"));
66+
assertEquals("${\\}", parser.parse("${\\}"));
67+
assertEquals("Hiya", parser.parse("${var{with\\}brace}"));
68+
assertEquals("", parser.parse("${}"));
6569
assertEquals("}", parser.parse("}"));
6670
assertEquals("Hello ${ this is a test.", parser.parse("Hello ${ this is a test."));
6771
assertEquals("Hello } this is a test.", parser.parse("Hello } this is a test."));

0 commit comments

Comments
 (0)