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