Skip to content

Commit 844da90

Browse files
authored
Support combine.keys
2 parents 89f74c5 + 177a0cd commit 844da90

File tree

3 files changed

+73
-3
lines changed

3 files changed

+73
-3
lines changed

pom.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ limitations under the License.
2626
</parent>
2727

2828
<artifactId>plexus-utils</artifactId>
29-
<version>3.3.1-SNAPSHOT</version>
29+
<version>3.4.0-SNAPSHOT</version>
3030

3131
<name>Plexus Common Utilities</name>
3232
<description>A collection of various utility classes to ease working with strings, files, command lines, XML and

src/main/java/org/codehaus/plexus/util/xml/Xpp3DomUtils.java

+37-2
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,7 @@
1919
import org.codehaus.plexus.util.xml.pull.XmlSerializer;
2020

2121
import java.io.IOException;
22-
import java.util.Arrays;
2322
import java.util.HashMap;
24-
import java.util.Iterator;
2523
import java.util.Map;
2624

2725
/** @author Jason van Zyl */
@@ -52,6 +50,14 @@ public class Xpp3DomUtils
5250
* @since 3.0.22
5351
*/
5452
public static final String ID_COMBINATION_MODE_ATTRIBUTE = "combine.id";
53+
54+
/**
55+
* In case of complex XML structures, combining can be done based on keys.
56+
* This is a comma separated list of attribute names.
57+
*
58+
* @Since 3.4.0
59+
*/
60+
public static final String KEYS_COMBINATION_MODE_ATTRIBUTE = "combine.keys";
5561

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

171180
Xpp3Dom childDom = null;
172181
if ( isNotEmpty( idValue ) )
@@ -181,6 +190,32 @@ private static void mergeIntoXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive, Boole
181190
}
182191
}
183192
}
193+
else if ( isNotEmpty( keysValue ) )
194+
{
195+
String[] keys = keysValue.split( "," );
196+
Map<String, String> recessiveKeyValues = new HashMap<String, String>( keys.length );
197+
for ( String key : keys )
198+
{
199+
recessiveKeyValues.put( key, recessiveChild.getChild( key ).getValue() );
200+
}
201+
202+
for ( Xpp3Dom dominantChild : dominant.getChildren() )
203+
{
204+
Map<String, String> dominantKeyValues = new HashMap<String, String>( keys.length );
205+
for ( String key : keys )
206+
{
207+
dominantKeyValues.put( key, dominantChild.getChild( key ).getValue() );
208+
}
209+
210+
if ( recessiveKeyValues.equals( dominantKeyValues ) )
211+
{
212+
childDom = dominantChild;
213+
// we have a match, so don't append but merge
214+
mergeChildren = true;
215+
}
216+
}
217+
218+
}
184219
else
185220
{
186221
childDom = dominant.getChild( recessiveChild.getName() );

src/test/java/org/codehaus/plexus/util/xml/Xpp3DomUtilsTest.java

+35
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,41 @@ public void testCombineId()
6060
assertEquals( "right", p2.getChild( "value" ).getInputLocation() );
6161
}
6262

63+
@Test
64+
public void testCombineKeys()
65+
throws Exception
66+
{
67+
String lhs = "<props>" + "<property combine.keys='name'><name>LHS-ONLY</name><value>LHS</value></property>"
68+
+ "<property combine.keys='name'><name>TOOVERWRITE</name><value>LHS</value></property>" + "</props>";
69+
70+
String rhs = "<props>" + "<property combine.keys='name'><name>RHS-ONLY</name><value>RHS</value></property>"
71+
+ "<property combine.keys='name'><name>TOOVERWRITE</name><value>RHS</value></property>" + "</props>";
72+
73+
Xpp3Dom leftDom = Xpp3DomBuilder.build( new StringReader( lhs ), new FixedInputLocationBuilder( "left" ) );
74+
Xpp3Dom rightDom = Xpp3DomBuilder.build( new StringReader( rhs ), new FixedInputLocationBuilder( "right" ) );
75+
76+
Xpp3Dom mergeResult = Xpp3DomUtils.mergeXpp3Dom( leftDom, rightDom, true );
77+
assertEquals( 3, mergeResult.getChildren( "property" ).length );
78+
79+
Xpp3Dom p0 = mergeResult.getChildren( "property" )[0];
80+
assertEquals( "LHS-ONLY", p0.getChild( "name" ).getValue() );
81+
assertEquals( "left", p0.getChild( "name" ).getInputLocation() );
82+
assertEquals( "LHS", p0.getChild( "value" ).getValue() );
83+
assertEquals( "left", p0.getChild( "value" ).getInputLocation() );
84+
85+
Xpp3Dom p1 = mergeResult.getChildren( "property" )[1];
86+
assertEquals( "TOOVERWRITE", mergeResult.getChildren( "property" )[1].getChild( "name" ).getValue() );
87+
assertEquals( "left", p1.getChild( "name" ).getInputLocation() );
88+
assertEquals( "LHS", mergeResult.getChildren( "property" )[1].getChild( "value" ).getValue() );
89+
assertEquals( "left", p1.getChild( "value" ).getInputLocation() );
90+
91+
Xpp3Dom p2 = mergeResult.getChildren( "property" )[2];
92+
assertEquals( "RHS-ONLY", mergeResult.getChildren( "property" )[2].getChild( "name" ).getValue() );
93+
assertEquals( "right", p2.getChild( "name" ).getInputLocation() );
94+
assertEquals( "RHS", mergeResult.getChildren( "property" )[2].getChild( "value" ).getValue() );
95+
assertEquals( "right", p2.getChild( "value" ).getInputLocation() );
96+
}
97+
6398
private static class FixedInputLocationBuilder
6499
implements Xpp3DomBuilder.InputLocationBuilder
65100
{

0 commit comments

Comments
 (0)