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