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