View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.shared.utils.xml;
20  
21  import javax.annotation.Nonnull;
22  
23  import java.io.IOException;
24  import java.io.StringWriter;
25  import java.util.ArrayList;
26  import java.util.Collections;
27  import java.util.HashMap;
28  import java.util.Iterator;
29  import java.util.List;
30  import java.util.Map;
31  
32  /**
33   * A reimplementation of Plexus Xpp3Dom based on the public interface of Plexus Xpp3Dom.
34   *
35   * @author Kristian Rosenvold
36   */
37  public class Xpp3Dom implements Iterable<Xpp3Dom> {
38      private static final long serialVersionUID = 2567894443061173996L;
39  
40      private String name; // plexus: protected
41  
42      private String value; // plexus: protected
43  
44      private Map<String, String> attributes; // plexus: protected
45  
46      final List<Xpp3Dom> childList; // plexus: protected
47  
48      final Map<String, Xpp3Dom> childMap; // plexus: protected
49  
50      private Xpp3Dom parent; // plexus: protected
51  
52      /**
53       * The attribute which identifies merge/append.
54       */
55      public static final String CHILDREN_COMBINATION_MODE_ATTRIBUTE = "combine.children";
56  
57      private static final String CHILDREN_COMBINATION_MERGE = "merge";
58  
59      /**
60       * The attribute append.
61       */
62      public static final String CHILDREN_COMBINATION_APPEND = "append";
63  
64      @SuppressWarnings("UnusedDeclaration")
65      private static final String DEFAULT_CHILDREN_COMBINATION_MODE = CHILDREN_COMBINATION_MERGE; // plexus: public
66  
67      /**
68       * The name of the attribute.
69       */
70      public static final String SELF_COMBINATION_MODE_ATTRIBUTE = "combine.self";
71  
72      /**
73       * The attributes which identifies <code>override</code>.
74       */
75      public static final String SELF_COMBINATION_OVERRIDE = "override"; // plexus: public
76  
77      /**
78       * The attribute which identifies <code>merge</code>
79       */
80      public static final String SELF_COMBINATION_MERGE = "merge";
81  
82      @SuppressWarnings("UnusedDeclaration")
83      private static final String DEFAULT_SELF_COMBINATION_MODE = SELF_COMBINATION_MERGE; // plexus: public
84  
85      private static final String[] EMPTY_STRING_ARRAY = new String[0];
86      private static final Xpp3Dom[] EMPTY_DOM_ARRAY = new Xpp3Dom[0];
87  
88      /**
89       * @param name The name of the instance.
90       */
91      public Xpp3Dom(String name) {
92          this.name = name;
93          childList = new ArrayList<Xpp3Dom>();
94          childMap = new HashMap<String, Xpp3Dom>();
95      }
96  
97      /**
98       * Create instance.
99       * @param source The source.
100      */
101     public Xpp3Dom(Xpp3Dom source) {
102         this(source, source.getName());
103     }
104 
105     /**
106      * Create instance.
107      * @param src The source Dom.
108      * @param name The name of the Dom.
109      */
110     public Xpp3Dom(@Nonnull Xpp3Dom src, String name) {
111         this.name = name;
112 
113         int size = src.getChildCount();
114         childList = new ArrayList<Xpp3Dom>(size);
115         childMap = new HashMap<String, Xpp3Dom>();
116 
117         setValue(src.getValue());
118 
119         for (String attributeName : src.getAttributeNames()) {
120             setAttribute(attributeName, src.getAttribute(attributeName));
121         }
122 
123         for (Xpp3Dom xpp3Dom : src.getChildren()) {
124             addChild(new Xpp3Dom(xpp3Dom));
125         }
126     }
127 
128     /**
129      * @return The current name.
130      */
131     public String getName() {
132         return name;
133     }
134 
135     /**
136      * @return The current value.
137      */
138     @Nonnull
139     public String getValue() {
140         return value;
141     }
142 
143     /**
144      * @param value The value to be set.
145      */
146     public void setValue(@Nonnull String value) {
147         this.value = value;
148     }
149 
150     /**
151      * @return The array of attribute names.
152      */
153     public String[] getAttributeNames() {
154         boolean isNothing = attributes == null || attributes.isEmpty();
155         return isNothing ? EMPTY_STRING_ARRAY : attributes.keySet().toArray(new String[attributes.size()]);
156     }
157 
158     /**
159      * @param nameParameter The name of the attribute.
160      * @return The attribute value.
161      */
162     public String getAttribute(String nameParameter) {
163         return this.attributes != null ? this.attributes.get(nameParameter) : null;
164     }
165 
166     /**
167      * @param nameParameter The name of the attribute.
168      * @param valueParameter The value of the attribute.
169      */
170     public void setAttribute(@Nonnull String nameParameter, @Nonnull String valueParameter) {
171         if (valueParameter == null) {
172             throw new NullPointerException("value can not be null");
173         }
174         if (nameParameter == null) {
175             throw new NullPointerException("name can not be null");
176         }
177         if (attributes == null) {
178             attributes = new HashMap<String, String>();
179         }
180 
181         attributes.put(nameParameter, valueParameter);
182     }
183 
184     /**
185      * @param i The index to be selected.
186      * @return The child selected by index.
187      */
188     public Xpp3Dom getChild(int i) {
189         return childList.get(i);
190     }
191 
192     /**
193      * @param nameParameter The name of the child.
194      * @return The child selected by name.
195      */
196     public Xpp3Dom getChild(String nameParameter) {
197         return childMap.get(nameParameter);
198     }
199 
200     /**
201      * @param child The child to be added.
202      */
203     public void addChild(Xpp3Dom child) {
204         child.setParent(this);
205         childList.add(child);
206         childMap.put(child.getName(), child);
207     }
208 
209     /**
210      * @return The array of childs.
211      */
212     public Xpp3Dom[] getChildren() {
213         boolean isNothing = childList == null || childList.isEmpty();
214         return isNothing ? EMPTY_DOM_ARRAY : childList.toArray(new Xpp3Dom[childList.size()]);
215     }
216 
217     private List<Xpp3Dom> getChildrenList() {
218         boolean isNothing = childList == null || childList.isEmpty();
219         return isNothing ? Collections.<Xpp3Dom>emptyList() : childList;
220     }
221 
222     /**
223      * @param nameParameter The name of the child.
224      * @return The array of the Dom.
225      */
226     public Xpp3Dom[] getChildren(String nameParameter) {
227         List<Xpp3Dom> children = getChildrenList(nameParameter);
228         return children.toArray(new Xpp3Dom[children.size()]);
229     }
230 
231     List<Xpp3Dom> getChildrenList(String nameParameter) {
232         if (childList == null) {
233             return Collections.emptyList();
234         } else {
235             ArrayList<Xpp3Dom> children = new ArrayList<Xpp3Dom>();
236             for (Xpp3Dom aChildList : childList) {
237                 if (nameParameter.equals(aChildList.getName())) {
238                     children.add(aChildList);
239                 }
240             }
241             return children;
242         }
243     }
244 
245     /**
246      * @return The number of childs.
247      */
248     public int getChildCount() {
249         if (childList == null) {
250             return 0;
251         }
252 
253         return childList.size();
254     }
255 
256     /**
257      * @param i The child to be removed.
258      */
259     public void removeChild(int i) {
260         Xpp3Dom child = childList.remove(i);
261         childMap.values().remove(child);
262         child.setParent(null);
263     }
264 
265     /**
266      * @return The current parent.
267      */
268     public Xpp3Dom getParent() {
269         return parent;
270     }
271 
272     /**
273      * @param parent Set the parent.
274      */
275     public void setParent(Xpp3Dom parent) {
276         this.parent = parent;
277     }
278 
279     /**
280      * @param dominant The dominant part.
281      * @param recessive The recessive part.
282      * @param childMergeOverride true if child merge will take precedence false otherwise.
283      * @return The merged Xpp3Dom.
284      */
285     public static Xpp3Dom mergeXpp3Dom(Xpp3Dom dominant, Xpp3Dom recessive, Boolean childMergeOverride) {
286         return Xpp3DomUtils.mergeXpp3Dom(dominant, recessive, childMergeOverride);
287     }
288 
289     /**
290      * @param dominant The dominant part.
291      * @param recessive The recessive part.
292      * @return The merged Xpp3Dom.
293      */
294     public static Xpp3Dom mergeXpp3Dom(Xpp3Dom dominant, Xpp3Dom recessive) {
295         return Xpp3DomUtils.mergeXpp3Dom(dominant, recessive);
296     }
297 
298     /** {@inheritDoc} */
299     public boolean equals(Object obj) {
300         if (obj == this) {
301             return true;
302         }
303 
304         if (!(obj instanceof Xpp3Dom)) {
305             return false;
306         }
307 
308         Xpp3Dom dom = (Xpp3Dom) obj;
309 
310         return !(name == null ? dom.name != null : !name.equals(dom.name))
311                 && !(value == null ? dom.value != null : !value.equals(dom.value))
312                 && !(attributes == null ? dom.attributes != null : !attributes.equals(dom.attributes))
313                 && !(childList == null ? dom.childList != null : !childList.equals(dom.childList));
314     }
315 
316     /** {@inheritDoc} */
317     public int hashCode() {
318         int result = 17;
319         result = 37 * result + (name != null ? name.hashCode() : 0);
320         result = 37 * result + (value != null ? value.hashCode() : 0);
321         result = 37 * result + (attributes != null ? attributes.hashCode() : 0);
322         result = 37 * result + (childList != null ? childList.hashCode() : 0);
323         return result;
324     }
325 
326     /** {@inheritDoc} */
327     public String toString() {
328         try {
329             StringWriter writer = new StringWriter();
330             Xpp3DomWriter.write(getPrettyPrintXMLWriter(writer), this);
331             return writer.toString();
332         } catch (final IOException e) {
333             // JDK error in StringWriter.
334             throw (AssertionError) new AssertionError("Unexpected IOException from StringWriter.").initCause(e);
335         }
336     }
337 
338     /**
339      * @return Unescaped string.
340      */
341     public String toUnescapedString() {
342         try {
343             StringWriter writer = new StringWriter();
344             Xpp3DomWriter.write(getPrettyPrintXMLWriter(writer), this, false);
345             return writer.toString();
346         } catch (final IOException e) {
347             // JDK error in StringWriter.
348             throw (AssertionError) new AssertionError("Unexpected IOException from StringWriter.").initCause(e);
349         }
350     }
351 
352     private PrettyPrintXMLWriter getPrettyPrintXMLWriter(StringWriter writer) {
353         return new PrettyPrintXMLWriter(writer, "UTF-8", null);
354     }
355 
356     /**
357      * Warning: this is not the reverse of {@link #isEmpty}.
358      * Whitespace only strings are both empty and not empty.
359      *
360      * @param str the string to be checked
361      * @return true if the string is not empty (length &gt; 0) and not <code>null</code>
362      * @deprecated use <code>str != null &amp;&amp; !str.isEmpty()</code>
363      */
364     @Deprecated
365     public static boolean isNotEmpty(String str) {
366         return str != null && str.length() > 0;
367     }
368 
369     /**
370      * Warning: this is not the reverse of {@link #isNotEmpty}.
371      * Whitespace only strings are both empty and not empty.
372      *
373      * @param str the string to be checked
374      * @return true if the string only contains whitespace or is <code>null</code>
375      * @deprecated use <code>str == null || str.trim().isEmpty()</code>
376      */
377     @Deprecated
378     public static boolean isEmpty(String str) {
379         return str == null || str.trim().length() == 0;
380     }
381 
382     /** {@inheritDoc} */
383     public Iterator<Xpp3Dom> iterator() {
384         return getChildrenList().iterator();
385     }
386 }