Skip to content

Commit dd8d35a

Browse files
committed
#61 keep input location while merging
1 parent 26cc2bf commit dd8d35a

File tree

4 files changed

+175
-45
lines changed

4 files changed

+175
-45
lines changed

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

+47-18
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,15 @@ public Xpp3Dom( String name )
9191
childMap = new HashMap<String, Xpp3Dom>();
9292
}
9393

94+
/**
95+
* @since 3.2.0
96+
*/
97+
public Xpp3Dom( String name, Object inputLocation )
98+
{
99+
this( name );
100+
this.inputLocation = inputLocation;
101+
}
102+
94103
/**
95104
* Copy constructor.
96105
*/
@@ -105,6 +114,7 @@ public Xpp3Dom( Xpp3Dom src )
105114
public Xpp3Dom( Xpp3Dom src, String name )
106115
{
107116
this.name = name;
117+
this.inputLocation = src.inputLocation;
108118

109119
int childCount = src.getChildCount();
110120

@@ -321,23 +331,41 @@ public void writeToSerializer( String namespace, XmlSerializer serializer )
321331
}
322332

323333
/**
324-
* Merges one DOM into another, given a specific algorithm and possible override points for that algorithm. The
325-
* algorithm is as follows: 1. if the recessive DOM is null, there is nothing to do...return. 2. Determine whether
326-
* the dominant node will suppress the recessive one (flag=mergeSelf). A. retrieve the 'combine.self' attribute on
327-
* the dominant node, and try to match against 'override'... if it matches 'override', then set mergeSelf ==
328-
* false...the dominant node suppresses the recessive one completely. B. otherwise, use the default value for
329-
* mergeSelf, which is true...this is the same as specifying 'combine.self' == 'merge' as an attribute of the
330-
* dominant root node. 3. If mergeSelf == true A. if the dominant root node's value is empty, set it to the
331-
* recessive root node's value B. For each attribute in the recessive root node which is not set in the dominant
332-
* root node, set it. C. Determine whether children from the recessive DOM will be merged or appended to the
333-
* dominant DOM as siblings (flag=mergeChildren). i. if childMergeOverride is set (non-null), use that value
334-
* (true/false) ii. retrieve the 'combine.children' attribute on the dominant node, and try to match against
335-
* 'append'...if it matches 'append', then set mergeChildren == false...the recessive children will be appended as
336-
* siblings of the dominant children. iii. otherwise, use the default value for mergeChildren, which is true...this
337-
* is the same as specifying 'combine.children' == 'merge' as an attribute on the dominant root node. D. Iterate
338-
* through the recessive children, and: i. if mergeChildren == true and there is a corresponding dominant child
339-
* (matched by element name), merge the two. ii. otherwise, add the recessive child as a new child on the dominant
340-
* root node.
334+
* Merges one DOM into another, given a specific algorithm and possible override points for that algorithm.<p>
335+
* The algorithm is as follows:
336+
* <ol>
337+
* <li> if the recessive DOM is null, there is nothing to do... return.</li>
338+
* <li> Determine whether the dominant node will suppress the recessive one (flag=mergeSelf).
339+
* <ol type="A">
340+
* <li> retrieve the 'combine.self' attribute on the dominant node, and try to match against 'override'...
341+
* if it matches 'override', then set mergeSelf == false...the dominant node suppresses the recessive one
342+
* completely.</li>
343+
* <li> otherwise, use the default value for mergeSelf, which is true...this is the same as specifying
344+
* 'combine.self' == 'merge' as an attribute of the dominant root node.</li>
345+
* </ol></li>
346+
* <li> If mergeSelf == true
347+
* <ol type="A">
348+
* <li> if the dominant root node's value is empty, set it to the recessive root node's value</li>
349+
* <li> For each attribute in the recessive root node which is not set in the dominant root node, set it.</li>
350+
* <li> Determine whether children from the recessive DOM will be merged or appended to the dominant DOM as
351+
* siblings (flag=mergeChildren).
352+
* <ol type="i">
353+
* <li> if childMergeOverride is set (non-null), use that value (true/false)</li>
354+
* <li> retrieve the 'combine.children' attribute on the dominant node, and try to match against
355+
* 'append'...</li>
356+
* <li> if it matches 'append', then set mergeChildren == false...the recessive children will be appended as
357+
* siblings of the dominant children.</li>
358+
* <li> otherwise, use the default value for mergeChildren, which is true...this is the same as specifying
359+
* 'combine.children' == 'merge' as an attribute on the dominant root node.</li>
360+
* </ol></li>
361+
* <li> Iterate through the recessive children, and:
362+
* <ol type="i">
363+
* <li> if mergeChildren == true and there is a corresponding dominant child (matched by element name),
364+
* merge the two.</li>
365+
* <li> otherwise, add the recessive child as a new child on the dominant root node.</li>
366+
* </ol></li>
367+
* </ol></li>
368+
* </ol>
341369
*/
342370
private static void mergeIntoXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive, Boolean childMergeOverride )
343371
{
@@ -358,9 +386,10 @@ private static void mergeIntoXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive, Boole
358386

359387
if ( mergeSelf )
360388
{
361-
if ( isEmpty( dominant.getValue() ) )
389+
if ( isEmpty( dominant.getValue() ) && !isEmpty( recessive.getValue() ) )
362390
{
363391
dominant.setValue( recessive.getValue() );
392+
dominant.setInputLocation( recessive.getInputLocation() );
364393
}
365394

366395
String[] recessiveAttrs = recessive.getAttributeNames();

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

+43-19
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@
1919
import org.codehaus.plexus.util.xml.pull.XmlSerializer;
2020

2121
import java.io.IOException;
22+
import java.util.Arrays;
23+
import java.util.HashMap;
24+
import java.util.Iterator;
25+
import java.util.Map;
2226

2327
/** @author Jason van Zyl */
2428
public class Xpp3DomUtils
@@ -71,24 +75,43 @@ public void writeToSerializer( String namespace, XmlSerializer serializer, Xpp3D
7175
}
7276

7377
/**
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.
78+
* Merges one DOM into another, given a specific algorithm and possible override points for that algorithm.<p>
79+
* The algorithm is as follows:
80+
* <ol>
81+
* <li> if the recessive DOM is null, there is nothing to do... return.</li>
82+
* <li> Determine whether the dominant node will suppress the recessive one (flag=mergeSelf).
83+
* <ol type="A">
84+
* <li> retrieve the 'combine.self' attribute on the dominant node, and try to match against 'override'...
85+
* if it matches 'override', then set mergeSelf == false...the dominant node suppresses the recessive one
86+
* completely.</li>
87+
* <li> otherwise, use the default value for mergeSelf, which is true...this is the same as specifying
88+
* 'combine.self' == 'merge' as an attribute of the dominant root node.</li>
89+
* </ol></li>
90+
* <li> If mergeSelf == true
91+
* <ol type="A">
92+
* <li> if the dominant root node's value is empty, set it to the recessive root node's value</li>
93+
* <li> For each attribute in the recessive root node which is not set in the dominant root node, set it.</li>
94+
* <li> Determine whether children from the recessive DOM will be merged or appended to the dominant DOM as
95+
* siblings (flag=mergeChildren).
96+
* <ol type="i">
97+
* <li> if childMergeOverride is set (non-null), use that value (true/false)</li>
98+
* <li> retrieve the 'combine.children' attribute on the dominant node, and try to match against
99+
* 'append'...</li>
100+
* <li> if it matches 'append', then set mergeChildren == false...the recessive children will be appended as
101+
* siblings of the dominant children.</li>
102+
* <li> otherwise, use the default value for mergeChildren, which is true...this is the same as specifying
103+
* 'combine.children' == 'merge' as an attribute on the dominant root node.</li>
104+
* </ol></li>
105+
* <li> Iterate through the recessive children, and:
106+
* <ol type="i">
107+
* <li> if 'combine.id' is set and there is a corresponding dominant child (matched by value of 'combine.id'),
108+
* merge the two.</li>
109+
* <li> if mergeChildren == true and there is a corresponding dominant child (matched by element name),
110+
* merge the two.</li>
111+
* <li> otherwise, add the recessive child as a new child on the dominant root node.</li>
112+
* </ol></li>
113+
* </ol></li>
114+
* </ol>
92115
*/
93116
private static void mergeIntoXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive, Boolean childMergeOverride )
94117
{
@@ -109,9 +132,10 @@ private static void mergeIntoXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive, Boole
109132

110133
if ( mergeSelf )
111134
{
112-
if ( isEmpty( dominant.getValue() ) )
135+
if ( isEmpty( dominant.getValue() ) && !isEmpty( recessive.getValue() ) )
113136
{
114137
dominant.setValue( recessive.getValue() );
138+
dominant.setInputLocation( recessive.getInputLocation() );
115139
}
116140

117141
String[] recessiveAttrs = recessive.getAttributeNames();

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

+53-4
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import java.io.StringReader;
2828
import java.util.HashMap;
2929

30+
import org.codehaus.plexus.util.xml.pull.XmlPullParser;
3031
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
3132
import org.junit.Test;
3233

@@ -38,24 +39,34 @@ public void testShouldPerformAppendAtFirstSubElementLevel()
3839
// create the dominant DOM
3940
Xpp3Dom t1 = new Xpp3Dom( "top" );
4041
t1.setAttribute( Xpp3Dom.CHILDREN_COMBINATION_MODE_ATTRIBUTE, Xpp3Dom.CHILDREN_COMBINATION_APPEND );
42+
t1.setInputLocation( "t1top" );
4143

4244
Xpp3Dom t1s1 = new Xpp3Dom( "topsub1" );
4345
t1s1.setValue( "t1s1Value" );
46+
t1s1.setInputLocation( "t1s1" );
4447

4548
t1.addChild( t1s1 );
4649

4750
// create the recessive DOM
4851
Xpp3Dom t2 = new Xpp3Dom( "top" );
52+
t2.setInputLocation( "t2top" );
4953

5054
Xpp3Dom t2s1 = new Xpp3Dom( "topsub1" );
5155
t2s1.setValue( "t2s1Value" );
56+
t2s1.setInputLocation( "t2s1" );
5257

5358
t2.addChild( t2s1 );
5459

5560
// merge and check results.
5661
Xpp3Dom result = Xpp3Dom.mergeXpp3Dom( t1, t2 );
5762

5863
assertEquals( 2, result.getChildren( "topsub1" ).length );
64+
assertEquals( "t2s1Value", result.getChildren( "topsub1" )[0].getValue() );
65+
assertEquals( "t1s1Value", result.getChildren( "topsub1" )[1].getValue() );
66+
67+
assertEquals( "t1top", result.getInputLocation() );
68+
assertEquals( "t2s1", result.getChildren( "topsub1" )[0].getInputLocation() );
69+
assertEquals( "t1s1", result.getChildren( "topsub1" )[1].getInputLocation() );
5970
}
6071

6172
@Test
@@ -64,24 +75,32 @@ public void testShouldOverrideAppendAndDeepMerge()
6475
// create the dominant DOM
6576
Xpp3Dom t1 = new Xpp3Dom( "top" );
6677
t1.setAttribute( Xpp3Dom.CHILDREN_COMBINATION_MODE_ATTRIBUTE, Xpp3Dom.CHILDREN_COMBINATION_APPEND );
78+
t1.setInputLocation( "t1top" );
6779

6880
Xpp3Dom t1s1 = new Xpp3Dom( "topsub1" );
6981
t1s1.setValue( "t1s1Value" );
82+
t1s1.setInputLocation( "t1s1" );
7083

7184
t1.addChild( t1s1 );
7285

7386
// create the recessive DOM
7487
Xpp3Dom t2 = new Xpp3Dom( "top" );
88+
t2.setInputLocation( "t2top" );
7589

7690
Xpp3Dom t2s1 = new Xpp3Dom( "topsub1" );
7791
t2s1.setValue( "t2s1Value" );
92+
t2s1.setInputLocation( "t2s1" );
7893

7994
t2.addChild( t2s1 );
8095

8196
// merge and check results.
8297
Xpp3Dom result = Xpp3Dom.mergeXpp3Dom( t1, t2, Boolean.TRUE );
8398

8499
assertEquals( 1, result.getChildren( "topsub1" ).length );
100+
assertEquals( "t1s1Value", result.getChildren( "topsub1" )[0].getValue() );
101+
102+
assertEquals( "t1top", result.getInputLocation() );
103+
assertEquals( "t1s1", result.getChildren( "topsub1" )[0].getInputLocation() );
85104
}
86105

87106
@Test
@@ -90,19 +109,22 @@ public void testShouldPerformSelfOverrideAtTopLevel()
90109
// create the dominant DOM
91110
Xpp3Dom t1 = new Xpp3Dom( "top" );
92111
t1.setAttribute( "attr", "value" );
112+
t1.setInputLocation( "t1top" );
93113

94114
t1.setAttribute( Xpp3Dom.SELF_COMBINATION_MODE_ATTRIBUTE, Xpp3Dom.SELF_COMBINATION_OVERRIDE );
95115

96116
// create the recessive DOM
97117
Xpp3Dom t2 = new Xpp3Dom( "top" );
98118
t2.setAttribute( "attr2", "value2" );
99119
t2.setValue( "t2Value" );
120+
t2.setInputLocation( "t2top" );
100121

101122
// merge and check results.
102123
Xpp3Dom result = Xpp3Dom.mergeXpp3Dom( t1, t2 );
103124

104125
assertEquals( 2, result.getAttributeNames().length );
105126
assertNull( result.getValue() );
127+
assertEquals( "t1top", result.getInputLocation() );
106128
}
107129

