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