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 LocalizedContent findInternationalizedChildContentInDefaultNamespace(String name) {
72 return LocalizedContent.get(this, name);
73 }
74
75 public Element findChild(String name, String xmlns) {
76 for (Element child : this.children) {
77 if (name.equals(child.getName()) && xmlns.equals(child.getAttribute("xmlns"))) {
78 return child;
79 }
80 }
81 return null;
82 }
83
84 public Element findChildEnsureSingle(String name, String xmlns) {
85 final List<Element> results = new ArrayList<>();
86 for (Element child : this.children) {
87 if (name.equals(child.getName()) && xmlns.equals(child.getAttribute("xmlns"))) {
88 results.add(child);
89 }
90 }
91 if (results.size() == 1) {
92 return results.get(0);
93 }
94 return null;
95 }
96
97 public String findChildContent(String name, String xmlns) {
98 Element element = findChild(name, xmlns);
99 return element == null ? null : element.getContent();
100 }
101
102 public boolean hasChild(final String name) {
103 return findChild(name) != null;
104 }
105
106 public boolean hasChild(final String name, final String xmlns) {
107 return findChild(name, xmlns) != null;
108 }
109
110 public List<Element> getChildren() {
111 return this.children;
112 }
113
114 public Element setChildren(List<Element> children) {
115 this.children = children;
116 return this;
117 }
118
119 public final String getContent() {
120 return content;
121 }
122
123 public Element setAttribute(String name, String value) {
124 if (name != null && value != null) {
125 this.attributes.put(name, value);
126 }
127 return this;
128 }
129
130 public Element setAttribute(String name, Jid value) {
131 if (name != null && value != null) {
132 this.attributes.put(name, value.toString());
133 }
134 return this;
135 }
136
137 public void setAttribute(final String name, final boolean value) {
138 this.setAttribute(name, value ? "1" : "0");
139 }
140
141 public void removeAttribute(final String name) {
142 this.attributes.remove(name);
143 }
144
145 public Element setAttributes(Hashtable<String, String> attributes) {
146 this.attributes = attributes;
147 return this;
148 }
149
150 public String getAttribute(String name) {
151 if (this.attributes.containsKey(name)) {
152 return this.attributes.get(name);
153 } else {
154 return null;
155 }
156 }
157
158 public long getLongAttribute(final String name) {
159 final var value = Longs.tryParse(Strings.nullToEmpty(this.attributes.get(name)));
160 return value == null ? 0 : value;
161 }
162
163 public Optional<Integer> getOptionalIntAttribute(final String name) {
164 final String value = getAttribute(name);
165 if (value == null) {
166 return Optional.absent();
167 }
168 return Optional.fromNullable(Ints.tryParse(value));
169 }
170
171 public Jid getAttributeAsJid(final String name) {
172 final String jid = this.getAttribute(name);
173 if (Strings.isNullOrEmpty(jid)) {
174 return null;
175 }
176 return Jid.ofOrInvalid(jid, this instanceof Message);
177 }
178
179 public Hashtable<String, String> getAttributes() {
180 return this.attributes;
181 }
182
183 @NonNull
184 public String toString() {
185 final StringBuilder elementOutput = new StringBuilder();
186 if (content == null && children.isEmpty()) {
187 final Tag emptyTag = Tag.empty(name);
188 emptyTag.setAttributes(this.attributes);
189 elementOutput.append(emptyTag);
190 } else {
191 final Tag startTag = Tag.start(name);
192 startTag.setAttributes(this.attributes);
193 elementOutput.append(startTag);
194 if (content != null) {
195 elementOutput.append(XmlHelper.encodeEntities(content));
196 } else {
197 for (final Element child : children) {
198 elementOutput.append(child.toString());
199 }
200 }
201 final Tag endTag = Tag.end(name);
202 elementOutput.append(endTag);
203 }
204 return elementOutput.toString();
205 }
206
207 public final String getName() {
208 return name;
209 }
210
211 public void clearChildren() {
212 this.children.clear();
213 }
214
215 public void setAttribute(String name, long value) {
216 this.setAttribute(name, Long.toString(value));
217 }
218
219 public void setAttribute(String name, int value) {
220 this.setAttribute(name, Integer.toString(value));
221 }
222
223 public boolean getAttributeAsBoolean(String name) {
224 String attr = getAttribute(name);
225 return (attr != null && (attr.equalsIgnoreCase("true") || attr.equalsIgnoreCase("1")));
226 }
227
228 public String getNamespace() {
229 return getAttribute("xmlns");
230 }
231}