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