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