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 java.io.IOException;
20  import java.io.Serializable;
21  import java.util.ArrayList;
22  import java.util.HashMap;
23  import java.util.List;
24  import java.util.Map;
25  
26  import org.apache.maven.api.xml.XmlNode;
27  import org.apache.maven.internal.xml.XmlNodeImpl;
28  import org.codehaus.plexus.util.xml.pull.XmlSerializer;
29  
30  
31  
32  
33  public class Xpp3Dom implements Serializable {
34      private static final String[] EMPTY_STRING_ARRAY = new String[0];
35  
36      public static final String CHILDREN_COMBINATION_MODE_ATTRIBUTE = XmlNode.CHILDREN_COMBINATION_MODE_ATTRIBUTE;
37  
38      public static final String CHILDREN_COMBINATION_MERGE = XmlNode.CHILDREN_COMBINATION_MERGE;
39  
40      public static final String CHILDREN_COMBINATION_APPEND = XmlNode.CHILDREN_COMBINATION_APPEND;
41  
42      
43  
44  
45  
46  
47      public static final String DEFAULT_CHILDREN_COMBINATION_MODE = XmlNode.DEFAULT_CHILDREN_COMBINATION_MODE;
48  
49      public static final String SELF_COMBINATION_MODE_ATTRIBUTE = XmlNode.SELF_COMBINATION_MODE_ATTRIBUTE;
50  
51      public static final String SELF_COMBINATION_OVERRIDE = XmlNode.SELF_COMBINATION_OVERRIDE;
52  
53      public static final String SELF_COMBINATION_MERGE = XmlNode.SELF_COMBINATION_MERGE;
54  
55      public static final String SELF_COMBINATION_REMOVE = XmlNode.SELF_COMBINATION_REMOVE;
56  
57      
58  
59  
60  
61  
62  
63      public static final String DEFAULT_SELF_COMBINATION_MODE = XmlNode.DEFAULT_SELF_COMBINATION_MODE;
64  
65      public static final String ID_COMBINATION_MODE_ATTRIBUTE = XmlNode.ID_COMBINATION_MODE_ATTRIBUTE;
66  
67      public static final String KEYS_COMBINATION_MODE_ATTRIBUTE = XmlNode.KEYS_COMBINATION_MODE_ATTRIBUTE;
68  
69      private ChildrenTracking childrenTracking;
70      private XmlNode dom;
71  
72      public Xpp3Dom(String name) {
73          this.dom = new XmlNodeImpl(name);
74      }
75  
76      
77  
78  
79  
80  
81      public Xpp3Dom(String name, Object inputLocation) {
82          this.dom = new XmlNodeImpl(name, null, null, null, inputLocation);
83      }
84  
85      
86  
87  
88  
89      public Xpp3Dom(Xpp3Dom src) {
90          this(src, src.getName());
91      }
92  
93      
94  
95  
96  
97  
98      public Xpp3Dom(Xpp3Dom src, String name) {
99          this.dom = new XmlNodeImpl(src.dom, name);
100     }
101 
102     public Xpp3Dom(XmlNode dom) {
103         this.dom = dom;
104     }
105 
106     public Xpp3Dom(XmlNode dom, Xpp3Dom parent) {
107         this.dom = dom;
108         this.childrenTracking = parent::replace;
109     }
110 
111     public Xpp3Dom(XmlNode dom, ChildrenTracking childrenTracking) {
112         this.dom = dom;
113         this.childrenTracking = childrenTracking;
114     }
115 
116     public XmlNode getDom() {
117         return dom;
118     }
119 
120     
121     
122     
123 
124     public String getName() {
125         return dom.getName();
126     }
127 
128     
129     
130     
131 
132     public String getValue() {
133         return dom.getValue();
134     }
135 
136     public void setValue(String value) {
137         update(new XmlNodeImpl(dom.getName(), value, dom.getAttributes(), dom.getChildren(), dom.getInputLocation()));
138     }
139 
140     
141     
142     
143 
144     public String[] getAttributeNames() {
145         return dom.getAttributes().keySet().toArray(EMPTY_STRING_ARRAY);
146     }
147 
148     public String getAttribute(String name) {
149         return dom.getAttribute(name);
150     }
151 
152     
153 
154 
155 
156 
157 
158     public boolean removeAttribute(String name) {
159         if (name != null && !name.isEmpty()) {
160             Map<String, String> attrs = new HashMap<>(dom.getAttributes());
161             boolean ret = attrs.remove(name) != null;
162             if (ret) {
163                 update(new XmlNodeImpl(
164                         dom.getName(), dom.getValue(), attrs, dom.getChildren(), dom.getInputLocation()));
165             }
166             return ret;
167         }
168         return false;
169     }
170 
171     
172 
173 
174 
175 
176 
177     public void setAttribute(String name, String value) {
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         Map<String, String> attrs = new HashMap<>(dom.getAttributes());
185         attrs.put(name, value);
186         update(new XmlNodeImpl(dom.getName(), dom.getValue(), attrs, dom.getChildren(), dom.getInputLocation()));
187     }
188 
189     
190     
191     
192 
193     public Xpp3Dom getChild(int i) {
194         return new Xpp3Dom(dom.getChildren().get(i), this);
195     }
196 
197     public Xpp3Dom getChild(String name) {
198         XmlNode child = dom.getChild(name);
199         return child != null ? new Xpp3Dom(child, this) : null;
200     }
201 
202     public void addChild(Xpp3Dom xpp3Dom) {
203         List<XmlNode> children = new ArrayList<>(dom.getChildren());
204         children.add(xpp3Dom.dom);
205         xpp3Dom.childrenTracking = this::replace;
206         update(new XmlNodeImpl(dom.getName(), dom.getValue(), dom.getAttributes(), children, dom.getInputLocation()));
207     }
208 
209     public Xpp3Dom[] getChildren() {
210         return dom.getChildren().stream().map(d -> new Xpp3Dom(d, this)).toArray(Xpp3Dom[]::new);
211     }
212 
213     public Xpp3Dom[] getChildren(String name) {
214         return dom.getChildren().stream()
215                 .filter(c -> c.getName().equals(name))
216                 .map(d -> new Xpp3Dom(d, this))
217                 .toArray(Xpp3Dom[]::new);
218     }
219 
220     public int getChildCount() {
221         return dom.getChildren().size();
222     }
223 
224     public void removeChild(int i) {
225         List<XmlNode> children = new ArrayList<>(dom.getChildren());
226         children.remove(i);
227         update(new XmlNodeImpl(dom.getName(), dom.getValue(), dom.getAttributes(), children, dom.getInputLocation()));
228     }
229 
230     public void removeChild(Xpp3Dom child) {
231         List<XmlNode> children = new ArrayList<>(dom.getChildren());
232         children.remove(child.dom);
233         update(new XmlNodeImpl(dom.getName(), dom.getValue(), dom.getAttributes(), children, dom.getInputLocation()));
234     }
235 
236     
237     
238     
239 
240     public Xpp3Dom getParent() {
241         throw new UnsupportedOperationException();
242     }
243 
244     public void setParent(Xpp3Dom parent) {}
245 
246     
247     
248     
249 
250     
251 
252 
253 
254     public Object getInputLocation() {
255         return dom.getInputLocation();
256     }
257 
258     
259 
260 
261 
262     public void setInputLocation(Object inputLocation) {
263         update(new XmlNodeImpl(dom.getName(), dom.getValue(), dom.getAttributes(), dom.getChildren(), inputLocation));
264     }
265 
266     
267     
268     
269 
270     public void writeToSerializer(String namespace, XmlSerializer serializer) throws IOException {
271         
272         
273         SerializerXMLWriter xmlWriter = new SerializerXMLWriter(namespace, serializer);
274         Xpp3DomWriter.write(xmlWriter, this);
275         if (xmlWriter.getExceptions().size() > 0) {
276             throw (IOException) xmlWriter.getExceptions().get(0);
277         }
278     }
279 
280     
281 
282 
283 
284 
285 
286 
287 
288 
289 
290 
291 
292 
293 
294 
295 
296 
297 
298 
299 
300 
301 
302 
303 
304 
305 
306 
307 
308 
309 
310 
311 
312 
313 
314 
315 
316 
317     private static void mergeIntoXpp3Dom(Xpp3Dom dominant, Xpp3Dom recessive, Boolean childMergeOverride) {
318         
319         if (recessive == null) {
320             return;
321         }
322         dominant.dom = dominant.dom.merge(recessive.dom, childMergeOverride);
323     }
324 
325     
326 
327 
328 
329 
330 
331 
332 
333 
334 
335 
336     public static Xpp3Dom mergeXpp3Dom(Xpp3Dom dominant, Xpp3Dom recessive, Boolean childMergeOverride) {
337         if (dominant != null) {
338             mergeIntoXpp3Dom(dominant, recessive, childMergeOverride);
339             return dominant;
340         }
341         return recessive;
342     }
343 
344     
345 
346 
347 
348 
349 
350 
351 
352 
353 
354     public static Xpp3Dom mergeXpp3Dom(Xpp3Dom dominant, Xpp3Dom recessive) {
355         if (dominant != null) {
356             mergeIntoXpp3Dom(dominant, recessive, null);
357             return dominant;
358         }
359         return recessive;
360     }
361 
362     
363     
364     
365 
366     @Override
367     public boolean equals(Object obj) {
368         if (obj == this) {
369             return true;
370         }
371 
372         if (!(obj instanceof Xpp3Dom)) {
373             return false;
374         }
375 
376         Xpp3Dom dom = (Xpp3Dom) obj;
377         return this.dom.equals(dom.dom);
378     }
379 
380     @Override
381     public int hashCode() {
382         return dom.hashCode();
383     }
384 
385     @Override
386     public String toString() {
387         return dom.toString();
388     }
389 
390     public String toUnescapedString() {
391         return ((Xpp3Dom) dom).toUnescapedString();
392     }
393 
394     public static boolean isNotEmpty(String str) {
395         return ((str != null) && (str.length() > 0));
396     }
397 
398     public static boolean isEmpty(String str) {
399         return ((str == null) || (str.trim().length() == 0));
400     }
401 
402     private void update(XmlNode dom) {
403         if (childrenTracking != null) {
404             childrenTracking.replace(this.dom, dom);
405         }
406         this.dom = dom;
407     }
408 
409     private boolean replace(Object prevChild, Object newChild) {
410         List<XmlNode> children = new ArrayList<>(dom.getChildren());
411         children.replaceAll(d -> d == prevChild ? (XmlNode) newChild : d);
412         update(new XmlNodeImpl(dom.getName(), dom.getValue(), dom.getAttributes(), children, dom.getInputLocation()));
413         return true;
414     }
415 
416     public void setChildrenTracking(ChildrenTracking childrenTracking) {
417         this.childrenTracking = childrenTracking;
418     }
419 
420     @FunctionalInterface
421     public interface ChildrenTracking {
422         boolean replace(Object oldDelegate, Object newDelegate);
423     }
424 }