1 package org.apache.maven.shared.utils.xml;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.StringWriter;
23 import java.util.ArrayList;
24 import java.util.Collections;
25 import java.util.HashMap;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.Map;
29
30 import javax.annotation.Nonnull;
31
32
33
34
35
36
37 public class Xpp3Dom
38 implements Iterable<Xpp3Dom>
39 {
40 @SuppressWarnings( "UnusedDeclaration" )
41 private static final long serialVersionUID = 2567894443061173996L;
42
43 private String name;
44
45 private String value;
46
47 private Map<String, String> attributes;
48
49 private final List<Xpp3Dom> childList;
50
51 private final Map<String, Xpp3Dom> childMap;
52
53 private Xpp3Dom parent;
54
55 public static final String CHILDREN_COMBINATION_MODE_ATTRIBUTE = "combine.children";
56
57 private static final String CHILDREN_COMBINATION_MERGE = "merge";
58
59 public static final String CHILDREN_COMBINATION_APPEND = "append";
60
61 @SuppressWarnings("UnusedDeclaration")
62 private static final String DEFAULT_CHILDREN_COMBINATION_MODE = CHILDREN_COMBINATION_MERGE;
63
64 public static final String SELF_COMBINATION_MODE_ATTRIBUTE = "combine.self";
65
66 public static final String SELF_COMBINATION_OVERRIDE = "override";
67
68 public static final String SELF_COMBINATION_MERGE = "merge";
69
70 @SuppressWarnings("UnusedDeclaration")
71 private static final String DEFAULT_SELF_COMBINATION_MODE = SELF_COMBINATION_MERGE;
72
73 private static final String[] EMPTY_STRING_ARRAY = new String[0];
74 private static final Xpp3Dom[] EMPTY_DOM_ARRAY = new Xpp3Dom[0];
75
76 public Xpp3Dom( String name )
77 {
78 this.name = name;
79 childList = new ArrayList<Xpp3Dom>();
80 childMap = new HashMap<String, Xpp3Dom>();
81 }
82
83 public Xpp3Dom( Xpp3Dom source)
84 {
85 this( source, source.getName() );
86 }
87
88 public Xpp3Dom( @Nonnull Xpp3Dom src, String name )
89 {
90 this.name = name;
91
92 int size = src.getChildCount();
93 childList = new ArrayList<Xpp3Dom>( size );
94 childMap = new HashMap<String, Xpp3Dom>();
95
96 setValue( src.getValue() );
97
98 for ( String attributeName : src.getAttributeNames() )
99 {
100 setAttribute( attributeName, src.getAttribute( attributeName ) );
101 }
102
103 for ( Xpp3Dom xpp3Dom : src.getChildren() )
104 {
105 addChild( new Xpp3Dom( xpp3Dom ) );
106 }
107 }
108
109 public String getName()
110 {
111 return name;
112 }
113
114 public @Nonnull String getValue()
115 {
116 return value;
117 }
118
119 public void setValue( @Nonnull String value )
120 {
121 this.value = value;
122 }
123
124
125 public String[] getAttributeNames()
126 {
127 boolean isNothing = attributes == null || attributes.isEmpty();
128 return isNothing ? EMPTY_STRING_ARRAY : attributes.keySet().toArray( new String[attributes.size()] );
129 }
130
131
132 public String getAttribute( String name )
133 {
134 return attributes != null ? attributes.get( name ) : null;
135 }
136
137 @SuppressWarnings( "ConstantConditions" )
138 public void setAttribute( @Nonnull String name, @Nonnull String value )
139 {
140 if ( value == null )
141 {
142 throw new NullPointerException( "value can not be null" );
143 }
144 if ( name == null )
145 {
146 throw new NullPointerException( "name can not be null" );
147 }
148 if ( attributes == null )
149 {
150 attributes = new HashMap<String, String>();
151 }
152
153 attributes.put( name, value );
154 }
155
156 public Xpp3Dom getChild( int i )
157 {
158 return childList.get( i );
159 }
160
161 public Xpp3Dom getChild( String name )
162 {
163 return childMap.get( name );
164 }
165
166 public void addChild( Xpp3Dom child )
167 {
168 child.setParent( this );
169 childList.add( child );
170 childMap.put( child.getName(), child );
171 }
172
173 public Xpp3Dom[] getChildren()
174 {
175 boolean isNothing = childList == null || childList.isEmpty();
176 return isNothing ? EMPTY_DOM_ARRAY : childList.toArray( new Xpp3Dom[childList.size()] );
177 }
178
179 private List<Xpp3Dom> getChildrenList()
180 {
181 boolean isNothing = childList == null || childList.isEmpty();
182 return isNothing ? Collections.<Xpp3Dom>emptyList() : childList;
183 }
184
185 public Xpp3Dom[] getChildren( String name )
186 {
187 List<Xpp3Dom> children = getChildrenList( name );
188 return children.toArray( new Xpp3Dom[children.size()] );
189 }
190
191 private List<Xpp3Dom> getChildrenList( String name )
192 {
193 if ( childList == null )
194 {
195 return Collections.emptyList();
196 }
197 else
198 {
199 ArrayList<Xpp3Dom> children = new ArrayList<Xpp3Dom>();
200 for ( Xpp3Dom aChildList : childList )
201 {
202 if ( name.equals( aChildList.getName() ) )
203 {
204 children.add( aChildList );
205 }
206 }
207 return children;
208 }
209 }
210
211 public int getChildCount()
212 {
213 if ( childList == null )
214 {
215 return 0;
216 }
217
218 return childList.size();
219 }
220
221 public void removeChild( int i )
222 {
223 Xpp3Dom child = childList.remove( i );
224 childMap.values().remove( child );
225 child.setParent( null );
226 }
227
228 public Xpp3Dom getParent()
229 {
230 return parent;
231 }
232
233 public void setParent( Xpp3Dom parent )
234 {
235 this.parent = parent;
236 }
237
238
239
240
241
242 private static Xpp3Dom merge( Xpp3Dom dominant, Xpp3Dom recessive, Boolean childMergeOverride )
243 {
244 if ( recessive == null || isCombineSelfOverride( dominant ) )
245 {
246 return dominant;
247 }
248
249 if ( isEmpty( dominant.getValue() ) )
250 {
251 dominant.setValue( recessive.getValue() );
252 }
253
254 for ( String attr : recessive.getAttributeNames() )
255 {
256 if ( isEmpty( dominant.getAttribute( attr ) ) )
257 {
258 dominant.setAttribute( attr, recessive.getAttribute( attr ) );
259 }
260 }
261
262 if ( recessive.getChildCount() > 0 )
263 {
264 boolean mergeChildren = isMergeChildren( dominant, childMergeOverride );
265
266 if ( mergeChildren )
267 {
268 Map<String, Iterator<Xpp3Dom>> commonChildren = getCommonChildren( dominant, recessive );
269 for ( Xpp3Dom recessiveChild : recessive )
270 {
271 Iterator<Xpp3Dom> it = commonChildren.get( recessiveChild.getName() );
272 if ( it == null )
273 {
274 dominant.addChild( new Xpp3Dom( recessiveChild ) );
275 }
276 else if ( it.hasNext() )
277 {
278 Xpp3Dom dominantChild = it.next();
279 merge( dominantChild, recessiveChild, childMergeOverride );
280 }
281 }
282 }
283 else
284 {
285 Xpp3Dom[] dominantChildren = dominant.getChildren();
286 dominant.childList.clear();
287 for ( Xpp3Dom child : recessive )
288 {
289 dominant.addChild( new Xpp3Dom( child ) );
290 }
291
292 for ( Xpp3Dom aDominantChildren : dominantChildren )
293 {
294 dominant.addChild( aDominantChildren );
295 }
296 }
297 }
298 return dominant;
299 }
300
301 private static Map<String, Iterator<Xpp3Dom>> getCommonChildren( Xpp3Dom dominant, Xpp3Dom recessive )
302 {
303 Map<String, Iterator<Xpp3Dom>> commonChildren = new HashMap<String, Iterator<Xpp3Dom>>();
304
305 for ( String childName : recessive.childMap.keySet() )
306 {
307 List<Xpp3Dom> dominantChildren = dominant.getChildrenList( childName );
308 if ( dominantChildren.size() > 0 )
309 {
310 commonChildren.put( childName, dominantChildren.iterator() );
311 }
312 }
313 return commonChildren;
314 }
315
316 private static boolean isMergeChildren( Xpp3Dom dominant, Boolean override )
317 {
318 return override != null ? override : !isMergeChildren( dominant );
319 }
320
321 private static boolean isMergeChildren( Xpp3Dom dominant )
322 {
323 return CHILDREN_COMBINATION_APPEND.equals( dominant.getAttribute( CHILDREN_COMBINATION_MODE_ATTRIBUTE ) );
324 }
325
326 private static boolean isCombineSelfOverride( Xpp3Dom xpp3Dom )
327 {
328 String selfMergeMode = xpp3Dom.getAttribute( SELF_COMBINATION_MODE_ATTRIBUTE );
329 return SELF_COMBINATION_OVERRIDE.equals( selfMergeMode );
330 }
331
332 public static Xpp3Dom mergeXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive, Boolean childMergeOverride )
333 {
334 return dominant != null ? merge( dominant, recessive, childMergeOverride ) : recessive;
335 }
336
337 public static Xpp3Dom mergeXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive )
338 {
339 return dominant != null ? merge( dominant, recessive, null ) : recessive;
340 }
341
342 public boolean equals( Object obj )
343 {
344 if ( obj == this )
345 {
346 return true;
347 }
348
349 if ( !( obj instanceof Xpp3Dom ) )
350 {
351 return false;
352 }
353
354 Xpp3Dom dom = (Xpp3Dom) obj;
355
356 return !( name == null ? dom.name != null : !name.equals( dom.name ) )
357 && !( value == null ? dom.value != null : !value.equals( dom.value ) )
358 && !( attributes == null ? dom.attributes != null : !attributes.equals( dom.attributes ) )
359 && !( childList == null ? dom.childList != null : !childList.equals( dom.childList ) );
360 }
361
362 public int hashCode()
363 {
364 int result = 17;
365 result = 37 * result + ( name != null ? name.hashCode() : 0 );
366 result = 37 * result + ( value != null ? value.hashCode() : 0 );
367 result = 37 * result + ( attributes != null ? attributes.hashCode() : 0 );
368 result = 37 * result + ( childList != null ? childList.hashCode() : 0 );
369 return result;
370 }
371
372 public String toString()
373 {
374 StringWriter writer = new StringWriter();
375 Xpp3DomWriter.write( getPrettyPrintXMLWriter( writer ), this );
376 return writer.toString();
377
378 }
379
380 public String toUnescapedString()
381 {
382 StringWriter writer = new StringWriter();
383 Xpp3DomWriter.write( getPrettyPrintXMLWriter( writer ), this, false );
384 return writer.toString();
385 }
386
387 private PrettyPrintXMLWriter getPrettyPrintXMLWriter( StringWriter writer )
388 {
389 return new PrettyPrintXMLWriter( writer, "UTF-8", null );
390 }
391
392 public static boolean isNotEmpty( String str )
393 {
394 return str != null && str.length() > 0;
395 }
396
397 public static boolean isEmpty( String str )
398 {
399 return str == null || str.trim().length() == 0;
400 }
401
402 public Iterator<Xpp3Dom> iterator()
403 {
404 return getChildrenList().iterator();
405 }
406 }