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