Coverage Report - org.codehaus.plexus.util.xml.Xpp3DomUtils
 
Classes in this File Line Coverage Branch Coverage Complexity
Xpp3DomUtils
65%
32/49
58%
27/46
5.167
 
 1  
 package org.codehaus.plexus.util.xml;
 2  
 
 3  
 /*
 4  
  * Copyright The Codehaus Foundation.
 5  
  *
 6  
  * Licensed under the Apache License, Version 2.0 (the "License");
 7  
  * you may not use this file except in compliance with the License.
 8  
  * 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, software
 13  
  * distributed under the License is distributed on an "AS IS" BASIS,
 14  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 15  
  * See the License for the specific language governing permissions and
 16  
  * limitations under the License.
 17  
  */
 18  
 
 19  
 import org.codehaus.plexus.util.xml.pull.XmlSerializer;
 20  
 
 21  
 import java.io.IOException;
 22  
 
 23  
 /** @author Jason van Zyl */
 24  0
 public class Xpp3DomUtils
 25  
 {
 26  
     public static final String CHILDREN_COMBINATION_MODE_ATTRIBUTE = "combine.children";
 27  
 
 28  
     public static final String CHILDREN_COMBINATION_MERGE = "merge";
 29  
 
 30  
     public static final String CHILDREN_COMBINATION_APPEND = "append";
 31  
 
 32  
     /**
 33  
      * This default mode for combining children DOMs during merge means that where element names match, the process will
 34  
      * try to merge the element data, rather than putting the dominant and recessive elements (which share the same
 35  
      * element name) as siblings in the resulting DOM.
 36  
      */
 37  
     public static final String DEFAULT_CHILDREN_COMBINATION_MODE = CHILDREN_COMBINATION_MERGE;
 38  
 
 39  
     public static final String SELF_COMBINATION_MODE_ATTRIBUTE = "combine.self";
 40  
 
 41  
     public static final String SELF_COMBINATION_OVERRIDE = "override";
 42  
 
 43  
     public static final String SELF_COMBINATION_MERGE = "merge";
 44  
 
 45  
     /**
 46  
      * In case of complex XML structures, combining can be done based on id.
 47  
      * 
 48  
      * @since 3.0.22
 49  
      */
 50  
     public static final String ID_COMBINATION_MODE_ATTRIBUTE = "combine.id";
 51  
 
 52  
     /**
 53  
      * This default mode for combining a DOM node during merge means that where element names match, the process will
 54  
      * try to merge the element attributes and values, rather than overriding the recessive element completely with the
 55  
      * dominant one. This means that wherever the dominant element doesn't provide the value or a particular attribute,
 56  
      * that value or attribute will be set from the recessive DOM node.
 57  
      */
 58  
     public static final String DEFAULT_SELF_COMBINATION_MODE = SELF_COMBINATION_MERGE;
 59  
 
 60  
     public void writeToSerializer( String namespace, XmlSerializer serializer, Xpp3Dom dom )
 61  
         throws IOException
 62  
     {
 63  
         // TODO: WARNING! Later versions of plexus-utils psit out an <?xml ?> header due to thinking this is a new
 64  
         // document - not the desired behaviour!
 65  0
         SerializerXMLWriter xmlWriter = new SerializerXMLWriter( namespace, serializer );
 66  0
         Xpp3DomWriter.write( xmlWriter, dom );
 67  0
         if ( xmlWriter.getExceptions().size() > 0 )
 68  
         {
 69  0
             throw (IOException) xmlWriter.getExceptions().get( 0 );
 70  
         }
 71  0
     }
 72  
 
 73  
     /**
 74  
      * Merges one DOM into another, given a specific algorithm and possible override points for that algorithm. The
 75  
      * algorithm is as follows: 1. if the recessive DOM is null, there is nothing to do...return. 2. Determine whether
 76  
      * the dominant node will suppress the recessive one (flag=mergeSelf). A. retrieve the 'combine.self' attribute on
 77  
      * the dominant node, and try to match against 'override'... if it matches 'override', then set mergeSelf ==
 78  
      * false...the dominant node suppresses the recessive one completely. B. otherwise, use the default value for
 79  
      * mergeSelf, which is true...this is the same as specifying 'combine.self' == 'merge' as an attribute of the
 80  
      * dominant root node. 3. If mergeSelf == true A. if the dominant root node's value is empty, set it to the
 81  
      * recessive root node's value B. For each attribute in the recessive root node which is not set in the dominant
 82  
      * root node, set it. C. Determine whether children from the recessive DOM will be merged or appended to the
 83  
      * dominant DOM as siblings (flag=mergeChildren). i. if childMergeOverride is set (non-null), use that value
 84  
      * (true/false) ii. retrieve the 'combine.children' attribute on the dominant node, and try to match against
 85  
      * 'append'...if it matches 'append', then set mergeChildren == false...the recessive children will be appended as
 86  
      * siblings of the dominant children. iii. otherwise, use the default value for mergeChildren, which is true...this
 87  
      * is the same as specifying 'combine.children' == 'merge' as an attribute on the dominant root node. D. Iterate
 88  
      * through the recessive children, and: i. if 'combine.id' is set and there is a corresponding dominant child
 89  
      * (matched by value of 'combine.id'), merge the two. ii. if mergeChildren == true and there is a corresponding
 90  
      * dominant child (matched by element name), merge the two. iii. otherwise, add the recessive child as a new child
 91  
      * on the dominant root node.
 92  
      */
 93  
     private static void mergeIntoXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive, Boolean childMergeOverride )
 94  
     {
 95  
         // TODO: share this as some sort of assembler, implement a walk interface?
 96  4
         if ( recessive == null )
 97  
         {
 98  0
             return;
 99  
         }
 100  
 
 101  4
         boolean mergeSelf = true;
 102  
 
 103  4
         String selfMergeMode = dominant.getAttribute( SELF_COMBINATION_MODE_ATTRIBUTE );
 104  
 
 105  4
         if ( isNotEmpty( selfMergeMode ) && SELF_COMBINATION_OVERRIDE.equals( selfMergeMode ) )
 106  
         {
 107  0
             mergeSelf = false;
 108  
         }
 109  
 
 110  4
         if ( mergeSelf )
 111  
         {
 112  4
             if ( isEmpty( dominant.getValue() ) )
 113  
             {
 114  2
                 dominant.setValue( recessive.getValue() );
 115  
             }
 116  
 
 117  4
             String[] recessiveAttrs = recessive.getAttributeNames();
 118  5
             for ( String attr : recessiveAttrs )
 119  
             {
 120  1
                 if ( isEmpty( dominant.getAttribute( attr ) ) )
 121  
                 {
 122  0
                     dominant.setAttribute( attr, recessive.getAttribute( attr ) );
 123  
                 }
 124  
             }
 125  
 
 126  4
             boolean mergeChildren = true;
 127  
 
 128  4
             if ( childMergeOverride != null )
 129  
             {
 130  4
                 mergeChildren = childMergeOverride;
 131  
             }
 132  
             else
 133  
             {
 134  0
                 String childMergeMode = dominant.getAttribute( CHILDREN_COMBINATION_MODE_ATTRIBUTE );
 135  
 
 136  0
                 if ( isNotEmpty( childMergeMode ) && CHILDREN_COMBINATION_APPEND.equals( childMergeMode ) )
 137  
                 {
 138  0
                     mergeChildren = false;
 139  
                 }
 140  
             }
 141  
 
 142  4
             Xpp3Dom[] children = recessive.getChildren();
 143  8
             for ( Xpp3Dom recessiveChild : children )
 144  
             {
 145  4
                 String idValue = recessiveChild.getAttribute( ID_COMBINATION_MODE_ATTRIBUTE );
 146  
 
 147  4
                 Xpp3Dom childDom = null;
 148  4
                 if ( isNotEmpty( idValue ) )
 149  
                 {
 150  7
                     for ( Xpp3Dom dominantChild : dominant.getChildren() )
 151  
                     {
 152  5
                         if ( idValue.equals( dominantChild.getAttribute( ID_COMBINATION_MODE_ATTRIBUTE ) ) )
 153  
                         {
 154  1
                             childDom = dominantChild;
 155  
                             // we have a match, so don't append but merge
 156  1
                             mergeChildren = true;
 157  
                         }
 158  
                     }
 159  
                 }
 160  
                 else
 161  
                 {
 162  2
                     childDom = dominant.getChild( recessiveChild.getName() );
 163  
                 }
 164  
 
 165  4
                 if ( mergeChildren && childDom != null )
 166  
                 {
 167  3
                     mergeIntoXpp3Dom( childDom, recessiveChild, childMergeOverride );
 168  
                 }
 169  
                 else
 170  
                 {
 171  1
                     dominant.addChild( new Xpp3Dom( recessiveChild ) );
 172  
                 }
 173  
             }
 174  
         }
 175  4
     }
 176  
 
 177  
     /**
 178  
      * Merge two DOMs, with one having dominance in the case of collision.
 179  
      *
 180  
      * @see #CHILDREN_COMBINATION_MODE_ATTRIBUTE
 181  
      * @see #SELF_COMBINATION_MODE_ATTRIBUTE
 182  
      * @param dominant The dominant DOM into which the recessive value/attributes/children will be merged
 183  
      * @param recessive The recessive DOM, which will be merged into the dominant DOM
 184  
      * @param childMergeOverride Overrides attribute flags to force merging or appending of child elements into the
 185  
      *            dominant DOM
 186  
      */
 187  
     public static Xpp3Dom mergeXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive, Boolean childMergeOverride )
 188  
     {
 189  1
         if ( dominant != null )
 190  
         {
 191  1
             mergeIntoXpp3Dom( dominant, recessive, childMergeOverride );
 192  1
             return dominant;
 193  
         }
 194  0
         return recessive;
 195  
     }
 196  
 
 197  
     /**
 198  
      * Merge two DOMs, with one having dominance in the case of collision. Merge mechanisms (vs. override for nodes, or
 199  
      * vs. append for children) is determined by attributes of the dominant root node.
 200  
      *
 201  
      * @see #CHILDREN_COMBINATION_MODE_ATTRIBUTE
 202  
      * @see #SELF_COMBINATION_MODE_ATTRIBUTE
 203  
      * @param dominant The dominant DOM into which the recessive value/attributes/children will be merged
 204  
      * @param recessive The recessive DOM, which will be merged into the dominant DOM
 205  
      */
 206  
     public static Xpp3Dom mergeXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive )
 207  
     {
 208  0
         if ( dominant != null )
 209  
         {
 210  0
             mergeIntoXpp3Dom( dominant, recessive, null );
 211  0
             return dominant;
 212  
         }
 213  0
         return recessive;
 214  
     }
 215  
 
 216  
     public static boolean isNotEmpty( String str )
 217  
     {
 218  8
         return ( str != null && str.length() > 0 );
 219  
     }
 220  
 
 221  
     public static boolean isEmpty( String str )
 222  
     {
 223  5
         return ( str == null || str.trim().length() == 0 );
 224  
     }
 225  
 }