Skip to content

Commit

Permalink
Support combine.keys
Browse files Browse the repository at this point in the history
  • Loading branch information
rfscholte committed Dec 7, 2019
2 parents 89f74c5 + 177a0cd commit 844da90
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 3 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Expand Up @@ -26,7 +26,7 @@ limitations under the License.
</parent>

<artifactId>plexus-utils</artifactId>
<version>3.3.1-SNAPSHOT</version>
<version>3.4.0-SNAPSHOT</version>

<name>Plexus Common Utilities</name>
<description>A collection of various utility classes to ease working with strings, files, command lines, XML and
Expand Down
39 changes: 37 additions & 2 deletions src/main/java/org/codehaus/plexus/util/xml/Xpp3DomUtils.java
Expand Up @@ -19,9 +19,7 @@
import org.codehaus.plexus.util.xml.pull.XmlSerializer;

import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/** @author Jason van Zyl */
Expand Down Expand Up @@ -52,6 +50,14 @@ public class Xpp3DomUtils
* @since 3.0.22
*/
public static final String ID_COMBINATION_MODE_ATTRIBUTE = "combine.id";

/**
* In case of complex XML structures, combining can be done based on keys.
* This is a comma separated list of attribute names.
*
* @Since 3.4.0
*/
public static final String KEYS_COMBINATION_MODE_ATTRIBUTE = "combine.keys";

/**
* This default mode for combining a DOM node during merge means that where element names match, the process will
Expand Down Expand Up @@ -106,6 +112,8 @@ public void writeToSerializer( String namespace, XmlSerializer serializer, Xpp3D
* <ol type="i">
* <li> if 'combine.id' is set and there is a corresponding dominant child (matched by value of 'combine.id'),
* merge the two.</li>
* <li> if 'combine.keys' is set and there is a corresponding dominant child (matched by value of key elements),
* merge the two.</li>
* <li> if mergeChildren == true and there is a corresponding dominant child (matched by element name),
* merge the two.</li>
* <li> otherwise, add the recessive child as a new child on the dominant root node.</li>
Expand Down Expand Up @@ -167,6 +175,7 @@ private static void mergeIntoXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive, Boole
for ( Xpp3Dom recessiveChild : children )
{
String idValue = recessiveChild.getAttribute( ID_COMBINATION_MODE_ATTRIBUTE );
String keysValue = recessiveChild.getAttribute( KEYS_COMBINATION_MODE_ATTRIBUTE );

Xpp3Dom childDom = null;
if ( isNotEmpty( idValue ) )
Expand All @@ -181,6 +190,32 @@ private static void mergeIntoXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive, Boole
}
}
}
else if ( isNotEmpty( keysValue ) )
{
String[] keys = keysValue.split( "," );
Map<String, String> recessiveKeyValues = new HashMap<String, String>( keys.length );
for ( String key : keys )
{
recessiveKeyValues.put( key, recessiveChild.getChild( key ).getValue() );
}

for ( Xpp3Dom dominantChild : dominant.getChildren() )
{
Map<String, String> dominantKeyValues = new HashMap<String, String>( keys.length );
for ( String key : keys )
{
dominantKeyValues.put( key, dominantChild.getChild( key ).getValue() );
}

if ( recessiveKeyValues.equals( dominantKeyValues ) )
{
childDom = dominantChild;
// we have a match, so don't append but merge
mergeChildren = true;
}
}

}
else
{
childDom = dominant.getChild( recessiveChild.getName() );
Expand Down
35 changes: 35 additions & 0 deletions src/test/java/org/codehaus/plexus/util/xml/Xpp3DomUtilsTest.java
Expand Up @@ -60,6 +60,41 @@ public void testCombineId()
assertEquals( "right", p2.getChild( "value" ).getInputLocation() );
}

@Test
public void testCombineKeys()
throws Exception
{
String lhs = "<props>" + "<property combine.keys='name'><name>LHS-ONLY</name><value>LHS</value></property>"
+ "<property combine.keys='name'><name>TOOVERWRITE</name><value>LHS</value></property>" + "</props>";

String rhs = "<props>" + "<property combine.keys='name'><name>RHS-ONLY</name><value>RHS</value></property>"
+ "<property combine.keys='name'><name>TOOVERWRITE</name><value>RHS</value></property>" + "</props>";

Xpp3Dom leftDom = Xpp3DomBuilder.build( new StringReader( lhs ), new FixedInputLocationBuilder( "left" ) );
Xpp3Dom rightDom = Xpp3DomBuilder.build( new StringReader( rhs ), new FixedInputLocationBuilder( "right" ) );

Xpp3Dom mergeResult = Xpp3DomUtils.mergeXpp3Dom( leftDom, rightDom, true );
assertEquals( 3, mergeResult.getChildren( "property" ).length );

Xpp3Dom p0 = mergeResult.getChildren( "property" )[0];
assertEquals( "LHS-ONLY", p0.getChild( "name" ).getValue() );
assertEquals( "left", p0.getChild( "name" ).getInputLocation() );
assertEquals( "LHS", p0.getChild( "value" ).getValue() );
assertEquals( "left", p0.getChild( "value" ).getInputLocation() );

Xpp3Dom p1 = mergeResult.getChildren( "property" )[1];
assertEquals( "TOOVERWRITE", mergeResult.getChildren( "property" )[1].getChild( "name" ).getValue() );
assertEquals( "left", p1.getChild( "name" ).getInputLocation() );
assertEquals( "LHS", mergeResult.getChildren( "property" )[1].getChild( "value" ).getValue() );
assertEquals( "left", p1.getChild( "value" ).getInputLocation() );

Xpp3Dom p2 = mergeResult.getChildren( "property" )[2];
assertEquals( "RHS-ONLY", mergeResult.getChildren( "property" )[2].getChild( "name" ).getValue() );
assertEquals( "right", p2.getChild( "name" ).getInputLocation() );
assertEquals( "RHS", mergeResult.getChildren( "property" )[2].getChild( "value" ).getValue() );
assertEquals( "right", p2.getChild( "value" ).getInputLocation() );
}

private static class FixedInputLocationBuilder
implements Xpp3DomBuilder.InputLocationBuilder
{
Expand Down

0 comments on commit 844da90

Please sign in to comment.