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