108130
@Test
@@ -111,11 +133,13 @@ public void testShouldMergeValuesAtTopLevelByDefault()
111133
// create the dominant DOM
112134
Xpp3Dom t1 = new Xpp3Dom( "top" );
113135
t1.setAttribute( "attr", "value" );
136+
t1.setInputLocation( "t1top" );
114137

115138
// create the recessive DOM
116139
Xpp3Dom t2 = new Xpp3Dom( "top" );
117140
t2.setAttribute( "attr2", "value2" );
118141
t2.setValue( "t2Value" );
142+
t2.setInputLocation( "t2top" );
119143

120144
// merge and check results.
121145
Xpp3Dom result = Xpp3Dom.mergeXpp3Dom( t1, t2 );
@@ -124,6 +148,7 @@ public void testShouldMergeValuesAtTopLevelByDefault()
124148
assertEquals( 2, result.getAttributeNames().length );
125149

126150
assertEquals( result.getValue(), t2.getValue() );
151+
assertEquals( "t2top", result.getInputLocation() );
127152
}
128153

129154
@Test
@@ -213,10 +238,12 @@ public void testShouldOverwritePluginConfigurationSubItemsByDefault()
213238
throws XmlPullParserException, IOException
214239
{
215240
String parentConfigStr = "<configuration><items><item>one</item><item>two</item></items></configuration>";
216-
Xpp3Dom parentConfig = Xpp3DomBuilder.build( new StringReader( parentConfigStr ) );
241+
Xpp3Dom parentConfig =
242+
Xpp3DomBuilder.build( new StringReader( parentConfigStr ), new FixedInputLocationBuilder( "parent" ) );
217243

218244
String childConfigStr = "<configuration><items><item>three</item></items></configuration>";
219-
Xpp3Dom childConfig = Xpp3DomBuilder.build( new StringReader( childConfigStr ) );
245+
Xpp3Dom childConfig =
246+
Xpp3DomBuilder.build( new StringReader( childConfigStr ), new FixedInputLocationBuilder( "child" ) );
220247

221248
Xpp3Dom result = Xpp3Dom.mergeXpp3Dom( childConfig, parentConfig );
222249
Xpp3Dom items = result.getChild( "items" );
@@ -225,18 +252,21 @@ public void testShouldOverwritePluginConfigurationSubItemsByDefault()
225252

226253
Xpp3Dom item = items.getChild( 0 );
227254
assertEquals( "three", item.getValue() );
255+
assertEquals( "child", item.getInputLocation() );
228256
}
229257

