diff --git a/pom.xml b/pom.xml index 62050f6b..b4be05fb 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ limitations under the License. plexus-utils - 3.1.2-SNAPSHOT + 3.2.0-SNAPSHOT Plexus Common Utilities A collection of various utility classes to ease working with strings, files, command lines, XML and diff --git a/src/main/java/org/codehaus/plexus/util/xml/Xpp3Dom.java b/src/main/java/org/codehaus/plexus/util/xml/Xpp3Dom.java index 3467ab4c..e10498f1 100644 --- a/src/main/java/org/codehaus/plexus/util/xml/Xpp3Dom.java +++ b/src/main/java/org/codehaus/plexus/util/xml/Xpp3Dom.java @@ -48,6 +48,11 @@ public class Xpp3Dom protected Xpp3Dom parent; + /** + * @since 3.2.0 + */ + protected Object inputLocation; + private static final String[] EMPTY_STRING_ARRAY = new String[0]; private static final Xpp3Dom[] EMPTY_DOM_ARRAY = new Xpp3Dom[0]; @@ -86,6 +91,15 @@ public Xpp3Dom( String name ) childMap = new HashMap(); } + /** + * @since 3.2.0 + */ + public Xpp3Dom( String name, Object inputLocation ) + { + this( name ); + this.inputLocation = inputLocation; + } + /** * Copy constructor. */ @@ -100,6 +114,7 @@ public Xpp3Dom( Xpp3Dom src ) public Xpp3Dom( Xpp3Dom src, String name ) { this.name = name; + this.inputLocation = src.inputLocation; int childCount = src.getChildCount(); @@ -278,6 +293,26 @@ public void setParent( Xpp3Dom parent ) this.parent = parent; } + // ---------------------------------------------------------------------- + // Input location handling + // ---------------------------------------------------------------------- + + /** + * @since 3.2.0 + */ + public Object getInputLocation() + { + return inputLocation; + } + + /** + * @since 3.2.0 + */ + public void setInputLocation( Object inputLocation ) + { + this.inputLocation = inputLocation; + } + // ---------------------------------------------------------------------- // Helpers // ---------------------------------------------------------------------- @@ -296,23 +331,41 @@ public void writeToSerializer( String namespace, XmlSerializer serializer ) } /** - * 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. + * 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. + *
  3. Determine whether the dominant node will suppress the recessive one (flag=mergeSelf). + *
      + *
    1. 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.
    2. + *
    3. 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.
    4. + *
  4. + *
  5. If mergeSelf == true + *
      + *
    1. if the dominant root node's value is empty, set it to the recessive root node's value
    2. + *
    3. For each attribute in the recessive root node which is not set in the dominant root node, set it.
    4. + *
    5. Determine whether children from the recessive DOM will be merged or appended to the dominant DOM as + * siblings (flag=mergeChildren). + *
        + *
      1. if childMergeOverride is set (non-null), use that value (true/false)
      2. + *
      3. retrieve the 'combine.children' attribute on the dominant node, and try to match against + * 'append'...
      4. + *
      5. if it matches 'append', then set mergeChildren == false...the recessive children will be appended as + * siblings of the dominant children.
      6. + *
      7. 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.
      8. + *
    6. + *
    7. Iterate through the recessive children, and: + *
        + *
      1. if mergeChildren == true and there is a corresponding dominant child (matched by element name), + * merge the two.
      2. + *
      3. otherwise, add the recessive child as a new child on the dominant root node.
      4. + *
    8. + *
  6. + *
