[Git][java-team/plexus-utils2][master] 7 commits: New upstream version 3.2.0

Emmanuel Bourg gitlab at salsa.debian.org
Fri Jul 12 08:14:17 BST 2019



Emmanuel Bourg pushed to branch master at Debian Java Maintainers / plexus-utils2


Commits:
4e033ff2 by Emmanuel Bourg at 2019-07-12T06:08:26Z
New upstream version 3.2.0
- - - - -
553fd973 by Emmanuel Bourg at 2019-07-12T06:08:41Z
New upstream version 3.2.1
- - - - -
19228e1d by Emmanuel Bourg at 2019-07-12T06:08:42Z
Update upstream source from tag 'upstream/3.2.1'

Update to upstream version '3.2.1'
with Debian dir 5a245d761fd716ded9204dc36ce27c28d9dd0eb3
- - - - -
5adc6e71 by Emmanuel Bourg at 2019-07-12T06:49:26Z
Refreshed the patches

- - - - -
d487bf71 by Emmanuel Bourg at 2019-07-12T06:50:55Z
Disabled the performance tests

- - - - -
9d5fbd92 by Emmanuel Bourg at 2019-07-12T06:51:07Z
Standards-Version updated to 4.4.0

- - - - -
ea133d5b by Emmanuel Bourg at 2019-07-12T06:51:13Z
Upload to unstable

- - - - -


24 changed files:

- .travis.yml
- debian/changelog
- debian/control
- debian/maven.ignoreRules
- debian/patches/01-add-junit-dependency.patch
- debian/patches/03-maven-plugin-testing-compatibility.patch
- − debian/patches/05-fix-nio-file-copy.patch
- + debian/patches/06-ignore-jmh-benchmarks.patch
- debian/patches/series
- pom.xml
- src/main/java/org/codehaus/plexus/util/MatchPattern.java
- src/main/java/org/codehaus/plexus/util/NioFiles.java
- src/main/java/org/codehaus/plexus/util/StringUtils.java
- src/main/java/org/codehaus/plexus/util/xml/Xpp3Dom.java
- src/main/java/org/codehaus/plexus/util/xml/Xpp3DomBuilder.java
- src/main/java/org/codehaus/plexus/util/xml/Xpp3DomUtils.java
- src/main/java/org/codehaus/plexus/util/xml/pull/MXParser.java
- src/test/java/org/codehaus/plexus/util/DirectoryScannerTest.java
- src/test/java/org/codehaus/plexus/util/MatchPatternTest.java
- src/test/java/org/codehaus/plexus/util/xml/Xpp3DomBuilderTest.java
- + src/test/java/org/codehaus/plexus/util/xml/Xpp3DomPerfTest.java
- src/test/java/org/codehaus/plexus/util/xml/Xpp3DomTest.java
- src/test/java/org/codehaus/plexus/util/xml/Xpp3DomUtilsTest.java
- src/test/java/org/codehaus/plexus/util/xml/pull/MXParserTest.java


Changes:

=====================================
.travis.yml
=====================================
@@ -1,7 +1,9 @@
 language: java
 jdk:
   - openjdk7
-  - oraclejdk8
+  - openjdk8
+  - openjdk11
+#  - openjdk12 add once code is requires Java 7
 
 # No need for preliminary install step.
 install: true


=====================================
debian/changelog
=====================================
@@ -1,3 +1,13 @@
+plexus-utils2 (3.2.1-1) unstable; urgency=medium
+
+  * Team upload.
+  * New upstream release
+    - Refreshed the patches
+    - Disabled the performance tests
+  * Standards-Version updated to 4.4.0
+
+ -- Emmanuel Bourg <ebourg at apache.org>  Fri, 12 Jul 2019 08:51:10 +0200
+
 plexus-utils2 (3.1.1-1) unstable; urgency=medium
 
   * New upstream release


=====================================
debian/control
=====================================
@@ -11,7 +11,7 @@ Build-Depends:
  libmaven-javadoc-plugin-java,
  libmaven-plugin-testing-java,
  maven-debian-helper
-Standards-Version: 4.3.0
+Standards-Version: 4.4.0
 Vcs-Git: https://salsa.debian.org/java-team/plexus-utils2.git
 Vcs-Browser: https://salsa.debian.org/java-team/plexus-utils2
 Homepage: http://codehaus-plexus.github.io/plexus-utils/


=====================================
debian/maven.ignoreRules
=====================================
@@ -17,3 +17,4 @@
 org.apache.maven.plugins maven-enforcer-plugin * * * *
 org.apache.maven.plugins maven-release-plugin * * * *
 org.apache.maven.plugins maven-scm-publish-plugin * * * *
+org.openjdk.jmh * * * * *


=====================================
debian/patches/01-add-junit-dependency.patch
=====================================
@@ -3,16 +3,16 @@ Author: Emmanuel Bourg <ebourg at apache.org>
 Forwarded: not-needed
 --- a/pom.xml
 +++ b/pom.xml
-@@ -61,6 +61,12 @@
-         <version>1.1</version>
-         <scope>test</scope>
-       </dependency>
-+      <dependency>
-+        <groupId>junit</groupId>
-+        <artifactId>junit</artifactId>
-+        <version>3.8.2</version>
-+        <scope>test</scope>
-+      </dependency>
+@@ -69,6 +69,12 @@
+       <version>1.21</version>
+       <scope>test</scope>
+     </dependency>
++    <dependency>
++      <groupId>junit</groupId>
++      <artifactId>junit</artifactId>
++      <version>4.12</version>
++      <scope>test</scope>
++    </dependency>
    </dependencies>
+ 
    <build>
-     <pluginManagement>


=====================================
debian/patches/03-maven-plugin-testing-compatibility.patch
=====================================
@@ -3,16 +3,16 @@ Author: Emmanuel Bourg <ebourg at apache.org>
 Forwarded: no
 --- a/pom.xml
 +++ b/pom.xml
-@@ -62,6 +62,12 @@
-         <scope>test</scope>
-       </dependency>
-       <dependency>
-+        <groupId>org.apache.maven</groupId>
-+        <artifactId>maven-core</artifactId>
-+        <version>3.x</version>
-+        <scope>test</scope>
-+      </dependency>
-+      <dependency>
-         <groupId>junit</groupId>
-         <artifactId>junit</artifactId>
-         <version>3.8.2</version>
+@@ -70,6 +70,12 @@
+       <scope>test</scope>
+     </dependency>
+     <dependency>
++      <groupId>org.apache.maven</groupId>
++      <artifactId>maven-core</artifactId>
++      <version>3.x</version>
++      <scope>test</scope>
++    </dependency>
++    <dependency>
+       <groupId>junit</groupId>
+       <artifactId>junit</artifactId>
+       <version>4.12</version>


