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.internal.xml.XmlNodeImpl;
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 = XmlNode.CHILDREN_COMBINATION_MODE_ATTRIBUTE;
37
38 public static final String CHILDREN_COMBINATION_MERGE = XmlNode.CHILDREN_COMBINATION_MERGE;
39
40 public static final String CHILDREN_COMBINATION_APPEND = XmlNode.CHILDREN_COMBINATION_APPEND;
41
42
43
44
45
46
47 public static final String DEFAULT_CHILDREN_COMBINATION_MODE = XmlNode.DEFAULT_CHILDREN_COMBINATION_MODE;
48
49 public static final String SELF_COMBINATION_MODE_ATTRIBUTE = XmlNode.SELF_COMBINATION_MODE_ATTRIBUTE;
50
51 public static final String SELF_COMBINATION_OVERRIDE = XmlNode.SELF_COMBINATION_OVERRIDE;
52
53 public static final String SELF_COMBINATION_MERGE = XmlNode.SELF_COMBINATION_MERGE;
54
55 public static final String SELF_COMBINATION_REMOVE = XmlNode.SELF_COMBINATION_REMOVE;
56
57
58
59
60
61
62
63 public static final String DEFAULT_SELF_COMBINATION_MODE = XmlNode.DEFAULT_SELF_COMBINATION_MODE;
64
65 public static final String ID_COMBINATION_MODE_ATTRIBUTE = XmlNode.ID_COMBINATION_MODE_ATTRIBUTE;
66
67 public static final String KEYS_COMBINATION_MODE_ATTRIBUTE = XmlNode.KEYS_COMBINATION_MODE_ATTRIBUTE;
68
69 private ChildrenTracking childrenTracking;
70 private XmlNode dom;
71
72 public Xpp3Dom(String name) {
73 this.dom = new XmlNodeImpl(name);
74 }
75
76
77
78
79
80
81 public Xpp3Dom(String name, Object inputLocation) {
82 this.dom = new XmlNodeImpl(name, null, null, null, inputLocation);
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 = new XmlNodeImpl(src.dom, name);
100 }
101
102 public Xpp3Dom(XmlNode dom) {
103 this.dom = dom;
104 }
105
106 public Xpp3Dom(XmlNode dom, Xpp3Dom parent) {
107 this.dom = dom;
108 this.childrenTracking = parent::replace;
109 }
110
111 public Xpp3Dom(XmlNode dom, ChildrenTracking childrenTracking) {
112 this.dom = dom;
113 this.childrenTracking = childrenTracking;
114 }
115
116 public XmlNode getDom() {
117 return dom;
118 }
119
120
121
122
123
124 public String getName() {
125 return dom.getName();
126 }
127
128
129
130
131
132 public String getValue() {
133 return dom.getValue();
134 }
135
136 public void setValue(String value) {
137 update(new XmlNodeImpl(dom.getName(), value, dom.getAttributes(), dom.getChildren(), dom.getInputLocation()));
138 }
139
140
141
142
143
144 public String[] getAttributeNames() {
145 return dom.getAttributes().keySet().toArray(EMPTY_STRING_ARRAY);
146 }
147
148 public String getAttribute(String name) {
149 return dom.getAttribute(name);
150 }
151
152
153
154
155
156
157
158 public boolean removeAttribute(String name) {
159 if (name != null && !name.isEmpty()) {
160 Map<String, String> attrs = new HashMap<>(dom.getAttributes());
161 boolean ret = attrs.remove(name) != null;
162 if (ret) {
163 update(new XmlNodeImpl(
164 dom.getName(), dom.getValue(), attrs, dom.getChildren(), dom.getInputLocation()));
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.getAttributes());
185 attrs.put(name, value);
186 update(new XmlNodeImpl(dom.getName(), dom.getValue(), attrs, dom.getChildren(), dom.getInputLocation()));
187 }
188
189
190
191
192
193 public Xpp3Dom getChild(int i) {
194 return new Xpp3Dom(dom.getChildren().get(i), this);
195 }
196
197 public Xpp3Dom getChild(String name) {
198 XmlNode child = dom.getChild(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.getChildren());
204 children.add(xpp3Dom.dom);
205 xpp3Dom.childrenTracking = this::replace;
206 update(new XmlNodeImpl(dom.getName(), dom.getValue(), dom.getAttributes(), children, dom.getInputLocation()));
207 }
208
209 public Xpp3Dom[] getChildren() {
210 return dom.getChildren().stream().map(d -> new Xpp3Dom(d, this)).toArray(Xpp3Dom[]::new);
211 }
212
213 public Xpp3Dom[] getChildren(String name) {
214 return dom.getChildren().stream()
215 .filter(c -> c.getName().equals(name))
216 .map(d -> new Xpp3Dom(d, this))
217 .toArray(Xpp3Dom[]::new);
218 }
219
220 public int getChildCount() {
221 return dom.getChildren().size();
222 }
223
224 public void removeChild(int i) {
225 List<XmlNode> children = new ArrayList<>(dom.getChildren());
226 children.remove(i);
227 update(new XmlNodeImpl(dom.getName(), dom.getValue(), dom.getAttributes(), children, dom.getInputLocation()));
228 }
229
230 public void removeChild(Xpp3Dom child) {
231 List<XmlNode> children = new ArrayList<>(dom.getChildren());
232 children.remove(child.dom);
233 update(new XmlNodeImpl(dom.getName(), dom.getValue(), dom.getAttributes(), children, dom.getInputLocation()));
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.getInputLocation();
256 }
257
258
259
260
261
262 public void setInputLocation(Object inputLocation) {
263 update(new XmlNodeImpl(dom.getName(), dom.getValue(), dom.getAttributes(), dom.getChildren(), 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 = dominant.dom.merge(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.getChildren());
411 children.replaceAll(d -> d == prevChild ? (XmlNode) newChild : d);
412 update(new XmlNodeImpl(dom.getName(), dom.getValue(), dom.getAttributes(), children, dom.getInputLocation()));
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 }