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