Skip to content

Commit b444898

Browse files
committed
Started to implement basic xml dumper
1 parent 7d45df6 commit b444898

File tree

4 files changed

+207
-0
lines changed

4 files changed

+207
-0
lines changed
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package org.javawebstack.abstractdata.xml;
2+
3+
import java.util.ArrayList;
4+
import java.util.Arrays;
5+
import java.util.List;
6+
import java.util.Map;
7+
import java.util.stream.Collectors;
8+
9+
public class XMLDumper {
10+
11+
private boolean pretty;
12+
private boolean useSelfClosing = true;
13+
private String indent = " ";
14+
15+
public XMLDumper setPretty(boolean pretty) {
16+
this.pretty = pretty;
17+
return this;
18+
}
19+
20+
public XMLDumper setIndent(String indent) {
21+
this.indent = indent;
22+
return this;
23+
}
24+
25+
public XMLDumper setUseSelfClosing(boolean useSelfClosing) {
26+
this.useSelfClosing = useSelfClosing;
27+
return this;
28+
}
29+
30+
public String dump(XMLNode node) {
31+
return String.join("\n", dumpLines(node));
32+
}
33+
34+
private List<String> dumpLines(XMLNode node) {
35+
if(node instanceof XMLTextNode) {
36+
XMLTextNode textNode = (XMLTextNode) node;
37+
return Arrays.asList(escape(textNode.getText(), true));
38+
}
39+
XMLElement element = (XMLElement) node;
40+
List<String> lines = new ArrayList<>();
41+
boolean selfClosing = useSelfClosing && element.getChildNodes().size() == 0;
42+
String openingTag = renderOpeningTag(element.tagName(), element.getAttributes(), selfClosing);
43+
String closingTag = selfClosing ? "" : "</" + escape(element.tagName(), false) + ">";
44+
if(pretty) {
45+
if(element.getChildNodes().size() == 0) {
46+
lines.add(openingTag + closingTag);
47+
} else if(element.getChildNodes().size() == 1 && element.getChildNodes().get(0) instanceof XMLTextNode) {
48+
lines.add(openingTag + escape(((XMLTextNode) element.getChildNodes().get(0)).getText(), true) + closingTag);
49+
} else {
50+
lines.add(openingTag);
51+
for(XMLNode child : element.getChildNodes()) {
52+
lines.addAll(dumpLines(child).stream().map(l -> indent + l).collect(Collectors.toList()));
53+
}
54+
lines.add(closingTag);
55+
}
56+
} else {
57+
StringBuilder sb = new StringBuilder();
58+
sb.append(openingTag);
59+
for(XMLNode child : element.getChildNodes())
60+
dumpLines(child).forEach(sb::append);
61+
sb.append(closingTag);
62+
lines.add(sb.toString());
63+
}
64+
return lines;
65+
}
66+
67+
public String renderOpeningTag(String tagName, Map<String, String> attributes, boolean selfClosing) {
68+
StringBuilder sb = new StringBuilder();
69+
sb.append('<');
70+
sb.append(escape(tagName, false));
71+
for(String attrName : attributes.keySet()) {
72+
sb.append(" ");
73+
sb.append(escape(attrName, false));
74+
sb.append('=');
75+
sb.append('"');
76+
sb.append(escape(attributes.get(attrName), false));
77+
sb.append('"');
78+
}
79+
if(selfClosing)
80+
sb.append("/");
81+
sb.append('>');
82+
return sb.toString();
83+
}
84+
85+
private String escape(String value, boolean text) {
86+
value = value.replace("<", "&lt;");
87+
value = value.replace("&", "&amp;");
88+
if(!text) {
89+
value = value.replace("\"", "&quot;");
90+
value = value.replace("'", "&apos;");
91+
value = value.replace(">", "&gt;");
92+
}
93+
return value;
94+
}
95+
96+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package org.javawebstack.abstractdata.xml;
2+
3+
import java.util.ArrayList;
4+
import java.util.LinkedHashMap;
5+
import java.util.List;
6+
import java.util.Map;
7+
import java.util.stream.Collectors;
8+
9+
public class XMLElement implements XMLNode {
10+
11+
private String tagName;
12+
private final Map<String, String> attributes = new LinkedHashMap<>();
13+
private final List<XMLNode> childNodes = new ArrayList<>();
14+
15+
public XMLElement(String tagName) {
16+
this.tagName = tagName;
17+
}
18+
19+
public XMLElement(String tagName, String text) {
20+
this(text);
21+
text(text);
22+
}
23+
24+
public String tagName() {
25+
return tagName;
26+
}
27+
28+
public XMLElement tagName(String tagName) {
29+
this.tagName = tagName;
30+
return this;
31+
}
32+
33+
public String text() {
34+
return childNodes.stream()
35+
.filter(n -> n instanceof XMLTextNode)
36+
.map(n -> ((XMLTextNode) n))
37+
.map(XMLTextNode::getText)
38+
.collect(Collectors.joining());
39+
}
40+
41+
public XMLElement text(String text) {
42+
childNodes.clear();
43+
childNodes.add(new XMLTextNode(text));
44+
return this;
45+
}
46+
47+
public XMLElement attr(String name, String value) {
48+
if(value == null) {
49+
attributes.remove(name);
50+
return this;
51+
}
52+
attributes.put(name, value);
53+
return this;
54+
}
55+
56+
public XMLElement ns(String namespace) {
57+
attr("xmlns", namespace);
58+
return this;
59+
}
60+
61+
public XMLElement ns(String alias, String namespace) {
62+
attr("xmlns:" + alias, namespace);
63+
return this;
64+
}
65+
66+
public XMLElement child(XMLNode childNode) {
67+
childNodes.add(childNode);
68+
return this;
69+
}
70+
71+
public Map<String, String> getAttributes() {
72+
return attributes;
73+
}
74+
75+
public List<XMLNode> getChildNodes() {
76+
return childNodes;
77+
}
78+
79+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package org.javawebstack.abstractdata.xml;
2+
3+
public interface XMLNode {
4+
5+
default String toXML() {
6+
return new XMLDumper().dump(this);
7+
}
8+
9+
default String toXML(boolean pretty) {
10+
return new XMLDumper().setPretty(pretty).dump(this);
11+
}
12+
13+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package org.javawebstack.abstractdata.xml;
2+
3+
public class XMLTextNode implements XMLNode {
4+
5+
private String text;
6+
7+
public XMLTextNode(String text) {
8+
this.text = text;
9+
}
10+
11+
public String getText() {
12+
return text;
13+
}
14+
15+
public void setText(String text) {
16+
this.text = text;
17+
}
18+
19+
}

0 commit comments

Comments
 (0)