230258
@Test
231259
public void testShouldMergePluginConfigurationSubItemsWithMergeAttributeSet()
232260
throws XmlPullParserException, IOException
233261
{
234262
String parentConfigStr = "<configuration><items><item>one</item><item>two</item></items></configuration>";
235-
Xpp3Dom parentConfig = Xpp3DomBuilder.build( new StringReader( parentConfigStr ) );
263+
Xpp3Dom parentConfig =
264+
Xpp3DomBuilder.build( new StringReader( parentConfigStr ), new FixedInputLocationBuilder( "parent" ) );
236265

237266
String childConfigStr =
238267
"<configuration><items combine.children=\"append\"><item>three</item></items></configuration>";
239-
Xpp3Dom childConfig = Xpp3DomBuilder.build( new StringReader( childConfigStr ) );
268+
Xpp3Dom childConfig =
269+
Xpp3DomBuilder.build( new StringReader( childConfigStr ), new FixedInputLocationBuilder( "child" ) );
240270

241271
Xpp3Dom result = Xpp3Dom.mergeXpp3Dom( childConfig, parentConfig );
242272
Xpp3Dom items = result.getChild( "items" );
@@ -246,8 +276,11 @@ public void testShouldMergePluginConfigurationSubItemsWithMergeAttributeSet()
246276
Xpp3Dom[] item = items.getChildren();
247277

248278
assertEquals( "one", item[0].getValue() );
279+
assertEquals( "parent", item[0].getInputLocation() );
249280
assertEquals( "two", item[1].getValue() );
281+
assertEquals( "parent", item[1].getInputLocation() );
250282
assertEquals( "three", item[2].getValue() );
283+
assertEquals( "child", item[2].getInputLocation() );
251284
}
252285

253286
@Test
@@ -295,4 +328,20 @@ public void testDupeChildren()
295328
assertNotNull( dom );
296329
assertEquals( "y", dom.getChild( "foo" ).getValue() );
297330
}
331+
332+
private static class FixedInputLocationBuilder
333+
implements Xpp3DomBuilder.InputLocationBuilder
334+
{
335+
private final Object location;
336+
337+
public FixedInputLocationBuilder( Object location )
338+
{
339+
this.location = location;
340+
}
341+
342+
public Object toInputLocation( XmlPullParser parser )
343+
{
344+
return location;
345+
}
346+
}
298347
}

0 commit comments

Comments
 (0)