*/ private static void mergeIntoXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive, Boolean childMergeOverride ) { @@ -333,9 +386,10 @@ private static void mergeIntoXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive, Boole if ( mergeSelf ) { - if ( isEmpty( dominant.getValue() ) ) + if ( isEmpty( dominant.getValue() ) && !isEmpty( recessive.getValue() ) ) { dominant.setValue( recessive.getValue() ); + dominant.setInputLocation( recessive.getInputLocation() ); } String[] recessiveAttrs = recessive.getAttributeNames(); diff --git a/src/main/java/org/codehaus/plexus/util/xml/Xpp3DomBuilder.java b/src/main/java/org/codehaus/plexus/util/xml/Xpp3DomBuilder.java index 3f8ce989..ffe108d5 100644 --- a/src/main/java/org/codehaus/plexus/util/xml/Xpp3DomBuilder.java +++ b/src/main/java/org/codehaus/plexus/util/xml/Xpp3DomBuilder.java @@ -37,7 +37,16 @@ public class Xpp3DomBuilder public static Xpp3Dom build( Reader reader ) throws XmlPullParserException, IOException { - return build( reader, DEFAULT_TRIM ); + return build( reader, null ); + } + + /** + * @since 3.2.0 + */ + public static Xpp3Dom build( Reader reader, InputLocationBuilder locationBuilder ) + throws XmlPullParserException, IOException + { + return build( reader, DEFAULT_TRIM, locationBuilder ); } public static Xpp3Dom build( InputStream is, String encoding ) @@ -68,13 +77,22 @@ public static Xpp3Dom build( InputStream is, String encoding, boolean trim ) public static Xpp3Dom build( Reader reader, boolean trim ) throws XmlPullParserException, IOException + { + return build( reader, trim, null ); + } + + /** + * @since 3.2.0 + */ + public static Xpp3Dom build( Reader reader, boolean trim, InputLocationBuilder locationBuilder ) + throws XmlPullParserException, IOException { try { final XmlPullParser parser = new MXParser(); parser.setInput( reader ); - final Xpp3Dom xpp3Dom = build( parser, trim ); + final Xpp3Dom xpp3Dom = build( parser, trim, locationBuilder ); reader.close(); reader = null; @@ -94,6 +112,15 @@ public static Xpp3Dom build( XmlPullParser parser ) public static Xpp3Dom build( XmlPullParser parser, boolean trim ) throws XmlPullParserException, IOException + { + return build( parser, trim, null ); + } + + /** + * @since 3.2.0 + */ + public static Xpp3Dom build( XmlPullParser parser, boolean trim, InputLocationBuilder locationBuilder ) + throws XmlPullParserException, IOException { List elements = new ArrayList(); @@ -113,6 +140,11 @@ public static Xpp3Dom build( XmlPullParser parser, boolean trim ) Xpp3Dom childConfiguration = new Xpp3Dom( rawName ); + if ( locationBuilder != null ) + { + childConfiguration.setInputLocation( locationBuilder.toInputLocation( parser ) ); + } + int depth = elements.size(); if ( depth > 0 ) @@ -194,4 +226,14 @@ else if ( eventType == XmlPullParser.END_TAG ) throw new IllegalStateException( "End of document found before returning to 0 depth" ); } + + /** + * Input location builder interface, to be implemented to choose how to store data. + * + * @since 3.2.0 + */ + public static interface InputLocationBuilder + { + Object toInputLocation( XmlPullParser parser ); + } } diff --git a/src/main/java/org/codehaus/plexus/util/xml/Xpp3DomUtils.java b/src/main/java/org/codehaus/plexus/util/xml/Xpp3DomUtils.java index f3d5d488..70d00143 100644 --- a/src/main/java/org/codehaus/plexus/util/xml/Xpp3DomUtils.java +++ b/src/main/java/org/codehaus/plexus/util/xml/Xpp3DomUtils.java @@ -19,6 +19,10 @@ 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 */ public class Xpp3DomUtils @@ -71,24 +75,43 @@ public void writeToSerializer( String namespace, XmlSerializer serializer, Xpp3D } /** - * 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 'combine.id' is set and there is a corresponding dominant child - * (matched by value of 'combine.id'), merge the two. ii. if mergeChildren == true and there is a corresponding - * dominant child (matched by element name), merge the two. iii. otherwise, add the recessive child as a new child - * on the dominant root node. + * 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. + *
  3. Determine whether the dominant node will suppress the recessive one (flag=mergeSelf). + *
      + *
    1. 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.
    2. + *
    3. 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.
    4. + *
  4. + *
  5. If mergeSelf == true + *
      + *
    1. if the dominant root node's value is empty, set it to the recessive root node's value
    2. + *
    3. For each attribute in the recessive root node which is not set in the dominant root node, set it.
    4. + *
    5. Determine whether children from the recessive DOM will be merged or appended to the dominant DOM as + * siblings (flag=mergeChildren). + *
        + *
      1. if childMergeOverride is set (non-null), use that value (true/false)
      2. + *
      3. retrieve the 'combine.children' attribute on the dominant node, and try to match against + * 'append'...
      4. + *
      5. if it matches 'append', then set mergeChildren == false...the recessive children will be appended as + * siblings of the dominant children.
      6. + *
      7. 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.
      8. + *
    6. + *
    7. Iterate through the recessive children, and: + *
        + *
      1. if 'combine.id' is set and there is a corresponding dominant child (matched by value of 'combine.id'), + * merge the two.
      2. + *
      3. if mergeChildren == true and there is a corresponding dominant child (matched by element name), + * merge the two.
      4. + *
      5. otherwise, add the recessive child as a new child on the dominant root node.
      6. + *
    8. + *
  6. + *
*/ private static void mergeIntoXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive, Boolean childMergeOverride ) { @@ -109,9 +132,10 @@ private static void mergeIntoXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive, Boole if ( mergeSelf ) { - if ( isEmpty( dominant.getValue() ) ) + if ( isEmpty( dominant.getValue() ) && !isEmpty( recessive.getValue() ) ) { dominant.setValue( recessive.getValue() ); + dominant.setInputLocation( recessive.getInputLocation() ); } String[] recessiveAttrs = recessive.getAttributeNames(); diff --git a/src/test/java/org/codehaus/plexus/util/xml/Xpp3DomBuilderTest.java b/src/test/java/org/codehaus/plexus/util/xml/Xpp3DomBuilderTest.java index 302443c2..0c8e5a17 100644 --- a/src/test/java/org/codehaus/plexus/util/xml/Xpp3DomBuilderTest.java +++ b/src/test/java/org/codehaus/plexus/util/xml/Xpp3DomBuilderTest.java @@ -175,6 +175,35 @@ public void testEscapingInAttributes() assertEquals( "Compare stringified DOMs", newString, s ); } + @Test + public void testInputLocationTracking() + throws IOException, XmlPullParserException + { + Xpp3DomBuilder.InputLocationBuilder ilb = new Xpp3DomBuilder.InputLocationBuilder() { + public Object toInputLocation( XmlPullParser parser ) + { + return parser.getLineNumber(); // store only line number as a simple Integer + } + + }; + Xpp3Dom dom = Xpp3DomBuilder.build( new StringReader( createDomString() ), true, ilb ); + Xpp3Dom expectedDom = createExpectedDom(); + assertEquals( "root input location", expectedDom.getInputLocation(), dom.getInputLocation() ); + for( int i = 0; i < dom.getChildCount(); i++ ) + { + Xpp3Dom elt = dom.getChild( i ); + Xpp3Dom expectedElt = expectedDom.getChild( i ); + assertEquals( elt.getName() + " input location", expectedElt.getInputLocation(), elt.getInputLocation() ); + + if ( "el2".equals( elt.getName() ) ) + { + Xpp3Dom el3 = elt.getChild( 0 ); + Xpp3Dom expectedEl3 = expectedElt.getChild( 0 ); + assertEquals( el3.getName() + " input location", expectedEl3.getInputLocation(), el3.getInputLocation() ); + } + } + } + private static String getAttributeEncodedString() { StringBuilder domString = new StringBuilder(); @@ -237,23 +266,33 @@ private static String createDomString() private static Xpp3Dom createExpectedDom() { + int line = 1; Xpp3Dom expectedDom = new Xpp3Dom( "root" ); + expectedDom.setInputLocation( line ); Xpp3Dom el1 = new Xpp3Dom( "el1" ); + el1.setInputLocation( ++line ); el1.setValue( "element1" ); expectedDom.addChild( el1 ); + ++line; // newline trimmed in Xpp3Dom but not in source Xpp3Dom el2 = new Xpp3Dom( "el2" ); + el2.setInputLocation( ++line ); el2.setAttribute( "att2", "attribute2\nnextline" ); expectedDom.addChild( el2 ); Xpp3Dom el3 = new Xpp3Dom( "el3" ); + el3.setInputLocation( ++line ); el3.setAttribute( "att3", "attribute3" ); el3.setValue( "element3" ); el2.addChild( el3 ); + ++line; Xpp3Dom el4 = new Xpp3Dom( "el4" ); + el4.setInputLocation( ++line ); el4.setValue( "" ); expectedDom.addChild( el4 ); Xpp3Dom el5 = new Xpp3Dom( "el5" ); + el5.setInputLocation( ++line ); expectedDom.addChild( el5 ); Xpp3Dom el6 = new Xpp3Dom( "el6" ); + el6.setInputLocation( ++line ); el6.setAttribute( "xml:space", "preserve" ); el6.setValue( " do not trim " ); expectedDom.addChild( el6 ); diff --git a/src/test/java/org/codehaus/plexus/util/xml/Xpp3DomTest.java b/src/test/java/org/codehaus/plexus/util/xml/Xpp3DomTest.java index dd06f7b1..04444f31 100644 --- a/src/test/java/org/codehaus/plexus/util/xml/Xpp3DomTest.java +++ b/src/test/java/org/codehaus/plexus/util/xml/Xpp3DomTest.java @@ -27,6 +27,7 @@ import java.io.StringReader; import java.util.HashMap; +import org.codehaus.plexus.util.xml.pull.XmlPullParser; import org.codehaus.plexus.util.xml.pull.XmlPullParserException; import org.junit.Test; @@ -38,17 +39,21 @@ public void testShouldPerformAppendAtFirstSubElementLevel() // create the dominant DOM Xpp3Dom t1 = new Xpp3Dom( "top" ); t1.setAttribute( Xpp3Dom.CHILDREN_COMBINATION_MODE_ATTRIBUTE, Xpp3Dom.CHILDREN_COMBINATION_APPEND ); + t1.setInputLocation( "t1top" ); Xpp3Dom t1s1 = new Xpp3Dom( "topsub1" ); t1s1.setValue( "t1s1Value" ); + t1s1.setInputLocation( "t1s1" ); t1.addChild( t1s1 ); // create the recessive DOM Xpp3Dom t2 = new Xpp3Dom( "top" ); + t2.setInputLocation( "t2top" ); Xpp3Dom t2s1 = new Xpp3Dom( "topsub1" ); t2s1.setValue( "t2s1Value" ); + t2s1.setInputLocation( "t2s1" ); t2.addChild( t2s1 ); @@ -56,6 +61,12 @@ public void testShouldPerformAppendAtFirstSubElementLevel() Xpp3Dom result = Xpp3Dom.mergeXpp3Dom( t1, t2 ); assertEquals( 2, result.getChildren( "topsub1" ).length ); + assertEquals( "t2s1Value", result.getChildren( "topsub1" )[0].getValue() ); + assertEquals( "t1s1Value", result.getChildren( "topsub1" )[1].getValue() ); + + assertEquals( "t1top", result.getInputLocation() ); + assertEquals( "t2s1", result.getChildren( "topsub1" )[0].getInputLocation() ); + assertEquals( "t1s1", result.getChildren( "topsub1" )[1].getInputLocation() ); } @Test @@ -64,17 +75,21 @@ public void testShouldOverrideAppendAndDeepMerge() // create the dominant DOM Xpp3Dom t1 = new Xpp3Dom( "top" ); t1.setAttribute( Xpp3Dom.CHILDREN_COMBINATION_MODE_ATTRIBUTE, Xpp3Dom.CHILDREN_COMBINATION_APPEND ); + t1.setInputLocation( "t1top" ); Xpp3Dom t1s1 = new Xpp3Dom( "topsub1" ); t1s1.setValue( "t1s1Value" ); + t1s1.setInputLocation( "t1s1" ); t1.addChild( t1s1 ); // create the recessive DOM Xpp3Dom t2 = new Xpp3Dom( "top" ); + t2.setInputLocation( "t2top" ); Xpp3Dom t2s1 = new Xpp3Dom( "topsub1" ); t2s1.setValue( "t2s1Value" ); + t2s1.setInputLocation( "t2s1" ); t2.addChild( t2s1 ); @@ -82,6 +97,10 @@ public void testShouldOverrideAppendAndDeepMerge() Xpp3Dom result = Xpp3Dom.mergeXpp3Dom( t1, t2, Boolean.TRUE ); assertEquals( 1, result.getChildren( "topsub1" ).length ); + assertEquals( "t1s1Value", result.getChildren( "topsub1" )[0].getValue() ); + + assertEquals( "t1top", result.getInputLocation() ); + assertEquals( "t1s1", result.getChildren( "topsub1" )[0].getInputLocation() ); } @Test @@ -90,6 +109,7 @@ public void testShouldPerformSelfOverrideAtTopLevel() // create the dominant DOM Xpp3Dom t1 = new Xpp3Dom( "top" ); t1.setAttribute( "attr", "value" ); + t1.setInputLocation( "t1top" ); t1.setAttribute( Xpp3Dom.SELF_COMBINATION_MODE_ATTRIBUTE, Xpp3Dom.SELF_COMBINATION_OVERRIDE ); @@ -97,12 +117,14 @@ public void testShouldPerformSelfOverrideAtTopLevel() Xpp3Dom t2 = new Xpp3Dom( "top" ); t2.setAttribute( "attr2", "value2" ); t2.setValue( "t2Value" ); + t2.setInputLocation( "t2top" ); // merge and check results. Xpp3Dom result = Xpp3Dom.mergeXpp3Dom( t1, t2 ); assertEquals( 2, result.getAttributeNames().length ); assertNull( result.getValue() ); + assertEquals( "t1top", result.getInputLocation() ); } @Test @@ -111,11 +133,13 @@ public void testShouldMergeValuesAtTopLevelByDefault() // create the dominant DOM Xpp3Dom t1 = new Xpp3Dom( "top" ); t1.setAttribute( "attr", "value" ); + t1.setInputLocation( "t1top" ); // create the recessive DOM Xpp3Dom t2 = new Xpp3Dom( "top" ); t2.setAttribute( "attr2", "value2" ); t2.setValue( "t2Value" ); + t2.setInputLocation( "t2top" ); // merge and check results. Xpp3Dom result = Xpp3Dom.mergeXpp3Dom( t1, t2 ); @@ -124,6 +148,7 @@ public void testShouldMergeValuesAtTopLevelByDefault() assertEquals( 2, result.getAttributeNames().length ); assertEquals( result.getValue(), t2.getValue() ); + assertEquals( "t2top", result.getInputLocation() ); } @Test @@ -213,10 +238,12 @@ public void testShouldOverwritePluginConfigurationSubItemsByDefault() throws XmlPullParserException, IOException { String parentConfigStr = "onetwo"; - Xpp3Dom parentConfig = Xpp3DomBuilder.build( new StringReader( parentConfigStr ) ); + Xpp3Dom parentConfig = + Xpp3DomBuilder.build( new StringReader( parentConfigStr ), new FixedInputLocationBuilder( "parent" ) ); String childConfigStr = "three"; - Xpp3Dom childConfig = Xpp3DomBuilder.build( new StringReader( childConfigStr ) ); + Xpp3Dom childConfig = + Xpp3DomBuilder.build( new StringReader( childConfigStr ), new FixedInputLocationBuilder( "child" ) ); Xpp3Dom result = Xpp3Dom.mergeXpp3Dom( childConfig, parentConfig ); Xpp3Dom items = result.getChild( "items" ); @@ -225,6 +252,7 @@ public void testShouldOverwritePluginConfigurationSubItemsByDefault() Xpp3Dom item = items.getChild( 0 ); assertEquals( "three", item.getValue() ); + assertEquals( "child", item.getInputLocation() ); } @Test @@ -232,11 +260,13 @@ public void testShouldMergePluginConfigurationSubItemsWithMergeAttributeSet() throws XmlPullParserException, IOException { String parentConfigStr = "onetwo"; - Xpp3Dom parentConfig = Xpp3DomBuilder.build( new StringReader( parentConfigStr ) ); + Xpp3Dom parentConfig = + Xpp3DomBuilder.build( new StringReader( parentConfigStr ), new FixedInputLocationBuilder( "parent" ) ); String childConfigStr = "three"; - Xpp3Dom childConfig = Xpp3DomBuilder.build( new StringReader( childConfigStr ) ); + Xpp3Dom childConfig = + Xpp3DomBuilder.build( new StringReader( childConfigStr ), new FixedInputLocationBuilder( "child" ) ); Xpp3Dom result = Xpp3Dom.mergeXpp3Dom( childConfig, parentConfig ); Xpp3Dom items = result.getChild( "items" ); @@ -246,8 +276,11 @@ public void testShouldMergePluginConfigurationSubItemsWithMergeAttributeSet() Xpp3Dom[] item = items.getChildren(); assertEquals( "one", item[0].getValue() ); + assertEquals( "parent", item[0].getInputLocation() ); assertEquals( "two", item[1].getValue() ); + assertEquals( "parent", item[1].getInputLocation() ); assertEquals( "three", item[2].getValue() ); + assertEquals( "child", item[2].getInputLocation() ); } @Test @@ -295,4 +328,20 @@ public void testDupeChildren() assertNotNull( dom ); assertEquals( "y", dom.getChild( "foo" ).getValue() ); } + + private static class FixedInputLocationBuilder + implements Xpp3DomBuilder.InputLocationBuilder + { + private final Object location; + + public FixedInputLocationBuilder( Object location ) + { + this.location = location; + } + + public Object toInputLocation( XmlPullParser parser ) + { + return location; + } + } } diff --git a/src/test/java/org/codehaus/plexus/util/xml/Xpp3DomUtilsTest.java b/src/test/java/org/codehaus/plexus/util/xml/Xpp3DomUtilsTest.java index f41f2641..f4d0eb04 100644 --- a/src/test/java/org/codehaus/plexus/util/xml/Xpp3DomUtilsTest.java +++ b/src/test/java/org/codehaus/plexus/util/xml/Xpp3DomUtilsTest.java @@ -20,6 +20,7 @@ import java.io.StringReader; +import org.codehaus.plexus.util.xml.pull.XmlPullParser; import org.junit.Test; public class Xpp3DomUtilsTest @@ -34,17 +35,44 @@ public void testCombineId() String rhs = "" + "RHS-ONLYRHS" + "TOOVERWRITERHS" + ""; - Xpp3Dom leftDom = Xpp3DomBuilder.build( new StringReader( lhs ) ); - Xpp3Dom rightDom = Xpp3DomBuilder.build( new StringReader( rhs ) ); + 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 ); - assertEquals( "LHS-ONLY", mergeResult.getChildren( "property" )[0].getChild( "name" ).getValue() ); - assertEquals( "LHS", mergeResult.getChildren( "property" )[0].getChild( "value" ).getValue() ); + 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 + { + private final Object location; + + public FixedInputLocationBuilder( Object location ) + { + this.location = location; + } + + public Object toInputLocation( XmlPullParser parser ) + { + return location; + } } }