1package eu.siacs.conversations.xml;
2
3import org.jetbrains.annotations.NotNull;
4
5import java.util.ArrayList;
6import java.util.Collection;
7import java.util.Hashtable;
8import java.util.List;
9import java.util.stream.Collectors;
10
11import eu.siacs.conversations.utils.XmlHelper;
12import eu.siacs.conversations.xmpp.InvalidJid;
13import eu.siacs.conversations.xmpp.Jid;
14import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
15
16public class Element implements Node {
17 private final String name;
18 private Hashtable<String, String> attributes = new Hashtable<>();
19 private List<Element> children = new ArrayList<>();
20 private List<Node> childNodes = new ArrayList<>();
21
22 public Element(String name) {
23 this.name = name;
24 }
25
26 public Element(String name, String xmlns) {
27 this.name = name;
28 this.setAttribute("xmlns", xmlns);
29 }
30
31 public Node prependChild(Node child) {
32 childNodes.add(0, child);
33 if (child instanceof Element) children.add(0, (Element) child);
34 return child;
35 }
36
37 public Node addChild(Node child) {
38 childNodes.add(child);
39 if (child instanceof Element) children.add((Element) child);
40 return child;
41 }
42
43 public Element addChild(String name) {
44 Element child = new Element(name);
45 childNodes.add(child);
46 children.add(child);
47 return child;
48 }
49
50 public Element addChild(String name, String xmlns) {
51 Element child = new Element(name);
52 child.setAttribute("xmlns", xmlns);
53 childNodes.add(child);
54 children.add(child);
55 return child;
56 }
57
58 public void addChildren(final Collection<? extends Node> children) {
59 if (children == null) return;
60
61 this.childNodes.addAll(children);
62 for (Node node : children) {
63 if (node instanceof Element) {
64 this.children.add((Element) node);
65 }
66 }
67 }
68
69 public void removeChild(Node child) {
70 this.childNodes.remove(child);
71 if (child instanceof Element) this.children.remove(child);
72 }
73
74 public Element setContent(String content) {
75 clearChildren();
76 if (content != null) this.childNodes.add(new TextNode(content));
77 return this;
78 }
79
80 public Element findChild(String name) {
81 for (Element child : this.children) {
82 if (child.getName().equals(name)) {
83 return child;
84 }
85 }
86 return null;
87 }
88
89 public String findChildContent(String name) {
90 Element element = findChild(name);
91 return element == null ? null : element.getContent();
92 }
93
94 public LocalizedContent findInternationalizedChildContentInDefaultNamespace(String name) {
95 return LocalizedContent.get(this, name);
96 }
97
98 public Element findChild(String name, String xmlns) {
99 for (Element child : this.children) {
100 if (name.equals(child.getName()) && xmlns.equals(child.getAttribute("xmlns"))) {
101 return child;
102 }
103 }
104 return null;
105 }
106
107 public Element findChildEnsureSingle(String name, String xmlns) {
108 final List<Element> results = new ArrayList<>();
109 for (Element child : this.children) {
110 if (name.equals(child.getName()) && xmlns.equals(child.getAttribute("xmlns"))) {
111 results.add(child);
112 }
113 }
114 if (results.size() == 1) {
115 return results.get(0);
116 }
117 return null;
118 }
119
120 public String findChildContent(String name, String xmlns) {
121 Element element = findChild(name,xmlns);
122 return element == null ? null : element.getContent();
123 }
124
125 public boolean hasChild(final String name) {
126 return findChild(name) != null;
127 }
128
129 public boolean hasChild(final String name, final String xmlns) {
130 return findChild(name, xmlns) != null;
131 }
132
133 public final List<Element> getChildren() {
134 return this.children;
135 }
136
137 public Element setChildren(List<Element> children) {
138 this.childNodes = new ArrayList(children);
139 this.children = children;
140 return this;
141 }
142
143 public final String getContent() {
144 return this.childNodes.stream().map(Node::getContent).filter(c -> c != null).collect(Collectors.joining());
145 }
146
147 public Element setAttribute(String name, String value) {
148 if (name != null && value != null) {
149 this.attributes.put(name, value);
150 }
151 return this;
152 }
153
154 public Element setAttribute(String name, Jid value) {
155 if (name != null && value != null) {
156 this.attributes.put(name, value.toEscapedString());
157 }
158 return this;
159 }
160
161 public Element removeAttribute(String name) {
162 this.attributes.remove(name);
163 return this;
164 }
165
166 public Element setAttributes(Hashtable<String, String> attributes) {
167 this.attributes = attributes;
168 return this;
169 }
170
171 public String getAttribute(String name) {
172 if (this.attributes.containsKey(name)) {
173 return this.attributes.get(name);
174 } else {
175 return null;
176 }
177 }
178
179 public Jid getAttributeAsJid(String name) {
180 final String jid = this.getAttribute(name);
181 if (jid != null && !jid.isEmpty()) {
182 try {
183 return Jid.ofEscaped(jid);
184 } catch (final IllegalArgumentException e) {
185 return InvalidJid.of(jid, this instanceof MessagePacket);
186 }
187 }
188 return null;
189 }
190
191 public Hashtable<String, String> getAttributes() {
192 return this.attributes;
193 }
194
195 @NotNull
196 public String toString() {
197 final StringBuilder elementOutput = new StringBuilder();
198 if (childNodes.size() == 0) {
199 Tag emptyTag = Tag.empty(name);
200 emptyTag.setAtttributes(this.attributes);
201 elementOutput.append(emptyTag.toString());
202 } else {
203 Tag startTag = Tag.start(name);
204 startTag.setAtttributes(this.attributes);
205 elementOutput.append(startTag);
206 for (Node child : childNodes) {
207 elementOutput.append(child.toString());
208 }
209 Tag endTag = Tag.end(name);
210 elementOutput.append(endTag);
211 }
212 return elementOutput.toString();
213 }
214
215 public final String getName() {
216 return name;
217 }
218
219 public void clearChildren() {
220 this.children.clear();
221 this.childNodes.clear();
222 }
223
224 public void setAttribute(String name, long value) {
225 this.setAttribute(name, Long.toString(value));
226 }
227
228 public void setAttribute(String name, int value) {
229 this.setAttribute(name, Integer.toString(value));
230 }
231
232 public boolean getAttributeAsBoolean(String name) {
233 String attr = getAttribute(name);
234 return (attr != null && (attr.equalsIgnoreCase("true") || attr.equalsIgnoreCase("1")));
235 }
236
237 public String getNamespace() {
238 return getAttribute("xmlns");
239 }
240}