=====================================
debian/patches/05-fix-nio-file-copy.patch deleted
=====================================
@@ -1,14 +0,0 @@
-Description: Follow symlink when copying files with NIO
-Author: Michael Simacek <msimacek at redhat.com>
-Forwarded: https://github.com/codehaus-plexus/plexus-utils/issues/45
---- a/src/main/java/org/codehaus/plexus/util/NioFiles.java
-+++ b/src/main/java/org/codehaus/plexus/util/NioFiles.java
-@@ -138,7 +138,7 @@
-         throws IOException
-     {
-         Path copy = Files.copy( source.toPath(), target.toPath(), StandardCopyOption.REPLACE_EXISTING,
--                                StandardCopyOption.COPY_ATTRIBUTES, LinkOption.NOFOLLOW_LINKS );
-+                                StandardCopyOption.COPY_ATTRIBUTES );
-         return copy.toFile();
-     }
- 


=====================================
debian/patches/06-ignore-jmh-benchmarks.patch
=====================================
@@ -0,0 +1,20 @@
+Description: Disables the performance tests
+Author: Emmanuel Bourg <ebourg at apache.org>
+Forwarded: not-needed
+--- a/pom.xml
++++ b/pom.xml
+@@ -95,6 +95,14 @@
+     </pluginManagement>
+     <plugins>
+       <plugin>
++        <artifactId>maven-compiler-plugin</artifactId>
++        <configuration>
++          <testExcludes>
++            <testExclude>**/*PerfTest*</testExclude>
++          </testExcludes>
++        </configuration>
++      </plugin>
++      <plugin>
+         <groupId>org.apache.maven.plugins</groupId>
+         <artifactId>maven-scm-publish-plugin</artifactId>
+         <configuration>


=====================================
debian/patches/series
=====================================
@@ -1,4 +1,4 @@
 01-add-junit-dependency.patch
 02-propertyutils-compatibility.patch
 03-maven-plugin-testing-compatibility.patch
-05-fix-nio-file-copy.patch
+06-ignore-jmh-benchmarks.patch


=====================================
pom.xml
=====================================
@@ -22,11 +22,11 @@ limitations under the License.
   <parent>
     <groupId>org.codehaus.plexus</groupId>
     <artifactId>plexus</artifactId>
-    <version>4.0</version>
+    <version>5.1</version>
   </parent>
 
   <artifactId>plexus-utils</artifactId>
-  <version>3.1.1</version>
+  <version>3.2.1</version>
 
   <name>Plexus Common Utilities</name>
   <description>A collection of various utility classes to ease working with strings, files, command lines, XML and
@@ -37,7 +37,7 @@ limitations under the License.
     <connection>scm:git:git at github.com:codehaus-plexus/plexus-utils.git</connection>
     <developerConnection>scm:git:git at github.com:codehaus-plexus/plexus-utils.git</developerConnection>
     <url>http://github.com/codehaus-plexus/plexus-utils</url>
-    <tag>plexus-utils-3.1.1</tag>
+    <tag>plexus-utils-3.2.1</tag>
   </scm>
   <issueManagement>
     <system>github</system>
@@ -50,10 +50,6 @@ limitations under the License.
     </site>
   </distributionManagement>
 
-  <properties>
-    <javaVersion>6</javaVersion>
-  </properties>
-
   <dependencies>
       <dependency>
         <groupId>org.apache.maven.shared</groupId>
@@ -61,7 +57,20 @@ limitations under the License.
         <version>1.1</version>
         <scope>test</scope>
       </dependency>
+    <dependency>
+      <groupId>org.openjdk.jmh</groupId>
+      <artifactId>jmh-core</artifactId>
+      <version>1.21</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.openjdk.jmh</groupId>
+      <artifactId>jmh-generator-annprocess</artifactId>
+      <version>1.21</version>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
+
   <build>
     <pluginManagement>
       <plugins>
@@ -114,7 +123,6 @@ limitations under the License.
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-enforcer-plugin</artifactId>
-        <version>1.1.1</version>
         <executions>
           <execution>
             <id>enforce-java</id>
@@ -133,6 +141,7 @@ limitations under the License.
       </plugin>
     </plugins>
   </build>
+
   <profiles>
     <profile>
       <!-- See https://github.com/codehaus-plexus/plexus-utils/pull/27 -->


=====================================
src/main/java/org/codehaus/plexus/util/MatchPattern.java
=====================================
@@ -94,7 +94,7 @@ public boolean matchPatternStart( String str, boolean isCaseSensitive )
         }
         else
         {
-            String altStr = source.replace( '\\', '/' );
+            String altStr = str.replace( '\\', '/' );
 
             return SelectorUtils.matchAntPathPatternStart( this, str, File.separator, isCaseSensitive )
                 || SelectorUtils.matchAntPathPatternStart( this, altStr, "/", isCaseSensitive );


=====================================
src/main/java/org/codehaus/plexus/util/NioFiles.java
=====================================
@@ -138,7 +138,7 @@ public static File copy( File source, File target )
         throws IOException
     {
         Path copy = Files.copy( source.toPath(), target.toPath(), StandardCopyOption.REPLACE_EXISTING,
-                                StandardCopyOption.COPY_ATTRIBUTES, LinkOption.NOFOLLOW_LINKS );
+                                StandardCopyOption.COPY_ATTRIBUTES );
         return copy.toFile();
     }
 


=====================================
src/main/java/org/codehaus/plexus/util/StringUtils.java
=====================================
@@ -57,6 +57,7 @@
 import java.util.Iterator;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Objects;
 import java.util.StringTokenizer;
 
 /**
@@ -258,10 +259,12 @@ public static boolean isNotBlank( String str )
      * @param str1 the first string
      * @param str2 the second string
      * @return <code>true</code> if the Strings are equal, case sensitive, or both <code>null</code>
+     * @see Objects#equals(Object, Object)
      */
+    @Deprecated
     public static boolean equals( String str1, String str2 )
     {
-        return ( str1 == null ? str2 == null : str1.equals( str2 ) );
+        return Objects.equals( str1, str2 );
     }
 
     /**
@@ -2038,7 +2041,9 @@ public static boolean isNumericSpace( String str )
      *
      * @param obj the Object to check
      * @return the passed in Object's toString, or blank if it was <code>null</code>
+     * @see Objects#toString(Object, String)
      */
