Skip to content

Commit

Permalink
Merge pull request #3001 from harawata/gh/2080-xnode-tostring
Browse files Browse the repository at this point in the history
XNode#toString() should output all child nodes
  • Loading branch information
harawata committed Nov 10, 2023
2 parents 83c5722 + b880ac0 commit 02e07b8
Show file tree
Hide file tree
Showing 3 changed files with 203 additions and 80 deletions.
52 changes: 25 additions & 27 deletions src/main/java/org/apache/ibatis/parsing/XNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -280,49 +280,47 @@ public Properties getChildrenAsProperties() {

@Override
public String toString() {
StringBuilder builder = new StringBuilder();
toString(builder, 0);
return builder.toString();
return buildToString(new StringBuilder(), 0).toString();
}

private void toString(StringBuilder builder, int level) {
builder.append("<");
builder.append(name);
private StringBuilder buildToString(StringBuilder builder, int indentLevel) {
indent(builder, indentLevel).append("<").append(name);
for (Map.Entry<Object, Object> entry : attributes.entrySet()) {
builder.append(" ");
builder.append(entry.getKey());
builder.append("=\"");
builder.append(entry.getValue());
builder.append("\"");
}
List<XNode> children = getChildren();
if (!children.isEmpty()) {

NodeList nodeList = node.getChildNodes();
if (nodeList == null || nodeList.getLength() == 0) {
builder.append(" />\n");
} else {
builder.append(">\n");
for (XNode child : children) {
indent(builder, level + 1);
child.toString(builder, level + 1);
for (int i = 0, n = nodeList.getLength(); i < n; i++) {
Node node = nodeList.item(i);
short nodeType = node.getNodeType();
if (nodeType == Node.ELEMENT_NODE) {
new XNode(xpathParser, node, variables).buildToString(builder, indentLevel + 1);
} else {
String text = getBodyData(node).trim();
if (text.length() > 0) {
indent(builder, indentLevel + 1).append(text).append("\n");
}
}
}
indent(builder, level);
builder.append("</");
builder.append(name);
builder.append(">");
} else if (body != null) {
builder.append(">");
builder.append(body);
builder.append("</");
builder.append(name);
builder.append(">");
} else {
builder.append("/>");
indent(builder, level);
indent(builder, indentLevel).append("</").append(name).append(">\n");
}
builder.append("\n");

return builder;
}

private void indent(StringBuilder builder, int level) {
private StringBuilder indent(StringBuilder builder, int level) {
for (int i = 0; i < level; i++) {
builder.append(" ");
builder.append(" ");
}
return builder;
}

private Properties parseAttributes(Node n) {
Expand Down
177 changes: 177 additions & 0 deletions src/test/java/org/apache/ibatis/parsing/XNodeTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
/*
* Copyright 2009-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.ibatis.parsing;

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.util.Properties;

import org.junit.jupiter.api.Test;

class XNodeTest {

@Test
void formatXNodeToString() {
XPathParser parser = new XPathParser(
"<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>");
String usersNodeToString = parser.evalNode("/users").toString();
String userNodeToString = parser.evalNode("/users/user").toString();
String carsNodeToString = parser.evalNode("/users/user/cars").toString();

// @formatter:off
String usersNodeToStringExpect =
"<users>\n"
+ " <user>\n"
+ " <id>\n"
+ " 100\n"
+ " </id>\n"
+ " <name>\n"
+ " Tom\n"
+ " </name>\n"
+ " <age>\n"
+ " 30\n"
+ " </age>\n"
+ " <cars>\n"
+ " <car index=\"1\">\n"
+ " BMW\n"
+ " </car>\n"
+ " <car index=\"2\">\n"
+ " Audi\n"
+ " </car>\n"
+ " <car index=\"3\">\n"
+ " Benz\n"
+ " </car>\n"
+ " </cars>\n"
+ " </user>\n"
+ "</users>\n";
// @formatter:on

// @formatter:off
String userNodeToStringExpect =
"<user>\n"
+ " <id>\n"
+ " 100\n"
+ " </id>\n"
+ " <name>\n"
+ " Tom\n"
+ " </name>\n"
+ " <age>\n"
+ " 30\n"
+ " </age>\n"
+ " <cars>\n"
+ " <car index=\"1\">\n"
+ " BMW\n"
+ " </car>\n"
+ " <car index=\"2\">\n"
+ " Audi\n"
+ " </car>\n"
+ " <car index=\"3\">\n"
+ " Benz\n"
+ " </car>\n"
+ " </cars>\n"
+ "</user>\n";
// @formatter:on

// @formatter:off
String carsNodeToStringExpect =
"<cars>\n"
+ " <car index=\"1\">\n"
+ " BMW\n"
+ " </car>\n"
+ " <car index=\"2\">\n"
+ " Audi\n"
+ " </car>\n"
+ " <car index=\"3\">\n"
+ " Benz\n"
+ " </car>\n"
+ "</cars>\n";
// @formatter:on

assertEquals(usersNodeToStringExpect, usersNodeToString);
assertEquals(userNodeToStringExpect, userNodeToString);
assertEquals(carsNodeToStringExpect, carsNodeToString);
}

@Test
void xNodeToString() {
// @formatter:off
String xml = "<mapper>\n" +
" <select id='select' resultType='map'>\n" +
" select\n" +
" <var set='foo' value='bar' />\n" +
" ID,\n" +
" NAME\n" +
" from STUDENT\n" +
" <where>\n" +
" <if test=\"name != null\">\n" +
" NAME = #{name}\n" +
" </if>\n" +
" and DISABLED = false\n" +
" </where>\n" +
" order by ID\n" +
" <choose>\n" +
" <when test='limit10'>\n" +
" limit 10\n" +
" </when>\n" +
" <otherwise>limit 20</otherwise>\n" +
" </choose>\n" +
" </select>\n" +
"</mapper>";

String expected = "<select id=\"select\" resultType=\"map\">\n" +
" select\n" +
" <var set=\"foo\" value=\"bar\" />\n" +
" ID,\n" +
// a little bit ugly here, but not a blocker
" NAME\n" +
" from STUDENT\n" +
" <where>\n" +
" <if test=\"name != null\">\n" +
" NAME = #{name}\n" +
" </if>\n" +
" and DISABLED = false\n" +
" </where>\n" +
" order by ID\n" +
" <choose>\n" +
" <when test=\"limit10\">\n" +
" limit 10\n" +
" </when>\n" +
" <otherwise>\n" +
" limit 20\n" +
" </otherwise>\n" +
" </choose>\n" +
"</select>\n";
// @formatter:on

XPathParser parser = new XPathParser(xml);
XNode selectNode = parser.evalNode("/mapper/select");
assertEquals(expected, selectNode.toString());
}

@Test
void testXnodeToStringVariables() throws Exception {
String src = "<root attr='${x}'>y = ${y}<sub attr='${y}'>x = ${x}</sub></root>";
String expected = "<root attr=\"foo\">\n y = bar\n <sub attr=\"bar\">\n x = foo\n </sub>\n</root>\n";
Properties vars = new Properties();
vars.put("x", "foo");
vars.put("y", "bar");
XPathParser parser = new XPathParser(src, false, vars);
XNode selectNode = parser.evalNode("/root");
assertEquals(expected, selectNode.toString());
}

}
54 changes: 1 addition & 53 deletions src/test/java/org/apache/ibatis/parsing/XPathParserTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -204,63 +204,11 @@ private void testEvalMethod(XPathParser parser) {
assertEquals((Float) 3.2f, parser.evalNode("/employee/active").getFloatAttribute("score"));
assertEquals((Double) 3.2d, parser.evalNode("/employee/active").getDoubleAttribute("score"));

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

@Test
void formatXNodeToString() {
XPathParser parser = new XPathParser(
"<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>");
String usersNodeToString = parser.evalNode("/users").toString();
String userNodeToString = parser.evalNode("/users/user").toString();
String carsNodeToString = parser.evalNode("/users/user/cars").toString();

// @formatter:off
String usersNodeToStringExpect =
"<users>\n"
+ " <user>\n"
+ " <id>100</id>\n"
+ " <name>Tom</name>\n"
+ " <age>30</age>\n"
+ " <cars>\n"
+ " <car index=\"1\">BMW</car>\n"
+ " <car index=\"2\">Audi</car>\n"
+ " <car index=\"3\">Benz</car>\n"
+ " </cars>\n"
+ " </user>\n"
+ "</users>\n";
// @formatter:on

// @formatter:off
String userNodeToStringExpect =
"<user>\n"
+ " <id>100</id>\n"
+ " <name>Tom</name>\n"
+ " <age>30</age>\n"
+ " <cars>\n"
+ " <car index=\"1\">BMW</car>\n"
+ " <car index=\"2\">Audi</car>\n"
+ " <car index=\"3\">Benz</car>\n"
+ " </cars>\n"
+ "</user>\n";
// @formatter:on

// @formatter:off
String carsNodeToStringExpect =
"<cars>\n"
+ " <car index=\"1\">BMW</car>\n"
+ " <car index=\"2\">Audi</car>\n"
+ " <car index=\"3\">Benz</car>\n"
+ "</cars>\n";
// @formatter:on

assertEquals(usersNodeToStringExpect, usersNodeToString);
assertEquals(userNodeToStringExpect, userNodeToString);
assertEquals(carsNodeToStringExpect, carsNodeToString);
}

}

0 comments on commit 02e07b8

Please sign in to comment.