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