+    @Deprecated
     public static String defaultString( Object obj )
     {
         return defaultString( obj, "" );
@@ -2053,10 +2058,12 @@ public static String defaultString( Object obj )
      * @param obj the Object to check
      * @param defaultString the default String to return if str is <code>null</code>
      * @return the passed in string, or the default if it was <code>null</code>
+     * @see Objects#toString(Object, String)
      */
+    @Deprecated
     public static String defaultString( Object obj, String defaultString )
     {
-        return ( obj == null ) ? defaultString : obj.toString();
+        return Objects.toString( obj, defaultString );
     }
 
     // Reversing


=====================================
src/main/java/org/codehaus/plexus/util/xml/Xpp3Dom.java
=====================================
@@ -23,9 +23,11 @@
 import java.io.StringWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
+import java.util.ListIterator;
 import java.util.Map;
 
 /**
@@ -44,10 +46,13 @@
 
     protected final List<Xpp3Dom> childList;
 
-    protected final Map<String, Xpp3Dom> childMap;
-
     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];
@@ -83,7 +88,15 @@ public Xpp3Dom( String name )
     {
         this.name = name;
         childList = new ArrayList<Xpp3Dom>();
-        childMap = new HashMap<String, Xpp3Dom>();
+    }
+
+    /**
+     * @since 3.2.0
+     */
+    public Xpp3Dom( String name, Object inputLocation )
+    {
+        this( name );
+        this.inputLocation = inputLocation;
     }
 
     /**
@@ -100,11 +113,11 @@ public Xpp3Dom( Xpp3Dom src )
     public Xpp3Dom( Xpp3Dom src, String name )
     {
         this.name = name;
+        this.inputLocation = src.inputLocation;
 
         int childCount = src.getChildCount();
 
         childList = new ArrayList<Xpp3Dom>( childCount );
-        childMap = new HashMap<String, Xpp3Dom>( childCount << 1 );
 
         setValue( src.getValue() );
 
@@ -155,13 +168,13 @@ public void setValue( String value )
         }
         else
         {
-            return (String[]) attributes.keySet().toArray( new String[attributes.size()] );
+            return attributes.keySet().toArray( EMPTY_STRING_ARRAY );
         }
     }
 
     public String getAttribute( String name )
     {
-        return ( null != attributes ) ? (String) attributes.get( name ) : null;
+        return ( null != attributes ) ? attributes.get( name ) : null;
     }
 
     /**
@@ -194,19 +207,30 @@ public void setAttribute( String name, String value )
 
     public Xpp3Dom getChild( int i )
     {
-        return (Xpp3Dom) childList.get( i );
+        return childList.get( i );
     }
 
     public Xpp3Dom getChild( String name )
     {
-        return (Xpp3Dom) childMap.get( name );
+        if ( name != null )
+        {
+            ListIterator<Xpp3Dom> it = childList.listIterator( childList.size() );
+            while ( it.hasPrevious() )
+            {
+                Xpp3Dom child = it.previous();
+                if ( name.equals( child.getName() ) )
+                {
+                    return child;
+                }
+            }
+        }
+        return null;
     }
 
     public void addChild( Xpp3Dom xpp3Dom )
     {
         xpp3Dom.setParent( this );
         childList.add( xpp3Dom );
-        childMap.put( xpp3Dom.getName(), xpp3Dom );
     }
 
     public Xpp3Dom[] getChildren()
@@ -217,31 +241,45 @@ public void addChild( Xpp3Dom xpp3Dom )
         }
         else
         {
-            return (Xpp3Dom[]) childList.toArray( new Xpp3Dom[childList.size()] );
+            return childList.toArray( EMPTY_DOM_ARRAY );
         }
     }
 
     public Xpp3Dom[] getChildren( String name )
+    {
+        return getChildrenAsList( name ).toArray( EMPTY_DOM_ARRAY );
+    }
+
+    private List<Xpp3Dom> getChildrenAsList( String name )
     {
         if ( null == childList )
         {
-            return EMPTY_DOM_ARRAY;
+            return Collections.emptyList();
         }
         else
         {
-            ArrayList<Xpp3Dom> children = new ArrayList<Xpp3Dom>();
-            int size = childList.size();
+            ArrayList<Xpp3Dom> children = null;
 
-            for ( Xpp3Dom aChildList : childList )
+            for ( Xpp3Dom configuration : childList )
             {
-                Xpp3Dom configuration = (Xpp3Dom) aChildList;
                 if ( name.equals( configuration.getName() ) )
                 {
+                    if ( children == null )
+                    {
+                        children = new ArrayList<Xpp3Dom>();
+                    }
                     children.add( configuration );
                 }
             }
 
-            return (Xpp3Dom[]) children.toArray( new Xpp3Dom[children.size()] );
+            if ( children != null )
+            {
+                return children;
+            }
+            else
+            {
+                return Collections.emptyList();
+            }
         }
     }
 
@@ -258,7 +296,6 @@ public int getChildCount()
     public void removeChild( int i )
     {
         Xpp3Dom child = getChild( i );
-        childMap.values().remove( child );
         childList.remove( i );
         // In case of any dangling references
         child.setParent( null );
@@ -278,6 +315,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 +353,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.<p>
+     * The algorithm is as follows:
+     * <ol>
+     * <li> if the recessive DOM is null, there is nothing to do... return.</li>
+     * <li> Determine whether the dominant node will suppress the recessive one (flag=mergeSelf).
+     *   <ol type="A">
+     *   <li> 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.</li>
+     *   <li> 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.</li>
+     *   </ol></li>
+     * <li> If mergeSelf == true
+     *   <ol type="A">
+     *   <li> if the dominant root node's value is empty, set it to the recessive root node's value</li>
+     *   <li> For each attribute in the recessive root node which is not set in the dominant root node, set it.</li>
+     *   <li> Determine whether children from the recessive DOM will be merged or appended to the dominant DOM as
+     *        siblings (flag=mergeChildren).
+     *     <ol type="i">
+     *     <li> if childMergeOverride is set (non-null), use that value (true/false)</li>
+     *     <li> retrieve the 'combine.children' attribute on the dominant node, and try to match against
+     *          'append'...</li>
+     *     <li> if it matches 'append', then set mergeChildren == false...the recessive children will be appended as
+     *          siblings of the dominant children.</li>
+     *     <li> 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.</li>
+     *     </ol></li>
+     *   <li> Iterate through the recessive children, and:
+     *     <ol type="i">
+     *     <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>
+     *     </ol></li>
+     *   </ol></li>
+     * </ol>
      */
     private static void mergeIntoXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive, Boolean childMergeOverride )
     {
@@ -333,17 +408,20 @@ 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();
-            for ( String attr : recessiveAttrs )
+            if ( recessive.attributes != null )
             {
-                if ( isEmpty( dominant.getAttribute( attr ) ) )
+                for ( String attr : recessive.attributes.keySet() )
                 {
-                    dominant.setAttribute( attr, recessive.getAttribute( attr ) );
+                    if ( isEmpty( dominant.getAttribute( attr ) ) )
+                    {
+                        dominant.setAttribute( attr, recessive.getAttribute( attr ) );
+                    }
                 }
             }
 
@@ -387,12 +465,16 @@ private static void mergeIntoXpp3Dom( Xpp3Dom dominant, Xpp3Dom recessive, Boole
                 {
                     Map<String, Iterator<Xpp3Dom>> commonChildren = new HashMap<String, Iterator<Xpp3Dom>>();
 
-                    for ( String childName : recessive.childMap.keySet() )
+                    for ( Xpp3Dom recChild : recessive.childList )
                     {
-                        Xpp3Dom[] dominantChildren = dominant.getChildren( childName );
-                        if ( dominantChildren.length > 0 )
+                        if ( commonChildren.containsKey( recChild.name ) )
+                        {
+                            continue;
+                        }
+                        List<Xpp3Dom> dominantChildren = dominant.getChildrenAsList( recChild.name );
+                        if ( dominantChildren.size() > 0 )
                         {
-                            commonChildren.put( childName, Arrays.asList( dominantChildren ).iterator() );
+                            commonChildren.put( recChild.name, dominantChildren.iterator() );
                         }
                     }
 


=====================================
src/main/java/org/codehaus/plexus/util/xml/Xpp3DomBuilder.java
=====================================
@@ -37,7 +37,16 @@
     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<Xpp3Dom> elements = new ArrayList<Xpp3Dom>();
 
@@ -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 );
+    }
 }


