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