1 package org.codehaus.plexus.util.xml;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 import java.io.IOException;
20 import java.io.Serializable;
21 import java.util.ArrayList;
22 import java.util.HashMap;
23 import java.util.List;
24 import java.util.Map;
25
26 import org.apache.maven.api.xml.XmlNode;
27 import org.apache.maven.api.xml.XmlService;
28 import org.codehaus.plexus.util.xml.pull.XmlSerializer;
29
30
31
32
33 public class Xpp3Dom implements Serializable {
34 private static final String[] EMPTY_STRING_ARRAY = new String[0];
35
36 public static final String CHILDREN_COMBINATION_MODE_ATTRIBUTE = XmlService.CHILDREN_COMBINATION_MODE_ATTRIBUTE;
37
38 public static final String CHILDREN_COMBINATION_MERGE = XmlService.CHILDREN_COMBINATION_MERGE;
39
40 public static final String CHILDREN_COMBINATION_APPEND = XmlService.CHILDREN_COMBINATION_APPEND;
41
42
43
44
45
46
47 public static final String DEFAULT_CHILDREN_COMBINATION_MODE = XmlService.DEFAULT_CHILDREN_COMBINATION_MODE;
48
49 public static final String SELF_COMBINATION_MODE_ATTRIBUTE = XmlService.SELF_COMBINATION_MODE_ATTRIBUTE;
50
51 public static final String SELF_COMBINATION_OVERRIDE = XmlService.SELF_COMBINATION_OVERRIDE;
52
53 public static final String SELF_COMBINATION_MERGE = XmlService.SELF_COMBINATION_MERGE;
54
55 public static final String SELF_COMBINATION_REMOVE = XmlService.SELF_COMBINATION_REMOVE;
56
57
58
59
60
61
62
63 public static final String DEFAULT_SELF_COMBINATION_MODE = XmlService.DEFAULT_SELF_COMBINATION_MODE;
64
65 public static final String ID_COMBINATION_MODE_ATTRIBUTE = XmlService.ID_COMBINATION_MODE_ATTRIBUTE;
66
67 public static final String KEYS_COMBINATION_MODE_ATTRIBUTE = XmlService.KEYS_COMBINATION_MODE_ATTRIBUTE;
68
69 private ChildrenTracking childrenTracking;
70 private XmlNode dom;
71
72 public Xpp3Dom(String name) {
73 this.dom = XmlNode.newBuilder().name(name).build();
74 }
75
76
77
78
79
80
81 public Xpp3Dom(String name, Object inputLocation) {
82 this.dom = XmlNode.newBuilder().name(name).inputLocation(inputLocation).build();
83 }
84
85
86
87
88
89 public Xpp3Dom(Xpp3Dom src) {
90 this(src, src.getName());
91 }
92
93
94
95
96
97
98 public Xpp3Dom(Xpp3Dom src, String name) {
99 this.dom = XmlNode.newInstance(
100 name, src.dom.value(), src.dom.attributes(), src.dom.children(), src.dom.inputLocation());
101 }
102
103 public Xpp3Dom(XmlNode dom) {
104 this.dom = dom;
105 }
106
107 public Xpp3Dom(XmlNode dom, Xpp3Dom parent) {
108 this.dom = dom;
109 this.childrenTracking = parent::replace;
110 }
111
112 public Xpp3Dom(XmlNode dom, ChildrenTracking childrenTracking) {
113 this.dom = dom;
114 this.childrenTracking = childrenTracking;
115 }
116
117 public XmlNode getDom() {
118 return dom;
119 }
120
121
122
123
124
125 public String getName() {
126 return dom.name();
127 }
128
129
130
131
132
133 public String getValue() {
134 return dom.value();
135 }
136
137 public void setValue(String value) {
138 update(XmlNode.newInstance(dom.name(), value, dom.attributes(), dom.children(), dom.inputLocation()));
139 }
140
141
142
143
144
145 public String[] getAttributeNames() {
146 return dom.attributes().keySet().toArray(EMPTY_STRING_ARRAY);
147 }
148
149 public String getAttribute(String name) {
150 return dom.attribute(name);
151 }
152
153
154
155
156
157
158
159 public boolean removeAttribute(String name) {
160 if (name != null && !name.isEmpty()) {
161 Map<String, String> attrs = new HashMap<>(dom.attributes());
162 boolean ret = attrs.remove(name) != null;
163 if (ret) {
164 update(XmlNode.newInstance(dom.name(), dom.value(), attrs, dom.children(), dom.inputLocation()));
165 }
166 return ret;
167 }
168 return false;
169 }
170
171
172
173
174
175
176
177 public void setAttribute(String name, String value) {
178 if (null == value) {
179 throw new NullPointerException("Attribute value can not be null");
180 }
181 if (null == name) {
182 throw new NullPointerException("Attribute name can not be null");
183 }
184 Map<String, String> attrs = new HashMap<>(dom.attributes());
185 attrs.put(name, value);
186 update(XmlNode.newInstance(dom.name(), dom.value(), attrs, dom.children(), dom.inputLocation()));
187 }
188
189
190
191
192
193 public Xpp3Dom getChild(int i) {
194 return new Xpp3Dom(dom.children().get(i), this);
195 }
196
197 public Xpp3Dom getChild(String name) {
198 XmlNode child = dom.child(name);
199 return child != null ? new Xpp3Dom(child, this) : null;
200 }
201
202 public void addChild(Xpp3Dom xpp3Dom) {
203 List<XmlNode> children = new ArrayList<>(dom.children());
204 children.add(xpp3Dom.dom);
205 xpp3Dom.childrenTracking = this::replace;
206 update(XmlNode.newInstance(dom.name(), dom.value(), dom.attributes(), children, dom.inputLocation()));
207 }
208
209 public Xpp3Dom[] getChildren() {
210 return dom.children().stream().map(d -> new Xpp3Dom(d, this)).toArray(Xpp3Dom[]::new);
211 }
212
213 public Xpp3Dom[] getChildren(String name) {
214 return dom.children().stream()
215 .filter(c -> c.name().equals(name))
216 .map(d -> new Xpp3Dom(d, this))
217 .toArray(Xpp3Dom[]::new);
218 }
219
220 public int getChildCount() {
221 return dom.children().size();
222 }
223
224 public void removeChild(int i) {
225 List<XmlNode> children = new ArrayList<>(dom.children());
226 children.remove(i);
227 update(XmlNode.newInstance(dom.name(), dom.value(), dom.attributes(), children, dom.inputLocation()));
228 }
229
230 public void removeChild(Xpp3Dom child) {
231 List<XmlNode> children = new ArrayList<>(dom.children());
232 children.remove(child.dom);
233 update(XmlNode.newInstance(dom.name(), dom.value(), dom.attributes(), children, dom.inputLocation()));
234 }
235
236
237
238
239
240 public Xpp3Dom getParent() {
241 throw new UnsupportedOperationException();
242 }
243
244 public void setParent(Xpp3Dom parent) {}
245
246
247
248
249
250
251
252
253
254 public Object getInputLocation() {
255 return dom.inputLocation();
256 }
257
258
259
260
261
262 public void setInputLocation(Object inputLocation) {
263 update(XmlNode.newInstance(dom.name(), dom.value(), dom.attributes(), dom.children(), inputLocation));
264 }
265
266
267
268
269
270 public void writeToSerializer(String namespace, XmlSerializer serializer) throws IOException {
271
272
273 SerializerXMLWriter xmlWriter = new SerializerXMLWriter(namespace, serializer);
274 Xpp3DomWriter.write(xmlWriter, this);
275 if (xmlWriter.getExceptions().size() > 0) {
276 throw (IOException) xmlWriter.getExceptions().get(0);
277 }
278 }
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317 private static void mergeIntoXpp3Dom(Xpp3Dom dominant, Xpp3Dom recessive, Boolean childMergeOverride) {
318
319 if (recessive == null) {
320 return;
321 }
322 dominant.dom = XmlService.merge(dominant.dom, recessive.dom, childMergeOverride);
323 }
324
325
326
327
328
329
330
331
332
333
334
335
336 public static Xpp3Dom mergeXpp3Dom(Xpp3Dom dominant, Xpp3Dom recessive, Boolean childMergeOverride) {
337 if (dominant != null) {
338 mergeIntoXpp3Dom(dominant, recessive, childMergeOverride);
339 return dominant;
340 }
341 return recessive;
342 }
343
344
345
346
347
348
349
350
351
352
353
354 public static Xpp3Dom mergeXpp3Dom(Xpp3Dom dominant, Xpp3Dom recessive) {
355 if (dominant != null) {
356 mergeIntoXpp3Dom(dominant, recessive, null);
357 return dominant;
358 }
359 return recessive;
360 }
361
362
363
364
365
366 @Override
367 public boolean equals(Object obj) {
368 if (obj == this) {
369 return true;
370 }
371
372 if (!(obj instanceof Xpp3Dom)) {
373 return false;
374 }
375
376 Xpp3Dom dom = (Xpp3Dom) obj;
377 return this.dom.equals(dom.dom);
378 }
379
380 @Override
381 public int hashCode() {
382 return dom.hashCode();
383 }
384
385 @Override
386 public String toString() {
387 return dom.toString();
388 }
389
390 public String toUnescapedString() {
391 return ((Xpp3Dom) dom).toUnescapedString();
392 }
393
394 public static boolean isNotEmpty(String str) {
395 return ((str != null) && (str.length() > 0));
396 }
397
398 public static boolean isEmpty(String str) {
399 return ((str == null) || (str.trim().length() == 0));
400 }
401
402 private void update(XmlNode dom) {
403 if (childrenTracking != null) {
404 childrenTracking.replace(this.dom, dom);
405 }
406 this.dom = dom;
407 }
408
409 private boolean replace(Object prevChild, Object newChild) {
410 List<XmlNode> children = new ArrayList<>(dom.children());
411 children.replaceAll(d -> d == prevChild ? (XmlNode) newChild : d);
412 update(XmlNode.newInstance(dom.name(), dom.value(), dom.attributes(), children, dom.inputLocation()));
413 return true;
414 }
415
416 public void setChildrenTracking(ChildrenTracking childrenTracking) {
417 this.childrenTracking = childrenTracking;
418 }
419
420 @FunctionalInterface
421 public interface ChildrenTracking {
422 boolean replace(Object oldDelegate, Object newDelegate);
423 }
424 }