=====================================
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.<p>
+     * The algorithm is as follows:
+     * <ol>
+     * <li> if the recessive DOM is null, there is nothing to do... return.</li>
+     * <li> Determine whether the dominant node will suppress the recessive one (flag=mergeSelf).
+     *   <ol type="A">
+     *   <li> 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.</li>
+     *   <li> 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.</li>
+     *   </ol></li>
+     * <li> If mergeSelf == true
+     *   <ol type="A">
+     *   <li> if the dominant root node's value is empty, set it to the recessive root node's value</li>
+     *   <li> For each attribute in the recessive root node which is not set in the dominant root node, set it.</li>
+     *   <li> Determine whether children from the recessive DOM will be merged or appended to the dominant DOM as
+     *        siblings (flag=mergeChildren).
+     *     <ol type="i">
+     *     <li> if childMergeOverride is set (non-null), use that value (true/false)</li>
+     *     <li> retrieve the 'combine.children' attribute on the dominant node, and try to match against
+     *          'append'...</li>
+     *     <li> if it matches 'append', then set mergeChildren == false...the recessive children will be appended as
+     *          siblings of the dominant children.</li>
+     *     <li> 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.</li>
+     *     </ol></li>
+     *   <li> Iterate through the recessive children, and:
+     *     <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 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>
+     *     </ol></li>
+     *   </ol></li>
+     * </ol>
      */
     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();


=====================================
src/main/java/org/codehaus/plexus/util/xml/pull/MXParser.java
=====================================
@@ -2664,13 +2664,16 @@ else if ( ch == '\t' || ch == '\n' || ch == '\r' )
         entityRefName = null;
         posStart = pos;
         char ch = more();
-        StringBuilder sb = new StringBuilder();
         if ( ch == '#' )
         {
             // parse character reference
+
             char charRef = 0;
             ch = more();
-            if ( ch == 'x' )
+            StringBuilder sb = new StringBuilder();
+            boolean isHex = ( ch == 'x' );
+
+            if ( isHex )
             {
                 // encoded in hex
                 while ( true )
@@ -2710,6 +2713,7 @@ else if ( ch >= 'A' && ch <= 'F' )
                     if ( ch >= '0' && ch <= '9' )
                     {
                         charRef = (char) ( charRef * 10 + ( ch - '0' ) );
+                        sb.append( ch );
                     }
                     else if ( ch == ';' )
                     {
@@ -2724,20 +2728,19 @@ else if ( ch >= 'A' && ch <= 'F' )
                 }
             }
             posEnd = pos - 1;
-            if ( sb.length() > 0 )
+            try
             {
-                char[] tmp = toChars( Integer.parseInt( sb.toString(), 16 ) );
-                charRefOneCharBuf = tmp;
-                if ( tokenize )
-                {
-                    text = newString( charRefOneCharBuf, 0, charRefOneCharBuf.length );
-                }
-                return charRefOneCharBuf;
+                charRefOneCharBuf = toChars( Integer.parseInt( sb.toString(), isHex ? 16 : 10 ) );
+            }
+            catch ( IllegalArgumentException e )
+            {
+                throw new XmlPullParserException( "character reference (with " + ( isHex ? "hex" : "decimal" )
+                    + " value " + sb.toString() + ") is invalid", this, null );
             }
-            charRefOneCharBuf[0] = charRef;
+
             if ( tokenize )
             {
-                text = newString( charRefOneCharBuf, 0, 1 );
+                text = newString( charRefOneCharBuf, 0, charRefOneCharBuf.length );
             }
             return charRefOneCharBuf;
         }
@@ -3014,6 +3017,7 @@ protected boolean parsePI()
 
         try
         {
+            boolean seenPITarget = false;
             boolean seenQ = false;
             char ch = more();
             if ( isS( ch ) )
@@ -3028,6 +3032,11 @@ protected boolean parsePI()
 
                 if ( ch == '?' )
                 {
+                    if ( !seenPITarget )
+                    {
+                        throw new XmlPullParserException( "processing instruction PITarget name not found", this,
+                                                          null );
+                    }
                     seenQ = true;
                 }
                 else if ( ch == '>' )
@@ -3036,7 +3045,12 @@ else if ( ch == '>' )
                     {
                         break; // found end sequence!!!!
                     }
-                    seenQ = false;
+
+                    if ( !seenPITarget )
+                    {
+                        throw new XmlPullParserException( "processing instruction PITarget name not found", this,
+                                                          null );
+                    }
                 }
                 else
                 {
@@ -3075,6 +3089,7 @@ else if ( ch == '>' )
                             }
                         }
                     }
+
                     seenQ = false;
                 }
                 if ( normalizeIgnorableWS )
@@ -3124,6 +3139,7 @@ else if ( ch == '\n' )
                         normalizedCR = false;
                     }
                 }
+                seenPITarget = true;
                 ch = more();
             }
         }
@@ -3689,31 +3705,57 @@ else if ( expand )
                     StringBuilder expectedTagStack = new StringBuilder();
                     if ( depth > 0 )
                     {
-                        // final char[] cbuf = elRawName[depth];
-                        // final String startname = new String(cbuf, 0, elRawNameEnd[depth]);
-                        expectedTagStack.append( " - expected end tag" );
-                        if ( depth > 1 )
-                        {
-                            expectedTagStack.append( "s" ); // more than one end tag
-                        }
-                        expectedTagStack.append( " " );
-                        for ( int i = depth; i > 0; i-- )
+                        if ( elRawName == null || elRawName[depth] == null )
                         {
-                            String tagName = new String( elRawName[i], 0, elRawNameEnd[i] );
-                            expectedTagStack.append( "</" ).append( tagName ).append( '>' );
+                            String tagName = new String( buf, posStart + 1, pos - posStart - 1 );
+                            expectedTagStack.append( " - expected the opening tag <" ).append( tagName ).append( "...>" );
                         }
-                        expectedTagStack.append( " to close" );
-                        for ( int i = depth; i > 0; i-- )
+                        else
                         {
-                            if ( i != depth )
+                            // final char[] cbuf = elRawName[depth];
+                            // final String startname = new String(cbuf, 0, elRawNameEnd[depth]);
+                            expectedTagStack.append( " - expected end tag" );
+                            if ( depth > 1 )
                             {
-                                expectedTagStack.append( " and" ); // more than one end tag
+                                expectedTagStack.append( "s" ); // more than one end tag
                             }
-                            String tagName = new String( elRawName[i], 0, elRawNameEnd[i] );
-                            expectedTagStack.append( " start tag <" ).append( tagName ).append( ">" );
-                            expectedTagStack.append( " from line " ).append( elRawNameLine[i] );
+                            expectedTagStack.append( " " );
+
+                            for ( int i = depth; i > 0; i-- )
+                            {
+                                if ( elRawName == null || elRawName[i] == null )
+                                {
+                                    String tagName = new String( buf, posStart + 1, pos - posStart - 1 );
+                                    expectedTagStack.append( " - expected the opening tag <" ).append( tagName ).append( "...>" );
+                                }
+                                else
+                                {
+                                    String tagName = new String( elRawName[i], 0, elRawNameEnd[i] );
+                                    expectedTagStack.append( "</" ).append( tagName ).append( '>' );
+                                }
+                            }
+                            expectedTagStack.append( " to close" );
+                            for ( int i = depth; i > 0; i-- )
+                            {
+                                if ( i != depth )
+                                {
+                                    expectedTagStack.append( " and" ); // more than one end tag
+                                }
+                                if ( elRawName == null || elRawName[i] == null )
+                                {
+                                    String tagName = new String( buf, posStart + 1, pos - posStart - 1 );
+                                    expectedTagStack.append( " start tag <" ).append( tagName ).append( ">" );
+                                    expectedTagStack.append( " from line " ).append( elRawNameLine[i] );
+                                }
+                                else
+                                {
+                                String tagName = new String( elRawName[i], 0, elRawNameEnd[i] );
+                                expectedTagStack.append( " start tag <" ).append( tagName ).append( ">" );
+                                expectedTagStack.append( " from line " ).append( elRawNameLine[i] );
+                                }
+                            }
+                            expectedTagStack.append( ", parser stopped on" );
                         }
-                        expectedTagStack.append( ", parser stopped on" );
                     }
                     throw new EOFException( "no more data available" + expectedTagStack.toString()
                         + getPositionDescription() );
