Skip to content

Commit 02e07b8

Browse files
authored
Merge pull request #3001 from harawata/gh/2080-xnode-tostring
XNode#toString() should output all child nodes
2 parents 83c5722 + b880ac0 commit 02e07b8

File tree

3 files changed

+203
-80
lines changed

3 files changed

+203
-80
lines changed

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

Lines changed: 25 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -280,49 +280,47 @@ public Properties getChildrenAsProperties() {
280280

281281
@Override
282282
public String toString() {
283-
StringBuilder builder = new StringBuilder();
284-
toString(builder, 0);
285-
return builder.toString();
283+
return buildToString(new StringBuilder(), 0).toString();
286284
}
287285

288-
private void toString(StringBuilder builder, int level) {
289-
builder.append("<");
290-
builder.append(name);
286+
private StringBuilder buildToString(StringBuilder builder, int indentLevel) {
287+
indent(builder, indentLevel).append("<").append(name);
291288
for (Map.Entry<Object, Object> entry : attributes.entrySet()) {
292289
builder.append(" ");
293290
builder.append(entry.getKey());
294291
builder.append("=\"");
295292
builder.append(entry.getValue());
296293
builder.append("\"");
297294
}
298-
List<XNode> children = getChildren();
299-
if (!children.isEmpty()) {
295+
296+
NodeList nodeList = node.getChildNodes();
297+
if (nodeList == null || nodeList.getLength() == 0) {
298+
builder.append(" />\n");
299+
} else {
300300
builder.append(">\n");
301-
for (XNode child : children) {
302-
indent(builder, level + 1);
303-
child.toString(builder, level + 1);
301+
for (int i = 0, n = nodeList.getLength(); i < n; i++) {
302+
Node node = nodeList.item(i);
303+
short nodeType = node.getNodeType();
304+
if (nodeType == Node.ELEMENT_NODE) {
305+
new XNode(xpathParser, node, variables).buildToString(builder, indentLevel + 1);
306+
} else {
307+
String text = getBodyData(node).trim();
308+
if (text.length() > 0) {
309+
indent(builder, indentLevel + 1).append(text).append("\n");
310+
}
311+
}
304312
}
305-
indent(builder, level);
306-
builder.append("</");
307-
builder.append(name);
308-
builder.append(">");
309-
} else if (body != null) {
310-
builder.append(">");
311-
builder.append(body);
312-
builder.append("</");
313-
builder.append(name);
314-
builder.append(">");
315-
} else {
316-
builder.append("/>");
317-
indent(builder, level);
313+
indent(builder, indentLevel).append("</").append(name).append(">\n");
318314
}
319-
builder.append("\n");
315+
316+
return builder;
320317
}
321318

322-
private void indent(StringBuilder builder, int level) {
319+
private StringBuilder indent(StringBuilder builder, int level) {
323320
for (int i = 0; i < level; i++) {
324-
builder.append(" ");
321+
builder.append(" ");
325322
}
323+
return builder;
326324
}
327325

328326
private Properties parseAttributes(Node n) {
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
/*
2+
* Copyright 2009-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.apache.ibatis.parsing;
18+
19+
import static org.junit.jupiter.api.Assertions.assertEquals;
20+
21+
import java.util.Properties;
22+
23+
import org.junit.jupiter.api.Test;
24+
25+
class XNodeTest {
26+
27+
@Test
28+
void formatXNodeToString() {
29+
XPathParser parser = new XPathParser(
30+
"<users><user><id>100</id><name>Tom</name><age>30</age><cars><car index=\"1\">BMW</car><car index=\"2\">Audi</car><car index=\"3\">Benz</car></cars></user></users>");
31+
String usersNodeToString = parser.evalNode("/users").toString();
32+
String userNodeToString = parser.evalNode("/users/user").toString();
33+
String carsNodeToString = parser.evalNode("/users/user/cars").toString();
34+
35+
// @formatter:off
36+
String usersNodeToStringExpect =
37+
"<users>\n"
38+
+ " <user>\n"
39+
+ " <id>\n"
40+
+ " 100\n"
41+
+ " </id>\n"
42+
+ " <name>\n"
43+
+ " Tom\n"
44+
+ " </name>\n"
45+
+ " <age>\n"
46+
+ " 30\n"
47+
+ " </age>\n"
48+
+ " <cars>\n"
49+
+ " <car index=\"1\">\n"
50+
+ " BMW\n"
51+
+ " </car>\n"
52+
+ " <car index=\"2\">\n"
53+
+ " Audi\n"
54+
+ " </car>\n"
55+
+ " <car index=\"3\">\n"
56+
+ " Benz\n"
57+
+ " </car>\n"
58+
+ " </cars>\n"
59+
+ " </user>\n"
60+
+ "</users>\n";
61+
// @formatter:on
62+
63+
// @formatter:off
64+
String userNodeToStringExpect =
65+
"<user>\n"
66+
+ " <id>\n"
67+
+ " 100\n"
68+
+ " </id>\n"
69+
+ " <name>\n"
70+
+ " Tom\n"
71+
+ " </name>\n"
72+
+ " <age>\n"
73+
+ " 30\n"
74+
+ " </age>\n"
75+
+ " <cars>\n"
76+
+ " <car index=\"1\">\n"
77+
+ " BMW\n"
78+
+ " </car>\n"
79+
+ " <car index=\"2\">\n"
80+
+ " Audi\n"
81+
+ " </car>\n"
82+
+ " <car index=\"3\">\n"
83+
+ " Benz\n"
84+
+ " </car>\n"
85+
+ " </cars>\n"
86+
+ "</user>\n";
87+
// @formatter:on
88+
89+
// @formatter:off
90+
String carsNodeToStringExpect =
91+
"<cars>\n"
92+
+ " <car index=\"1\">\n"
93+
+ " BMW\n"
94+
+ " </car>\n"
95+
+ " <car index=\"2\">\n"
96+
+ " Audi\n"
97+
+ " </car>\n"
98+
+ " <car index=\"3\">\n"
99+
+ " Benz\n"
100+
+ " </car>\n"
101+
+ "</cars>\n";
102+
// @formatter:on
103+
104+
assertEquals(usersNodeToStringExpect, usersNodeToString);
105+
assertEquals(userNodeToStringExpect, userNodeToString);
106+
assertEquals(carsNodeToStringExpect, carsNodeToString);
107+
}
108+
109+
@Test
110+
void xNodeToString() {
111+
// @formatter:off
112+
String xml = "<mapper>\n" +
113+
" <select id='select' resultType='map'>\n" +
114+
" select\n" +
115+
" <var set='foo' value='bar' />\n" +
116+
" ID,\n" +
117+
" NAME\n" +
118+
" from STUDENT\n" +
119+
" <where>\n" +
120+
" <if test=\"name != null\">\n" +
121+
" NAME = #{name}\n" +
122+
" </if>\n" +
123+
" and DISABLED = false\n" +
124+
" </where>\n" +
125+
" order by ID\n" +
126+
" <choose>\n" +
127+
" <when test='limit10'>\n" +
128+
" limit 10\n" +
129+
" </when>\n" +
130+
" <otherwise>limit 20</otherwise>\n" +
131+
" </choose>\n" +
132+
" </select>\n" +
133+
"</mapper>";
134+
135+
String expected = "<select id=\"select\" resultType=\"map\">\n" +
136+
" select\n" +
137+
" <var set=\"foo\" value=\"bar\" />\n" +
138+
" ID,\n" +
139+
// a little bit ugly here, but not a blocker
140+
" NAME\n" +
141+
" from STUDENT\n" +
142+
" <where>\n" +
143+
" <if test=\"name != null\">\n" +
144+
" NAME = #{name}\n" +
145+
" </if>\n" +
146+
" and DISABLED = false\n" +
147+
" </where>\n" +
148+
" order by ID\n" +
149+
" <choose>\n" +
150+
" <when test=\"limit10\">\n" +
151+
" limit 10\n" +
152+
" </when>\n" +
153+
" <otherwise>\n" +
154+
" limit 20\n" +
155+
" </otherwise>\n" +
156+
" </choose>\n" +
157+
"</select>\n";
158+
// @formatter:on
159+
160+
XPathParser parser = new XPathParser(xml);
161+
XNode selectNode = parser.evalNode("/mapper/select");
162+
assertEquals(expected, selectNode.toString());
163+
}
164+
165+
@Test
166+
void testXnodeToStringVariables() throws Exception {
167+
String src = "<root attr='${x}'>y = ${y}<sub attr='${y}'>x = ${x}</sub></root>";
168+
String expected = "<root attr=\"foo\">\n y = bar\n <sub attr=\"bar\">\n x = foo\n </sub>\n</root>\n";
169+
Properties vars = new Properties();
170+
vars.put("x", "foo");
171+
vars.put("y", "bar");
172+
XPathParser parser = new XPathParser(src, false, vars);
173+
XNode selectNode = parser.evalNode("/root");
174+
assertEquals(expected, selectNode.toString());
175+
}
176+
177+
}

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

Lines changed: 1 addition & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -204,63 +204,11 @@ private void testEvalMethod(XPathParser parser) {
204204
assertEquals((Float) 3.2f, parser.evalNode("/employee/active").getFloatAttribute("score"));
205205
assertEquals((Double) 3.2d, parser.evalNode("/employee/active").getDoubleAttribute("score"));
206206

207-
assertEquals("<id>${id_var}</id>", parser.evalNode("/employee/@id").toString().trim());
207+
assertEquals("<id>\n ${id_var}\n</id>", parser.evalNode("/employee/@id").toString().trim());
208208
assertEquals(7, parser.evalNodes("/employee/*").size());
209209
XNode node = parser.evalNode("/employee/height");
210210
assertEquals("employee/height", node.getPath());
211211
assertEquals("employee[${id_var}]_height", node.getValueBasedIdentifier());
212212
}
213213

214-
@Test
215-
void formatXNodeToString() {
216-
XPathParser parser = new XPathParser(
217-
"<users><user><id>100</id><name>Tom</name><age>30</age><cars><car index=\"1\">BMW</car><car index=\"2\">Audi</car><car index=\"3\">Benz</car></cars></user></users>");
218-
String usersNodeToString = parser.evalNode("/users").toString();
219-
String userNodeToString = parser.evalNode("/users/user").toString();
220-
String carsNodeToString = parser.evalNode("/users/user/cars").toString();
221-
222-
// @formatter:off
223-
String usersNodeToStringExpect =
224-
"<users>\n"
225-
+ " <user>\n"
226-
+ " <id>100</id>\n"
227-
+ " <name>Tom</name>\n"
228-
+ " <age>30</age>\n"
229-
+ " <cars>\n"
230-
+ " <car index=\"1\">BMW</car>\n"
231-
+ " <car index=\"2\">Audi</car>\n"
232-
+ " <car index=\"3\">Benz</car>\n"
233-
+ " </cars>\n"
234-
+ " </user>\n"
235-
+ "</users>\n";
236-
// @formatter:on
237-
238-
// @formatter:off
239-
String userNodeToStringExpect =
240-
"<user>\n"
241-
+ " <id>100</id>\n"
242-
+ " <name>Tom</name>\n"
243-
+ " <age>30</age>\n"
244-
+ " <cars>\n"
245-
+ " <car index=\"1\">BMW</car>\n"
246-
+ " <car index=\"2\">Audi</car>\n"
247-
+ " <car index=\"3\">Benz</car>\n"
248-
+ " </cars>\n"
249-
+ "</user>\n";
250-
// @formatter:on
251-
252-
// @formatter:off
253-
String carsNodeToStringExpect =
254-
"<cars>\n"
255-
+ " <car index=\"1\">BMW</car>\n"
256-
+ " <car index=\"2\">Audi</car>\n"
257-
+ " <car index=\"3\">Benz</car>\n"
258-
+ "</cars>\n";
259-
// @formatter:on
260-
261-
assertEquals(usersNodeToStringExpect, usersNodeToString);
262-
assertEquals(userNodeToStringExpect, userNodeToString);
263-
assertEquals(carsNodeToStringExpect, carsNodeToString);
264-
}
265-
266214
}

0 commit comments

Comments
 (0)