package org.ops4j.pax.construct.util;
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import org.codehaus.plexus.util.xml.Xpp3Dom;
/**
* Copied from plexus-utils 1.4.2 Xpp3Dom.java (last version which appended children correctly)
*/
public class Xpp3DomHelper
{
/**
* Merges one DOM into another, given a specific algorithm and possible override points for that algorithm. The
* algorithm is as follows:
*
* 1. if the recessive DOM is null, there is nothing to do...return.
*
* 2. Determine whether the dominant node will suppress the recessive one (flag=mergeSelf).
*
* A. retrieve the 'combine.self' attribute on the dominant node, and try to match against 'override'... if it
* matches 'override', then set mergeSelf == false...the dominant node suppresses the recessive one completely.
*
* B. otherwise, use the default value for mergeSelf, which is true...this is the same as specifying 'combine.self' ==
* 'merge' as an attribute of the dominant root node.
*
* 3. If mergeSelf == true
*
* A. if the dominant root node's value is empty, set it to the recessive root node's value
*
* B. For each attribute in the recessive root node which is not set in the dominant root node, set it.
*
* C. Determine whether children from the recessive DOM will be merged or appended to the dominant DOM as siblings
* (flag=mergeChildren).
*
* i. if childMergeOverride is set (non-null), use that value (true/false)
*
* ii. retrieve the 'combine.children' attribute on the dominant node, and try to match against 'append'...if it
* matches 'append', then set mergeChildren == false...the recessive children will be appended as siblings of the
* dominant children.
*
* iii. otherwise, use the default value for mergeChildren, which is true...this is the same as specifying
* 'combine.children' == 'merge' as an attribute on the dominant root node.
*
* D. Iterate through the recessive children, and:
*
* i. if mergeChildren == true and there is a corresponding dominant child (matched by element name), merge the two.
*
* ii. otherwise, add the recessive child as a new child on the dominant root node.
*/
private static void mergeIntoXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive, Boolean childMergeOverride )
{
// TODO: share this as some sort of assembler, implement a walk interface?
if( recessive == null )
{
return;
}
boolean mergeSelf = true;
String selfMergeMode = dominant.getAttribute( Xpp3Dom.SELF_COMBINATION_MODE_ATTRIBUTE );
if( isNotEmpty( selfMergeMode ) && Xpp3Dom.SELF_COMBINATION_OVERRIDE.equals( selfMergeMode ) )
{
mergeSelf = false;
}
if( mergeSelf )
{
if( isEmpty( dominant.getValue() ) )
{
dominant.setValue( recessive.getValue() );
}
String[] recessiveAttrs = recessive.getAttributeNames();
for( int i = 0; i < recessiveAttrs.length; i++ )
{
String attr = recessiveAttrs[i];
if( isEmpty( dominant.getAttribute( attr ) ) )
{
dominant.setAttribute( attr, recessive.getAttribute( attr ) );
}
}
boolean mergeChildren = true;
if( childMergeOverride != null )
{
mergeChildren = childMergeOverride.booleanValue();
}
else
{
String childMergeMode = dominant.getAttribute( Xpp3Dom.CHILDREN_COMBINATION_MODE_ATTRIBUTE );
if( isNotEmpty( childMergeMode ) && Xpp3Dom.CHILDREN_COMBINATION_APPEND.equals( childMergeMode ) )
{
mergeChildren = false;
}
}
Xpp3Dom[] children = recessive.getChildren();
for( int i = 0; i < children.length; i++ )
{
Xpp3Dom child = children[i];
Xpp3Dom childDom = dominant.getChild( child.getName() );
if( mergeChildren && childDom != null )
{
mergeIntoXpp3Dom( childDom, child, childMergeOverride );
}
else
{
dominant.addChild( new Xpp3Dom( child ) );
}
}
}
}
/**
* Merge two DOMs, with one having dominance in the case of collision.
*
* @see #CHILDREN_COMBINATION_MODE_ATTRIBUTE
* @see #SELF_COMBINATION_MODE_ATTRIBUTE
*
* @param dominant The dominant DOM into which the recessive value/attributes/children will be merged
* @param recessive The recessive DOM, which will be merged into the dominant DOM
* @param childMergeOverride Overrides attribute flags to force merging or appending of child elements into the
* dominant DOM
*/
public static Xpp3Dom mergeXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive, Boolean childMergeOverride )
{
if( dominant != null )
{
mergeIntoXpp3Dom( dominant, recessive, childMergeOverride );
return dominant;
}
return recessive;
}
/**
* Merge two DOMs, with one having dominance in the case of collision. Merge mechanisms (vs. override for nodes, or
* vs. append for children) is determined by attributes of the dominant root node.
*
* @see #CHILDREN_COMBINATION_MODE_ATTRIBUTE
* @see #SELF_COMBINATION_MODE_ATTRIBUTE
*
* @param dominant The dominant DOM into which the recessive value/attributes/children will be merged
* @param recessive The recessive DOM, which will be merged into the dominant DOM
*/
public static Xpp3Dom mergeXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive )
{
if( dominant != null )
{
mergeIntoXpp3Dom( dominant, recessive, null );
return dominant;
}
return recessive;
}
public static boolean isNotEmpty( String str )
{
return ( str != null && str.length() > 0 );
}
public static boolean isEmpty( String str )
{
return ( str == null || str.trim().length() == 0 );
}
}