@@ -3996,15 +4038,21 @@ private static boolean isHighSurrogate( char ch )
         return ( MIN_HIGH_SURROGATE <= ch && MAX_HIGH_SURROGATE >= ch );
     }
 
-    private static final int MIN_CODE_POINT = 0x000000;
-
     private static final int MAX_CODE_POINT = 0x10FFFF;
 
     private static final int MIN_SUPPLEMENTARY_CODE_POINT = 0x10000;
 
+    /**
+     * Check if the provided parameter is a valid Char, according to: {@link https://www.w3.org/TR/REC-xml/#NT-Char}
+     * 
+     * @param codePoint the numeric value to check
+     * @return true if it is a valid numeric character reference. False otherwise.
+     */
     private static boolean isValidCodePoint( int codePoint )
     {
-        return ( MIN_CODE_POINT <= codePoint && MAX_CODE_POINT >= codePoint );
+        // Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
+        return codePoint == 0x9 || codePoint == 0xA || codePoint == 0xD || ( 0x20 <= codePoint && codePoint <= 0xD7FF )
+            || ( 0xE000 <= codePoint && codePoint <= 0xFFFD ) || ( 0x10000 <= codePoint && codePoint <= 0x10FFFF );
     }
 
     private static boolean isSupplementaryCodePoint( int codePoint )


=====================================
src/test/java/org/codehaus/plexus/util/DirectoryScannerTest.java
=====================================
@@ -31,8 +31,11 @@
 import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TestName;
@@ -50,6 +53,19 @@
     
     private static String testDir = getTestDirectory().getPath();
 
+    @Before
+    public void setUp()
+    {
+        try
+        {
+            FileUtils.deleteDirectory( testDir );
+        }
+        catch ( IOException e )
+        {
+            fail( "Could not delete directory " + testDir );
+        }
+    }
+
     @Test
     public void testCrossPlatformIncludesString()
         throws IOException, URISyntaxException
@@ -477,6 +493,75 @@ public void testRegexWithSlashInsideCharacterClass()
         assertInclusionsAndExclusions( ds.getIncludedFiles(), excludedPaths, includedPaths );
     }
 
+    /**
+     * Test that the directory scanning does not enter into not matching directories.
+     *
+     * @see <a href="https://github.com/codehaus-plexus/plexus-utils/issues/63">Issue #63</a>
+     * @throws IOException if occurs an I/O error.
+     */
+    @Test
+    public void testDoNotScanUnnecesaryDirectories()
+        throws IOException
+    {
+        createTestDirectories();
+
+        // create additional directories 'anotherDir1', 'anotherDir2' and 'anotherDir3' with a 'file1.dat' file
+        FileUtils.mkdir( testDir + File.separator + "directoryTest" + File.separator + "testDir123" + File.separator
+            + "anotherDir1" );
+        FileUtils.mkdir( testDir + File.separator + "directoryTest" + File.separator + "test_dir_123" + File.separator
+            + "anotherDir2" );
+        FileUtils.mkdir( testDir + File.separator + "directoryTest" + File.separator + "test-dir-123" + File.separator
+            + "anotherDir3" );
+
+        this.createFile( new File( testDir + File.separator + "directoryTest" + File.separator + "testDir123"
+            + File.separator + "anotherDir1" + File.separator + "file1.dat" ), 0 );
+        this.createFile( new File( testDir + File.separator + "directoryTest" + File.separator + "test_dir_123"
+            + File.separator + "anotherDir2" + File.separator + "file1.dat" ), 0 );
+        this.createFile( new File( testDir + File.separator + "directoryTest" + File.separator + "test-dir-123"
+            + File.separator + "anotherDir3" + File.separator + "file1.dat" ), 0 );
+
+        String[] excludedPaths = {
+            "directoryTest" + File.separator + "testDir123" + File.separator + "anotherDir1" + File.separator
+                + "file1.dat",
+            "directoryTest" + File.separator + "test_dir_123" + File.separator + "anotherDir2" + File.separator
+                + "file1.dat",
+            "directoryTest" + File.separator + "test-dir-123" + File.separator + "anotherDir3" + File.separator
+                + "file1.dat"
+        };
+
+        String[] includedPaths = {
+            "directoryTest" + File.separator + "testDir123" + File.separator + "file1.dat",
+            "directoryTest" + File.separator + "test_dir_123" + File.separator + "file1.dat",
+            "directoryTest" + File.separator + "test-dir-123" + File.separator + "file1.dat"
+        };
+
+        final Set<String> scannedDirSet = new HashSet<String>();
+
+        DirectoryScanner ds = new DirectoryScanner()
+        {
+            @Override
+            protected void scandir( File dir, String vpath, boolean fast )
+            {
+                scannedDirSet.add( dir.getName() );
+                super.scandir( dir, vpath, fast );
+            }
+
+        };
+
+        // one '*' matches only ONE directory level
+        String[] includes = { "directoryTest" + File.separator + "*" + File.separator + "file1.dat" };
+        ds.setIncludes( includes );
+        ds.setBasedir( new File( testDir ) );
+        ds.scan();
+
+        assertInclusionsAndExclusions( ds.getIncludedFiles(), excludedPaths, includedPaths );
+
+        Set<String> expectedScannedDirSet =
+            new HashSet<String>( Arrays.asList( "io", "directoryTest", "testDir123", "test_dir_123", "test-dir-123" ) );
+
+        assertEquals( expectedScannedDirSet, scannedDirSet );
+    }
+
     @Test
     public void testIsSymbolicLink()
         throws IOException


