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