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