=====================================
src/test/java/org/codehaus/plexus/util/MatchPatternTest.java
=====================================
@@ -16,6 +16,7 @@
  * limitations under the License.
  */
 
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import org.junit.Test;
@@ -32,4 +33,23 @@ public void testMatchPath()
         MatchPattern mp = MatchPattern.fromString( "ABC*" );
         assertTrue( mp.matchPath( "ABCD", true ) );
     }
+
+    /**
+     * @see <a href="https://github.com/codehaus-plexus/plexus-utils/issues/63">Issue #63</a>
+     */
+    @Test
+    public void testMatchPatternStart()
+    {
+        MatchPattern mp = MatchPattern.fromString( "ABC*" );
+
+        assertTrue( mp.matchPatternStart( "ABCD", true ) );
+        assertFalse( mp.matchPatternStart( "AbCD", true ) );
+
+        assertTrue( mp.matchPatternStart( "ABCD", false ) );
+        assertTrue( mp.matchPatternStart( "AbCD", false ) );
+
+        assertFalse( mp.matchPatternStart( "XXXX", true ) );
+        assertFalse( mp.matchPatternStart( "XXXX", false ) );
+    }
+
 }


=====================================
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 );


=====================================
src/test/java/org/codehaus/plexus/util/xml/Xpp3DomPerfTest.java
=====================================
@@ -0,0 +1,80 @@
+package org.codehaus.plexus.util.xml;
+
+/*
+ * Copyright The Codehaus Foundation.
+ *
+ * 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 java.io.IOException;
+import java.io.StringReader;
+import java.util.concurrent.TimeUnit;
+
+import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Level;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+import org.openjdk.jmh.runner.Runner;
+import org.openjdk.jmh.runner.RunnerException;
+import org.openjdk.jmh.runner.options.Options;
+import org.openjdk.jmh.runner.options.OptionsBuilder;
+import org.openjdk.jmh.runner.options.TimeValue;
+
+ at BenchmarkMode(Mode.Throughput)
+ at OutputTimeUnit(TimeUnit.MILLISECONDS)
+ at Warmup(iterations = 3, time = 3, timeUnit = TimeUnit.SECONDS)
+public class Xpp3DomPerfTest
+{
+    @State(Scope.Benchmark)
+    static public class AdditionState {
+        Xpp3Dom dom1;
+        Xpp3Dom dom2;
+
+        @Setup(Level.Iteration)
+        public void setUp() throws IOException, XmlPullParserException {
+            String testDom = "<configuration><items thing='blah'><item>one</item><item>two</item></items></configuration>";
+            dom1 = Xpp3DomBuilder.build( new StringReader( testDom ) );
+            dom2 = new Xpp3Dom( dom1 );
+        }
+    }
+
+
+    @Benchmark
+    public Xpp3Dom benchmarkClone(AdditionState state)
+    {
+        return new Xpp3Dom( state.dom1 );
+    }
+
+    @Benchmark
+    public void benchmarkMerge(AdditionState state)
+    {
+        Xpp3Dom.mergeXpp3Dom( state.dom1, state.dom2 );
+    }
+
+    public static void main( String... args )
+        throws RunnerException
+    {
+        Options opts = new OptionsBuilder()
+                .measurementIterations( 3 )
+                .measurementTime( TimeValue.milliseconds( 3000 ) )
+                .forks( 1 )
+                .build();
+        new Runner( opts ).run();
+    }
+}


=====================================
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 = "<configuration><items><item>one</item><item>two</item></items></configuration>";
-        Xpp3Dom parentConfig = Xpp3DomBuilder.build( new StringReader( parentConfigStr ) );
+        Xpp3Dom parentConfig =
+            Xpp3DomBuilder.build( new StringReader( parentConfigStr ), new FixedInputLocationBuilder( "parent" ) );
 
         String childConfigStr = "<configuration><items><item>three</item></items></configuration>";
-        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 = "<configuration><items><item>one</item><item>two</item></items></configuration>";
-        Xpp3Dom parentConfig = Xpp3DomBuilder.build( new StringReader( parentConfigStr ) );
+        Xpp3Dom parentConfig =
+            Xpp3DomBuilder.build( new StringReader( parentConfigStr ), new FixedInputLocationBuilder( "parent" ) );
 
         String childConfigStr =
             "<configuration><items combine.children=\"append\"><item>three</item></items></configuration>";
-        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;
+        }
+    }
 }


=====================================
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 = "<props>" + "<property combine.id='RHS-ONLY'><name>RHS-ONLY</name><value>RHS</value></property>"
             + "<property combine.id='TOOVERWRITE'><name>TOOVERWRITE</name><value>RHS</value></property>" + "</props>";
 
-        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;
+        }
     }
 }


=====================================
src/test/java/org/codehaus/plexus/util/xml/pull/MXParserTest.java
=====================================
@@ -17,7 +17,10 @@
  */
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
+import java.io.EOFException;
 import java.io.IOException;
 import java.io.StringReader;
 
@@ -156,6 +159,133 @@ public void testUnicodeEntities()
         assertEquals( XmlPullParser.END_TAG, parser.nextToken() );
     }
 
