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