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.io.StringWriter;
22 import java.util.ArrayList;
23 import java.util.Collections;
24 import java.util.HashMap;
25 import java.util.Iterator;
26 import java.util.List;
27 import java.util.ListIterator;
28 import java.util.Map;
29
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 long serialVersionUID = 2567894443061173996L;
38
39 protected String name;
40
41 protected String value;
42
43 protected Map<String, String> attributes;
44
45 protected final List<Xpp3Dom> childList;
46
47 protected Xpp3Dom parent;
48
49
50
51
52 protected Object inputLocation;
53
54 private static final String[] EMPTY_STRING_ARRAY = new String[0];
55
56 private static final Xpp3Dom[] EMPTY_DOM_ARRAY = new Xpp3Dom[0];
57
58 public static final String CHILDREN_COMBINATION_MODE_ATTRIBUTE = "combine.children";
59
60 public static final String CHILDREN_COMBINATION_MERGE = "merge";
61
62 public static final String CHILDREN_COMBINATION_APPEND = "append";
63
64
65
66
67
68
69 public static final String DEFAULT_CHILDREN_COMBINATION_MODE = CHILDREN_COMBINATION_MERGE;
70
71 public static final String SELF_COMBINATION_MODE_ATTRIBUTE = "combine.self";
72
73 public static final String SELF_COMBINATION_OVERRIDE = "override";
74
75 public static final String SELF_COMBINATION_MERGE = "merge";
76
77 public static final String SELF_COMBINATION_REMOVE = "remove";
78
79
80
81
82
83
84
85 public static final String DEFAULT_SELF_COMBINATION_MODE = SELF_COMBINATION_MERGE;
86
87 public Xpp3Dom(String name) {
88 this.name = name;
89 childList = new ArrayList<>();
90 }
91
92
93
94
95
96
97 public Xpp3Dom(String name, Object inputLocation) {
98 this(name);
99 this.inputLocation = inputLocation;
100 }
101
102
103
104
105
106 public Xpp3Dom(Xpp3Dom src) {
107 this(src, src.getName());
108 }
109
110
111
112
113
114
115 public Xpp3Dom(Xpp3Dom src, String name) {
116 this.name = name;
117 this.inputLocation = src.inputLocation;
118
119 int childCount = src.getChildCount();
120
121 childList = new ArrayList<Xpp3Dom>(childCount);
122
123 setValue(src.getValue());
124
125 String[] attributeNames = src.getAttributeNames();
126 for (String attributeName : attributeNames) {
127 setAttribute(attributeName, src.getAttribute(attributeName));
128 }
129
130 for (int i = 0; i < childCount; i++) {
131 addChild(new Xpp3Dom(src.getChild(i)));
132 }
133 }
134
135
136
137
138
139 public String getName() {
140 return name;
141 }
142
143
144
145
146
147 public String getValue() {
148 return value;
149 }
150
151 public void setValue(String value) {
152 this.value = value;
153 }
154
155
156
157
158
159 public String[] getAttributeNames() {
160 if (null == attributes || attributes.isEmpty()) {
161 return EMPTY_STRING_ARRAY;
162 } else {
163 return attributes.keySet().toArray(EMPTY_STRING_ARRAY);
164 }
165 }
166
167 public String getAttribute(String name) {
168 return (null != attributes) ? attributes.get(name) : null;
169 }
170
171
172
173
174
175
176
177 public boolean removeAttribute(String name) {
178 return StringUtils.isEmpty(name) ? false : attributes.remove(name) == null;
179 }
180
181
182
183
184
185
186
187 public void setAttribute(String name, String value) {
188 if (null == value) {
189 throw new NullPointerException("Attribute value can not be null");
190 }
191 if (null == name) {
192 throw new NullPointerException("Attribute name can not be null");
193 }
194 if (null == attributes) {
195 attributes = new HashMap<String, String>();
196 }
197
198 attributes.put(name, value);
199 }
200
201
202
203
204
205 public Xpp3Dom getChild(int i) {
206 return childList.get(i);
207 }
208
209 public Xpp3Dom getChild(String name) {
210 if (name != null) {
211 ListIterator<Xpp3Dom> it = childList.listIterator(childList.size());
212 while (it.hasPrevious()) {
213 Xpp3Dom child = it.previous();
214 if (name.equals(child.getName())) {
215 return child;
216 }
217 }
218 }
219 return null;
220 }
221
222 public void addChild(Xpp3Dom xpp3Dom) {
223 xpp3Dom.setParent(this);
224 childList.add(xpp3Dom);
225 }
226
227 public Xpp3Dom[] getChildren() {
228 if (null == childList || childList.isEmpty()) {
229 return EMPTY_DOM_ARRAY;
230 } else {
231 return childList.toArray(EMPTY_DOM_ARRAY);
232 }
233 }
234
235 public Xpp3Dom[] getChildren(String name) {
236 return getChildrenAsList(name).toArray(EMPTY_DOM_ARRAY);
237 }
238
239 private List<Xpp3Dom> getChildrenAsList(String name) {
240 if (null == childList) {
241 return Collections.emptyList();
242 } else {
243 ArrayList<Xpp3Dom> children = null;
244
245 for (Xpp3Dom configuration : childList) {
246 if (name.equals(configuration.getName())) {
247 if (children == null) {
248 children = new ArrayList<Xpp3Dom>();
249 }
250 children.add(configuration);
251 }
252 }
253
254 if (children != null) {
255 return children;
256 } else {
257 return Collections.emptyList();
258 }
259 }
260 }
261
262 public int getChildCount() {
263 if (null == childList) {
264 return 0;
265 }
266
267 return childList.size();
268 }
269
270 public void removeChild(int i) {
271 Xpp3Dom child = getChild(i);
272 childList.remove(i);
273
274 child.setParent(null);
275 }
276
277 public void removeChild(Xpp3Dom child) {
278 childList.remove(child);
279
280 child.setParent(null);
281 }
282
283
284
285
286
287 public Xpp3Dom getParent() {
288 return parent;
289 }
290
291 public void setParent(Xpp3Dom parent) {
292 this.parent = parent;
293 }
294
295
296
297
298
299
300
301
302
303 public Object getInputLocation() {
304 return inputLocation;
305 }
306
307
308
309
310
311 public void setInputLocation(Object inputLocation) {
312 this.inputLocation = inputLocation;
313 }
314
315
316
317
318
319 public void writeToSerializer(String namespace, XmlSerializer serializer) throws IOException {
320
321
322 SerializerXMLWriter xmlWriter = new SerializerXMLWriter(namespace, serializer);
323 Xpp3DomWriter.write(xmlWriter, this);
324 if (xmlWriter.getExceptions().size() > 0) {
325 throw (IOException) xmlWriter.getExceptions().get(0);
326 }
327 }
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366 private static void mergeIntoXpp3Dom(Xpp3Dom dominant, Xpp3Dom recessive, Boolean childMergeOverride) {
367
368 if (recessive == null) {
369 return;
370 }
371
372 boolean mergeSelf = true;
373
374 String selfMergeMode = dominant.getAttribute(SELF_COMBINATION_MODE_ATTRIBUTE);
375
376 if (SELF_COMBINATION_OVERRIDE.equals(selfMergeMode)) {
377 mergeSelf = false;
378 }
379
380 if (mergeSelf) {
381 if (isEmpty(dominant.getValue()) && !isEmpty(recessive.getValue())) {
382 dominant.setValue(recessive.getValue());
383 dominant.setInputLocation(recessive.getInputLocation());
384 }
385
386 if (recessive.attributes != null) {
387 for (String attr : recessive.attributes.keySet()) {
388 if (isEmpty(dominant.getAttribute(attr))) {
389 dominant.setAttribute(attr, recessive.getAttribute(attr));
390 }
391 }
392 }
393
394 if (recessive.getChildCount() > 0) {
395 boolean mergeChildren = true;
396
397 if (childMergeOverride != null) {
398 mergeChildren = childMergeOverride;
399 } else {
400 String childMergeMode = dominant.getAttribute(CHILDREN_COMBINATION_MODE_ATTRIBUTE);
401
402 if (CHILDREN_COMBINATION_APPEND.equals(childMergeMode)) {
403 mergeChildren = false;
404 }
405 }
406
407 if (!mergeChildren) {
408 Xpp3Dom[] dominantChildren = dominant.getChildren();
409
410 dominant.childList.clear();
411
412 for (int i = 0, recessiveChildCount = recessive.getChildCount(); i < recessiveChildCount; i++) {
413 Xpp3Dom recessiveChild = recessive.getChild(i);
414 dominant.addChild(new Xpp3Dom(recessiveChild));
415 }
416
417
418 for (Xpp3Dom aDominantChildren : dominantChildren) {
419 dominant.addChild(aDominantChildren);
420 }
421 } else {
422 Map<String, Iterator<Xpp3Dom>> commonChildren = new HashMap<String, Iterator<Xpp3Dom>>();
423
424 for (Xpp3Dom recChild : recessive.childList) {
425 if (commonChildren.containsKey(recChild.name)) {
426 continue;
427 }
428 List<Xpp3Dom> dominantChildren = dominant.getChildrenAsList(recChild.name);
429 if (dominantChildren.size() > 0) {
430 commonChildren.put(recChild.name, dominantChildren.iterator());
431 }
432 }
433
434 for (int i = 0, recessiveChildCount = recessive.getChildCount(); i < recessiveChildCount; i++) {
435 Xpp3Dom recessiveChild = recessive.getChild(i);
436 Iterator<Xpp3Dom> it = commonChildren.get(recessiveChild.getName());
437 if (it == null) {
438 dominant.addChild(new Xpp3Dom(recessiveChild));
439 } else if (it.hasNext()) {
440 Xpp3Dom dominantChild = it.next();
441
442 String dominantChildCombinationMode =
443 dominantChild.getAttribute(SELF_COMBINATION_MODE_ATTRIBUTE);
444 if (SELF_COMBINATION_REMOVE.equals(dominantChildCombinationMode)) {
445 dominant.removeChild(dominantChild);
446 } else {
447 mergeIntoXpp3Dom(dominantChild, recessiveChild, childMergeOverride);
448 }
449 }
450 }
451 }
452 }
453 }
454 }
455
456
457
458
459
460
461
462
463
464
465
466
467 public static Xpp3Dom mergeXpp3Dom(Xpp3Dom dominant, Xpp3Dom recessive, Boolean childMergeOverride) {
468 if (dominant != null) {
469 mergeIntoXpp3Dom(dominant, recessive, childMergeOverride);
470 return dominant;
471 }
472 return recessive;
473 }
474
475
476
477
478
479
480
481
482
483
484
485 public static Xpp3Dom mergeXpp3Dom(Xpp3Dom dominant, Xpp3Dom recessive) {
486 if (dominant != null) {
487 mergeIntoXpp3Dom(dominant, recessive, null);
488 return dominant;
489 }
490 return recessive;
491 }
492
493
494
495
496
497 @Override
498 public boolean equals(Object obj) {
499 if (obj == this) {
500 return true;
501 }
502
503 if (!(obj instanceof Xpp3Dom)) {
504 return false;
505 }
506
507 Xpp3Dom dom = (Xpp3Dom) obj;
508
509 if (name == null ? dom.name != null : !name.equals(dom.name)) {
510 return false;
511 } else if (value == null ? dom.value != null : !value.equals(dom.value)) {
512 return false;
513 } else if (attributes == null ? dom.attributes != null : !attributes.equals(dom.attributes)) {
514 return false;
515 } else if (childList == null ? dom.childList != null : !childList.equals(dom.childList)) {
516 return false;
517 } else {
518 return true;
519 }
520 }
521
522 @Override
523 public int hashCode() {
524 int result = 17;
525 result = 37 * result + (name != null ? name.hashCode() : 0);
526 result = 37 * result + (value != null ? value.hashCode() : 0);
527 result = 37 * result + (attributes != null ? attributes.hashCode() : 0);
528 result = 37 * result + (childList != null ? childList.hashCode() : 0);
529 return result;
530 }
531
532 @Override
533 public String toString() {
534
535
536 StringWriter writer = new StringWriter();
537 XMLWriter xmlWriter = new PrettyPrintXMLWriter(writer, "UTF-8", null);
538 Xpp3DomWriter.write(xmlWriter, this);
539 return writer.toString();
540 }
541
542 public String toUnescapedString() {
543
544
545 StringWriter writer = new StringWriter();
546 XMLWriter xmlWriter = new PrettyPrintXMLWriter(writer, "UTF-8", null);
547 Xpp3DomWriter.write(xmlWriter, this, false);
548 return writer.toString();
549 }
550
551 public static boolean isNotEmpty(String str) {
552 return ((str != null) && (str.length() > 0));
553 }
554
555 public static boolean isEmpty(String str) {
556 return ((str == null) || (str.trim().length() == 0));
557 }
558 }