+    @Test
+    public void testInvalidCharacterReferenceHexa()
+        throws Exception
+    {
+        MXParser parser = new MXParser();
+        String input = "<root>&#x110000;</root>";
+        parser.setInput( new StringReader( input ) );
+
+        try
+        {
+            assertEquals( XmlPullParser.START_TAG, parser.nextToken() );
+            assertEquals( XmlPullParser.ENTITY_REF, parser.nextToken() );
+            fail( "Should fail since &#x110000; is an illegal character reference" );
+        }
+        catch ( XmlPullParserException e )
+        {
+            assertTrue( e.getMessage().contains( "character reference (with hex value 110000) is invalid" ) );
+        }
+    }
+
+    @Test
+    public void testValidCharacterReferenceHexa()
+        throws Exception
+    {
+        MXParser parser = new MXParser();
+        String input = "<root>&#x9;&#xA;&#xD;&#x20;&#x200;&#xD7FF;&#xE000;&#xFFA2;&#xFFFD;&#x10000;&#x10FFFD;&#x10FFFF;</root>";
+        parser.setInput( new StringReader( input ) );
+
+        try
+        {
+            assertEquals( XmlPullParser.START_TAG, parser.nextToken() );
+            assertEquals( XmlPullParser.ENTITY_REF, parser.nextToken() );
+            assertEquals( 0x9, parser.getText().codePointAt( 0 ) );
+            assertEquals( XmlPullParser.ENTITY_REF, parser.nextToken() );
+            assertEquals( 0xA, parser.getText().codePointAt( 0 ) );
+            assertEquals( XmlPullParser.ENTITY_REF, parser.nextToken() );
+            assertEquals( 0xD, parser.getText().codePointAt( 0 ) );
+            assertEquals( XmlPullParser.ENTITY_REF, parser.nextToken() );
+            assertEquals( 0x20, parser.getText().codePointAt( 0 ) );
+            assertEquals( XmlPullParser.ENTITY_REF, parser.nextToken() );
+            assertEquals( 0x200, parser.getText().codePointAt( 0 ) );
+            assertEquals( XmlPullParser.ENTITY_REF, parser.nextToken() );
+            assertEquals( 0xD7FF, parser.getText().codePointAt( 0 ) );
+            assertEquals( XmlPullParser.ENTITY_REF, parser.nextToken() );
+            assertEquals( 0xE000, parser.getText().codePointAt( 0 ) );
+            assertEquals( XmlPullParser.ENTITY_REF, parser.nextToken() );
+            assertEquals( 0xFFA2, parser.getText().codePointAt( 0 ) );
+            assertEquals( XmlPullParser.ENTITY_REF, parser.nextToken() );
+            assertEquals( 0xFFFD, parser.getText().codePointAt( 0 ) );
+            assertEquals( XmlPullParser.ENTITY_REF, parser.nextToken() );
+            assertEquals( 0x10000, parser.getText().codePointAt( 0 ) );
+            assertEquals( XmlPullParser.ENTITY_REF, parser.nextToken() );
+            assertEquals( 0x10FFFD, parser.getText().codePointAt( 0 ) );
+            assertEquals( XmlPullParser.ENTITY_REF, parser.nextToken() );
+            assertEquals( 0x10FFFF, parser.getText().codePointAt( 0 ) );
+            assertEquals( XmlPullParser.END_TAG, parser.nextToken() );
+        }
+        catch ( XmlPullParserException e )
+        {
+            fail( "Should success since the input represents all legal character references" );
+        }
+    }
+
+    @Test
+    public void testInvalidCharacterReferenceDecimal()
+        throws Exception
+    {
+        MXParser parser = new MXParser();
+        String input = "<root>�</root>";
+        parser.setInput( new StringReader( input ) );
+
+        try
+        {
+            assertEquals( XmlPullParser.START_TAG, parser.nextToken() );
+            assertEquals( XmlPullParser.ENTITY_REF, parser.nextToken() );
+            fail( "Should fail since � is an illegal character reference" );
+        }
+        catch ( XmlPullParserException e )
+        {
+            assertTrue( e.getMessage().contains( "character reference (with decimal value 1114112) is invalid" ) );
+        }
+    }
+
+    @Test
+    public void testValidCharacterReferenceDecimal()
+        throws Exception
+    {
+        MXParser parser = new MXParser();
+        String input =
+            "<root>	

 Ȁ퟿ᄁ�𐀀􏿽􏿿</root>";
+        parser.setInput( new StringReader( input ) );
+
+        try
+        {
+            assertEquals( XmlPullParser.START_TAG, parser.nextToken() );
+            assertEquals( XmlPullParser.ENTITY_REF, parser.nextToken() );
+            assertEquals( 9, parser.getText().codePointAt( 0 ) );
+            assertEquals( XmlPullParser.ENTITY_REF, parser.nextToken() );
+            assertEquals( 10, parser.getText().codePointAt( 0 ) );
+            assertEquals( XmlPullParser.ENTITY_REF, parser.nextToken() );
+            assertEquals( 13, parser.getText().codePointAt( 0 ) );
+            assertEquals( XmlPullParser.ENTITY_REF, parser.nextToken() );
+            assertEquals( 32, parser.getText().codePointAt( 0 ) );
+            assertEquals( XmlPullParser.ENTITY_REF, parser.nextToken() );
+            assertEquals( 512, parser.getText().codePointAt( 0 ) );
+            assertEquals( XmlPullParser.ENTITY_REF, parser.nextToken() );
+            assertEquals( 55295, parser.getText().codePointAt( 0 ) );
+            assertEquals( XmlPullParser.ENTITY_REF, parser.nextToken() );
+            assertEquals( 57344, parser.getText().codePointAt( 0 ) );
+            assertEquals( XmlPullParser.ENTITY_REF, parser.nextToken() );
+            assertEquals( 65442, parser.getText().codePointAt( 0 ) );
+            assertEquals( XmlPullParser.ENTITY_REF, parser.nextToken() );
+            assertEquals( 65533, parser.getText().codePointAt( 0 ) );
+            assertEquals( XmlPullParser.ENTITY_REF, parser.nextToken() );
+            assertEquals( 65536, parser.getText().codePointAt( 0 ) );
+            assertEquals( XmlPullParser.ENTITY_REF, parser.nextToken() );
+            assertEquals( 1114109, parser.getText().codePointAt( 0 ) );
+            assertEquals( XmlPullParser.ENTITY_REF, parser.nextToken() );
+            assertEquals( 1114111, parser.getText().codePointAt( 0 ) );
+            assertEquals( XmlPullParser.END_TAG, parser.nextToken() );
+        }
+        catch ( XmlPullParserException e )
+        {
+            fail( "Should success since the input represents all legal character references" );
+        }
+    }
+
     @Test
     public void testProcessingInstruction()
         throws Exception
@@ -171,6 +301,31 @@ public void testProcessingInstruction()
         assertEquals( XmlPullParser.END_TAG, parser.nextToken() );
     }
 
+    @Test
+    public void testProcessingInstructionsContainingXml()
+        throws Exception
+    {
+        StringBuffer sb = new StringBuffer();
+
+        sb.append( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" );
+        sb.append( "<project>\n" );
+        sb.append( " <?pi\n" );
+        sb.append( "   <tag>\n" );
+        sb.append( "   </tag>\n" );
+        sb.append( " ?>\n" );
+        sb.append( "</project>" );
+
+        MXParser parser = new MXParser();
+        parser.setInput( new StringReader( sb.toString() ) );
+
+        assertEquals( XmlPullParser.PROCESSING_INSTRUCTION, parser.nextToken() );
+        assertEquals( XmlPullParser.START_TAG, parser.nextToken() );
+        assertEquals( XmlPullParser.TEXT, parser.nextToken() ); // whitespace
+        assertEquals( XmlPullParser.PROCESSING_INSTRUCTION, parser.nextToken() );
+        assertEquals( XmlPullParser.TEXT, parser.nextToken() ); // whitespace
+        assertEquals( XmlPullParser.END_TAG, parser.nextToken() );
+    }
+
     @Test
     public void testSubsequentProcessingInstructionShort()
         throws Exception
@@ -235,4 +390,243 @@ public void testSubsequentProcessingInstructionMoreThan8k()
         assertEquals( XmlPullParser.PROCESSING_INSTRUCTION, parser.nextToken() );
         assertEquals( XmlPullParser.END_TAG, parser.nextToken() );
     }
+
+    public void testMalformedProcessingInstructionAfterTag()
+        throws Exception
+    {
+        MXParser parser = new MXParser();
+
+        String input = "<project /><?>";
+
+        parser.setInput( new StringReader( input ) );
+
+        try
+        {
+            assertEquals( XmlPullParser.START_TAG, parser.next() );
+
+            assertEquals( XmlPullParser.END_TAG, parser.next() );
+
+            assertEquals( XmlPullParser.PROCESSING_INSTRUCTION, parser.next() );
+
+            fail( "Should fail since it has an invalid Processing Instruction" );
+        }
+        catch ( XmlPullParserException ex )
+        {
+            assertTrue( ex.getMessage().contains( "processing instruction PITarget name not found" ) );
+        }
+    }
+
+    public void testMalformedProcessingInstructionBeforeTag()
+        throws Exception
+    {
+        MXParser parser = new MXParser();
+
+        String input = "<?><project />";
+
+        parser.setInput( new StringReader( input ) );
+
+        try
+        {
+            assertEquals( XmlPullParser.PROCESSING_INSTRUCTION, parser.next() );
+
+            assertEquals( XmlPullParser.START_TAG, parser.next() );
+
+            assertEquals( XmlPullParser.END_TAG, parser.next() );
+
+            fail( "Should fail since it has invalid PI" );
+        }
+        catch ( XmlPullParserException ex )
+        {
+            assertTrue( ex.getMessage().contains( "processing instruction PITarget name not found" ) );
+        }
+    }
+
+    public void testMalformedProcessingInstructionSpaceBeforeName()
+        throws Exception
+    {
+        MXParser parser = new MXParser();
+
+        StringBuilder sb = new StringBuilder();
+        sb.append( "<? shouldhavenospace>" );
+        sb.append( "<project />" );
+
+        parser.setInput( new StringReader( sb.toString() ) );
+
+        try
+        {
+            assertEquals( XmlPullParser.PROCESSING_INSTRUCTION, parser.next() );
+
+            assertEquals( XmlPullParser.START_TAG, parser.next() );
+
+            assertEquals( XmlPullParser.END_TAG, parser.next() );
+
+            fail( "Should fail since it has invalid PI" );
+        }
+        catch ( XmlPullParserException ex )
+        {
+            assertTrue( ex.getMessage().contains( "processing instruction PITarget must be exactly after <? and not white space character" ) );
+        }
+    }
+
+    public void testMalformedProcessingInstructionNoClosingQuestionMark()
+        throws Exception
+    {
+        MXParser parser = new MXParser();
+
+        StringBuilder sb = new StringBuilder();
+        sb.append( "<?shouldhavenospace>" );
+        sb.append( "<project />" );
+
+        parser.setInput( new StringReader( sb.toString() ) );
+
+        try
+        {
+            assertEquals( XmlPullParser.PROCESSING_INSTRUCTION, parser.next() );
+
+            assertEquals( XmlPullParser.START_TAG, parser.next() );
+
+            assertEquals( XmlPullParser.END_TAG, parser.next() );
+
+            fail( "Should fail since it has invalid PI" );
+        }
+        catch ( XmlPullParserException ex )
+        {
+            assertTrue( ex.getMessage().contains( "processing instruction started on line 1 and column 2 was not closed" ) );
+        }
+    }
+
+    public void testSubsequentMalformedProcessingInstructionNoClosingQuestionMark()
+        throws Exception
+    {
+        MXParser parser = new MXParser();
+
+        StringBuilder sb = new StringBuilder();
+        sb.append( "<project />" );
+        sb.append( "<?shouldhavenospace>" );
+
+        parser.setInput( new StringReader( sb.toString() ) );
+
+        try
+        {
+            assertEquals( XmlPullParser.START_TAG, parser.next() );
+
+            assertEquals( XmlPullParser.END_TAG, parser.next() );
+
+            assertEquals( XmlPullParser.PROCESSING_INSTRUCTION, parser.next() );
+
+            fail( "Should fail since it has invalid PI" );
+        }
+        catch ( XmlPullParserException ex )
+        {
+            assertTrue( ex.getMessage().contains( "processing instruction started on line 1 and column 13 was not closed" ) );
+        }
+    }
+
+    public void testMalformedXMLRootElement()
+        throws Exception
+    {
+        String input = "<Y";
+
+        MXParser parser = new MXParser();
+        parser.setInput( new StringReader( input ) );
+
+        try
+        {
+            assertEquals( XmlPullParser.START_TAG, parser.nextToken() );
+
+            fail( "Should throw EOFException" );
+        }
+        catch ( EOFException e )
+        {
+            assertTrue( e.getMessage().contains( "no more data available - expected the opening tag <Y...>" ) );
+        }
+    }
+
+    public void testMalformedXMLRootElement2()
+        throws Exception
+    {
+        String input = "<hello";
+
+        MXParser parser = new MXParser();
+        parser.setInput( new StringReader( input ) );
+
+        try
+        {
+            assertEquals( XmlPullParser.START_TAG, parser.nextToken() );
+
+            fail( "Should throw EOFException" );
+        }
+        catch ( EOFException e )
+        {
+            assertTrue( e.getMessage().contains( "no more data available - expected the opening tag <hello...>" ) );
+        }
+    }
+
+    public void testMalformedXMLRootElement3()
+        throws Exception
+    {
+        String input = "<hello><how";
+
+        MXParser parser = new MXParser();
+        parser.setInput( new StringReader( input ) );
+
+        try
+        {
+            assertEquals( XmlPullParser.START_TAG, parser.nextToken() );
+            assertEquals( XmlPullParser.START_TAG, parser.nextToken() );
+
+            fail( "Should throw EOFException" );
+        }
+        catch ( EOFException e )
+        {
+            assertTrue( e.getMessage().contains( "no more data available - expected the opening tag <how...>" ) );
+        }
+    }
+
+    public void testMalformedXMLRootElement4()
+        throws Exception
+    {
+        String input = "<hello>some text<how";
+
+        MXParser parser = new MXParser();
+        parser.setInput( new StringReader( input ) );
+
+        try
+        {
+            assertEquals( XmlPullParser.START_TAG, parser.nextToken() );
+            assertEquals( XmlPullParser.TEXT, parser.nextToken() );
+            assertEquals( "some text", parser.getText() );
+            assertEquals( XmlPullParser.START_TAG, parser.nextToken() );
+
+            fail( "Should throw EOFException" );
+        }
+        catch ( EOFException e )
+        {
+            assertTrue( e.getMessage().contains( "no more data available - expected the opening tag <how...>" ) );
+        }
+    }
+
+    public void testMalformedXMLRootElement5()
+        throws Exception
+    {
+        String input = "<hello>some text</hello";
+
+        MXParser parser = new MXParser();
+        parser.setInput( new StringReader( input ) );
+
+        try
+        {
+            assertEquals( XmlPullParser.START_TAG, parser.nextToken() );
+            assertEquals( XmlPullParser.TEXT, parser.nextToken() );
+            assertEquals( "some text", parser.getText() );
+            assertEquals( XmlPullParser.END_TAG, parser.nextToken() );
+
+            fail( "Should throw EOFException" );
+        }
+        catch ( EOFException e )
+        {
+            assertTrue( e.getMessage().contains( "no more data available - expected end tag </hello> to close start tag <hello>" ) );
+        }
+    }
+
 }



View it on GitLab: https://salsa.debian.org/java-team/plexus-utils2/compare/871a25b06338aa9dd2c3ccbc0f7e4151c3e07cf0...ea133d5bc1173d6a86daf8f9a0cd165612034114

-- 
View it on GitLab: https://salsa.debian.org/java-team/plexus-utils2/compare/871a25b06338aa9dd2c3ccbc0f7e4151c3e07cf0...ea133d5bc1173d6a86daf8f9a0cd165612034114
You're receiving this email because of your account on salsa.debian.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-java-commits/attachments/20190712/e3ffea29/attachment.html>


More information about the pkg-java-commits mailing list