maven-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From rfscho...@apache.org
Subject [maven] branch MNG-6656 updated: [MNG-6656] Support LexicalHandler
Date Fri, 11 Oct 2019 13:21:10 GMT
This is an automated email from the ASF dual-hosted git repository.

rfscholte pushed a commit to branch MNG-6656
in repository https://gitbox.apache.org/repos/asf/maven.git


The following commit(s) were added to refs/heads/MNG-6656 by this push:
     new c52f383  [MNG-6656] Support LexicalHandler
c52f383 is described below

commit c52f383c4b4d06adb8800d5cd88227bbbec19a2f
Author: rfscholte <rfscholte@apache.org>
AuthorDate: Fri Oct 11 15:20:57 2019 +0200

    [MNG-6656] Support LexicalHandler
---
 .../DefaultRepositorySystemSessionFactory.java     |   5 +-
 .../maven/xml/filter/ConsumerPomXMLFilter.java     |  73 ------
 .../apache/maven/xml/filter/FastForwardFilter.java | 128 ----------
 .../maven/xml/filter/RelativePathXMLFilter.java    |  98 --------
 .../internal/DefaultBuildPomXMLFilterFactory.java  |   8 +-
 .../DefaultConsumerPomXMLFilterFactory.java        |   2 +-
 .../maven/xml/filter/AbstractXMLFilterTests.java   |  80 ------
 .../maven/xml/filter/ConsumerPomXMLFilterTest.java |  64 -----
 .../maven/xml/filter/ModulesXMLFilterTest.java     |  62 -----
 .../xml/filter/RelativePathXMLFilterTest.java      |  61 -----
 .../maven/model/building/DefaultModelBuilder.java  |   2 +-
 .../model/building/DefaultModelCacheManager.java   |   2 +-
 .../maven/model/building/ModelCacheManager.java    |   2 +-
 .../main/java/org/apache/maven/xml/Factories.java  | 118 +++++++++
 .../apache/maven/xml/filter/ModulesXMLFilter.java  | 102 --------
 .../org/apache/maven/xml/{ => sax}/SAXEvent.java   |   2 +-
 .../maven/xml/{ => sax}/SAXEventFactory.java       |  63 +++--
 .../apache/maven/xml/{ => sax}/SAXEventUtils.java  |   2 +-
 .../xml/sax/filter/AbstractEventXMLFilter.java     | 272 ++++++++++++++++++++
 .../maven/xml/sax/filter/AbstractSAXFilter.java    | 130 ++++++++++
 .../xml/{ => sax}/filter/BuildPomXMLFilter.java    |   8 +-
 .../{ => sax}/filter/BuildPomXMLFilterFactory.java |  31 ++-
 .../xml/{ => sax}/filter/CiFriendlyXMLFilter.java  |   5 +-
 .../xml/{ => sax}/filter/ConsumerPomXMLFilter.java |   8 +-
 .../filter/ConsumerPomXMLFilterFactory.java        |  11 +-
 .../maven/xml/{ => sax}/filter/DependencyKey.java  |   2 +-
 .../xml/{ => sax}/filter/FastForwardFilter.java    |   8 +-
 .../maven/xml/sax}/filter/ModulesXMLFilter.java    |  19 +-
 .../xml/{ => sax}/filter/ParentXMLFilter.java      | 146 ++---------
 .../filter/ReactorDependencyXMLFilter.java         |  22 +-
 .../{ => sax}/filter/RelativePathXMLFilter.java    |  75 +++---
 .../xml/{ => sax}/filter/RelativeProject.java      |   2 +-
 .../maven/xml/filter/ConsumerPomXMLFilterTest.java | 168 -------------
 .../maven/xml/sax/LexicalHandlerVerifier.java      | 277 +++++++++++++++++++++
 .../apache/maven/xml/sax/SAXEventUtilsTest.java    |  31 ++-
 .../{ => sax}/filter/AbstractXMLFilterTests.java   |  50 +++-
 .../xml/sax/filter/ConsumerPomXMLFilterTest.java   | 252 +++++++++++++++++++
 .../xml/{ => sax}/filter/ModulesXMLFilterTest.java |  40 +--
 .../xml/{ => sax}/filter/ParentXMLFilterTest.java  |  34 +--
 .../filter/ReactorDependencyXMLFilterTest.java     |  36 ++-
 .../filter/RelativePathXMLFilterTest.java          |   9 +-
 41 files changed, 1379 insertions(+), 1131 deletions(-)

diff --git a/maven-core/src/main/java/org/apache/maven/internal/aether/DefaultRepositorySystemSessionFactory.java b/maven-core/src/main/java/org/apache/maven/internal/aether/DefaultRepositorySystemSessionFactory.java
index 107c98b..3eaad6f 100644
--- a/maven-core/src/main/java/org/apache/maven/internal/aether/DefaultRepositorySystemSessionFactory.java
+++ b/maven-core/src/main/java/org/apache/maven/internal/aether/DefaultRepositorySystemSessionFactory.java
@@ -40,6 +40,7 @@ import javax.inject.Inject;
 import javax.inject.Named;
 import javax.inject.Provider;
 import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.TransformerConfigurationException;
 import javax.xml.transform.TransformerFactory;
 import javax.xml.transform.sax.SAXSource;
 import javax.xml.transform.stream.StreamResult;
@@ -58,7 +59,7 @@ import org.apache.maven.settings.crypto.DefaultSettingsDecryptionRequest;
 import org.apache.maven.settings.crypto.SettingsDecrypter;
 import org.apache.maven.settings.crypto.SettingsDecryptionResult;
 import org.apache.maven.xml.Factories;
-import org.apache.maven.xml.filter.ConsumerPomXMLFilterFactory;
+import org.apache.maven.xml.sax.filter.ConsumerPomXMLFilterFactory;
 import org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration;
 import org.codehaus.plexus.logging.Logger;
 import org.codehaus.plexus.util.xml.Xpp3Dom;
@@ -302,7 +303,7 @@ public class DefaultRepositorySystemSessionFactory
                                     new SAXSource( consumerPomXMLFilterFactory.get().get( file.toPath() ),
                                                    new InputSource( new FileReader( file ) ) );
                             }
-                            catch ( SAXException | ParserConfigurationException e )
+                            catch ( SAXException | ParserConfigurationException | TransformerConfigurationException e )
                             {   
                                 throw new TransformException( "Failed to create a consumerPomXMLFilter", e );
                             }
diff --git a/maven-core/src/main/java/org/apache/maven/xml/filter/ConsumerPomXMLFilter.java b/maven-core/src/main/java/org/apache/maven/xml/filter/ConsumerPomXMLFilter.java
deleted file mode 100644
index 8709b9c..0000000
--- a/maven-core/src/main/java/org/apache/maven/xml/filter/ConsumerPomXMLFilter.java
+++ /dev/null
@@ -1,73 +0,0 @@
-package org.apache.maven.xml.filter;
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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 javax.xml.parsers.ParserConfigurationException;
-import javax.xml.parsers.SAXParserFactory;
-
-import org.xml.sax.SAXException;
-import org.xml.sax.XMLFilter;
-import org.xml.sax.XMLReader;
-
-import org.xml.sax.helpers.XMLFilterImpl;
-
-/**
- * XML Filter to transform pom.xml to consumer pom.
- * This often means stripping of build-specific information.
- * 
- * This filter is used at 2 locations:
- * - {@link org.apache.maven.internal.aether.DefaultRepositorySystemSessionFactory} when publishing pom files.
- * - TODO ???Class when a reactor module is used as dependency. This ensures consistency of dependency handling
- * 
- * @author Robert Scholte
- * @since 3.7.0
- */
-public class ConsumerPomXMLFilter extends XMLFilterImpl
-{
-    private final XMLFilter rootFilter;
-
-    public ConsumerPomXMLFilter() throws SAXException, ParserConfigurationException
-    {
-        this( SAXParserFactory.newInstance().newSAXParser().getXMLReader() );
-    }
-
-    public ConsumerPomXMLFilter( XMLReader parent )
-    {
-        rootFilter = new BuildPomXMLFilter( parent );
-        
-        // Ensure that xs:any elements aren't touched by next filters
-        XMLFilter filter = new FastForwardFilter( rootFilter );
-        
-        // Strip modules
-        filter = new ModulesXMLFilter( filter );
-        // Adjust relativePath
-        filter = new RelativePathXMLFilter( filter );
-        
-        // maybe more to follow
-        
-        super.setParent( filter );
-    }
-    
-    @Override
-    public void setParent( XMLReader parent )
-    {
-        rootFilter.setParent( parent );
-    }
-}
diff --git a/maven-core/src/main/java/org/apache/maven/xml/filter/FastForwardFilter.java b/maven-core/src/main/java/org/apache/maven/xml/filter/FastForwardFilter.java
deleted file mode 100644
index df68167..0000000
--- a/maven-core/src/main/java/org/apache/maven/xml/filter/FastForwardFilter.java
+++ /dev/null
@@ -1,128 +0,0 @@
-package org.apache.maven.xml.filter;
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.util.ArrayDeque;
-import java.util.Deque;
-
-import org.xml.sax.Attributes;
-import org.xml.sax.ContentHandler;
-import org.xml.sax.SAXException;
-import org.xml.sax.XMLFilter;
-import org.xml.sax.XMLReader;
-import org.xml.sax.helpers.XMLFilterImpl;
-
-/**
- * This filter will skip all following filters and write directly to the output.
- * Should be used in case of a DOM that should not be effected by other filters, even though the elements match 
- * 
- * @author Robert Scholte
- * @since 4.0.0
- */
-class FastForwardFilter extends XMLFilterImpl
-{
-    /**
-     * DOM elements of pom
-     * 
-     * <ul>
-     *  <li>execution.configuration</li>
-     *  <li>plugin.configuration</li>
-     *  <li>plugin.goals</li>
-     *  <li>profile.reports</li>
-     *  <li>project.reports</li>
-     *  <li>reportSet.configuration</li>
-     * <ul>
-     */
-    private final Deque<String> state = new ArrayDeque<>();
-    
-    private int domDepth = 0;
-    
-    private ContentHandler originalHandler;
-    
-    FastForwardFilter()
-    {
-        super();
-    }
-
-    FastForwardFilter( XMLReader parent )
-    {
-        super( parent );
-    }
-
-    @Override
-    public void startElement( String uri, String localName, String qName, Attributes atts )
-        throws SAXException
-    {
-        super.startElement( uri, localName, qName, atts );
-        if ( domDepth > 0 )
-        {
-            domDepth++;
-        }
-        else
-        {
-            final String key = state.peek() + '.' + localName;
-            switch ( key )
-            {
-                case "execution.configuration":
-                case "plugin.configuration":
-                case "plugin.goals":
-                case "profile.reports":
-                case "project.reports":
-                case "reportSet.configuration":
-                    domDepth++;
-
-                    originalHandler = getContentHandler();
-
-                    ContentHandler outputContentHandler = getContentHandler();
-                    while ( outputContentHandler instanceof XMLFilter )
-                    {
-                        outputContentHandler = ( (XMLFilter) outputContentHandler ).getContentHandler();
-                    }
-                    setContentHandler( outputContentHandler );
-                    break;
-                default:
-                    break;
-            }
-            state.push( localName );
-        }
-    }
-    
-    @Override
-    public void endElement( String uri, String localName, String qName )
-        throws SAXException
-    {
-        if ( domDepth > 0 )
-        {
-            domDepth--;
-            
-            if ( domDepth == 0 )
-            {
-                setContentHandler( originalHandler );
-            }
-        }
-        else
-        {
-            state.pop();
-        }
-        super.endElement( uri, localName, qName );
-    }
-    
-    
-}
\ No newline at end of file
diff --git a/maven-core/src/main/java/org/apache/maven/xml/filter/RelativePathXMLFilter.java b/maven-core/src/main/java/org/apache/maven/xml/filter/RelativePathXMLFilter.java
deleted file mode 100644
index 8548ddd..0000000
--- a/maven-core/src/main/java/org/apache/maven/xml/filter/RelativePathXMLFilter.java
+++ /dev/null
@@ -1,98 +0,0 @@
-package org.apache.maven.xml.filter;
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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 org.xml.sax.Attributes;
-import org.xml.sax.SAXException;
-import org.xml.sax.XMLReader;
-import org.xml.sax.helpers.XMLFilterImpl;
-
-/**
- * Remove content from relativePath
- * 
- * TODO fix indentation of closing parent tag
- * 
- * @author Robert Scholte
- * @since 3.7.0
- */
-class RelativePathXMLFilter
-    extends XMLFilterImpl
-{
-    /**
-     * Using 3 to also remove whitespace-block after closing tag
-     * -1 none
-     *  1 started
-     *  2 ended
-     */
-    int relativePathStatus = -1;
-    
-    RelativePathXMLFilter()
-    {
-        super();
-    }
-
-    RelativePathXMLFilter( XMLReader parent )
-    {
-        super( parent );
-    }
-
-    @Override
-    public void startElement( String uri, String localName, String qName, Attributes atts )
-        throws SAXException
-    {
-        if ( relativePathStatus == -1 && "relativePath".equals( localName ) )
-        {
-            relativePathStatus = 1;
-        }
-        else if ( relativePathStatus == 2 )
-        {
-            relativePathStatus = -1;
-        }
-
-        super.startElement( uri, localName, qName, atts );
-    }
-
-    @Override
-    public void endElement( String uri, String localName, String qName )
-        throws SAXException
-    {
-        if ( relativePathStatus == 1 && "relativePath".equals( localName ) )
-        {
-            relativePathStatus = 2;
-        }
-        else if ( relativePathStatus == 2 )
-        {
-            relativePathStatus = -1;
-        }
-        
-        super.endElement( uri, localName, qName );
-    }
-
-    @Override
-    public void characters( char[] ch, int start, int length )
-        throws SAXException
-    {
-        if ( relativePathStatus != 1 )
-        {
-            super.characters( ch, start, length );
-        }
-    }
-
-}
diff --git a/maven-core/src/main/java/org/apache/maven/xml/internal/DefaultBuildPomXMLFilterFactory.java b/maven-core/src/main/java/org/apache/maven/xml/internal/DefaultBuildPomXMLFilterFactory.java
index 2728103..d359eb6 100644
--- a/maven-core/src/main/java/org/apache/maven/xml/internal/DefaultBuildPomXMLFilterFactory.java
+++ b/maven-core/src/main/java/org/apache/maven/xml/internal/DefaultBuildPomXMLFilterFactory.java
@@ -31,9 +31,9 @@ import javax.inject.Singleton;
 import org.apache.maven.execution.MavenSession;
 import org.apache.maven.model.Model;
 import org.apache.maven.model.building.ModelCacheManager;
-import org.apache.maven.xml.filter.BuildPomXMLFilterFactory;
-import org.apache.maven.xml.filter.DependencyKey;
-import org.apache.maven.xml.filter.RelativeProject;
+import org.apache.maven.xml.sax.filter.BuildPomXMLFilterFactory;
+import org.apache.maven.xml.sax.filter.DependencyKey;
+import org.apache.maven.xml.sax.filter.RelativeProject;
 
 /**
  * 
@@ -87,7 +87,7 @@ public class DefaultBuildPomXMLFilterFactory extends BuildPomXMLFilterFactory
                             .orElse( null );
     }
 
-    private RelativeProject toRelativeProject( final Model m )
+    private static RelativeProject toRelativeProject( final Model m )
     {
         String groupId = m.getGroupId();
         if ( groupId == null && m.getParent() != null )
diff --git a/maven-core/src/main/java/org/apache/maven/xml/internal/DefaultConsumerPomXMLFilterFactory.java b/maven-core/src/main/java/org/apache/maven/xml/internal/DefaultConsumerPomXMLFilterFactory.java
index af05bc3..489019f 100644
--- a/maven-core/src/main/java/org/apache/maven/xml/internal/DefaultConsumerPomXMLFilterFactory.java
+++ b/maven-core/src/main/java/org/apache/maven/xml/internal/DefaultConsumerPomXMLFilterFactory.java
@@ -22,7 +22,7 @@ package org.apache.maven.xml.internal;
 import javax.inject.Named;
 import javax.inject.Singleton;
 
-import org.apache.maven.xml.filter.ConsumerPomXMLFilterFactory;
+import org.apache.maven.xml.sax.filter.ConsumerPomXMLFilterFactory;
 
 /**
  * 
diff --git a/maven-core/src/test/java/org/apache/maven/xml/filter/AbstractXMLFilterTests.java b/maven-core/src/test/java/org/apache/maven/xml/filter/AbstractXMLFilterTests.java
deleted file mode 100644
index 69fc33b..0000000
--- a/maven-core/src/test/java/org/apache/maven/xml/filter/AbstractXMLFilterTests.java
+++ /dev/null
@@ -1,80 +0,0 @@
-package org.apache.maven.xml.filter;
-
-import java.io.Reader;
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.StringReader;
-import java.io.StringWriter;
-import java.io.Writer;
-
-import javax.xml.transform.OutputKeys;
-import javax.xml.transform.Transformer;
-import javax.xml.transform.TransformerException;
-import javax.xml.transform.TransformerFactory;
-import javax.xml.transform.sax.SAXSource;
-import javax.xml.transform.stream.StreamResult;
-
-import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
-import org.xml.sax.XMLFilter;
-import org.xml.sax.XMLReader;
-import org.xml.sax.helpers.XMLReaderFactory;
-
-public class AbstractXMLFilterTests
-{
-
-    public AbstractXMLFilterTests()
-    {
-        super();
-    }
-
-    protected String transform( String input, XMLFilter filter )
-        throws TransformerException, SAXException
-    {
-        return transform( new StringReader( input ), filter );
-    }
-    
-    protected String transform( Reader input, XMLFilter filter )
-        throws TransformerException, SAXException
-    {
-        XMLReader reader = XMLReaderFactory.createXMLReader();
-
-        XMLFilter parent = filter;
-        while ( parent.getParent() instanceof XMLFilter )
-        {
-            parent = (XMLFilter) parent.getParent();
-        }
-        parent.setParent( reader );
-
-        Writer writer = new StringWriter();
-        StreamResult result = new StreamResult( writer );
-
-        TransformerFactory transformerFactory = TransformerFactory.newInstance();
-        Transformer transformer = transformerFactory.newTransformer();
-        transformer.setOutputProperty( OutputKeys.OMIT_XML_DECLARATION, "yes" );
-
-        SAXSource transformSource = new SAXSource( filter, new InputSource( input ) );
-
-        transformer.transform( transformSource, result );
-
-        return writer.toString();
-    }
-}
\ No newline at end of file
diff --git a/maven-core/src/test/java/org/apache/maven/xml/filter/ConsumerPomXMLFilterTest.java b/maven-core/src/test/java/org/apache/maven/xml/filter/ConsumerPomXMLFilterTest.java
deleted file mode 100644
index b3d44db..0000000
--- a/maven-core/src/test/java/org/apache/maven/xml/filter/ConsumerPomXMLFilterTest.java
+++ /dev/null
@@ -1,64 +0,0 @@
-package org.apache.maven.xml.filter;
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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 static org.xmlunit.assertj.XmlAssert.assertThat;
-
-import org.junit.Before;
-import org.junit.Test;
-
-public class ConsumerPomXMLFilterTest extends AbstractXMLFilterTests
-{
-    private ConsumerPomXMLFilter filter;
-    
-    @Before
-    public void setup() throws Exception {
-        filter = new ConsumerPomXMLFilter();
-    }
-    
-    @Test
-    public void testAllFilters() throws Exception {
-        String input = "<project>\n"
-                     + "  <parent>\n"
-                     + "    <groupId>GROUPID</groupId>\n"
-                     + "    <artifactId>PARENT</artifactId>\n"
-                     + "    <version>VERSION</version>\n"
-                     + "    <relativePath>../pom.xml</relativePath>\n"
-                     + "  </parent>\n"
-                     + "  <artifactId>PROJECT</artifactId>\n"
-                     + "  <modules>\n"
-                     + "    <module>ab</module>\n"
-                     + "    <module>../cd</module>\n"
-                     + "  </modules>\n"
-                     + "</project>";
-        String expected = "<project>\n"
-                        + "  <parent>\n"
-                        + "    <groupId>GROUPID</groupId>\n"
-                        + "    <artifactId>PARENT</artifactId>\n"
-                        + "    <version>VERSION</version>\n"
-                        + "    <relativePath/>\n"
-                        + "  </parent>\n"
-                        + "  <artifactId>PROJECT</artifactId>\n"
-                        + "</project>";
-        String actual = transform( input, filter );
-        assertThat( actual ).and( expected ).ignoreWhitespace().areIdentical();
-    }
-
-}
diff --git a/maven-core/src/test/java/org/apache/maven/xml/filter/ModulesXMLFilterTest.java b/maven-core/src/test/java/org/apache/maven/xml/filter/ModulesXMLFilterTest.java
deleted file mode 100644
index 353f174..0000000
--- a/maven-core/src/test/java/org/apache/maven/xml/filter/ModulesXMLFilterTest.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package org.apache.maven.xml.filter;
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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 static org.xmlunit.assertj.XmlAssert.assertThat;
-
-import org.junit.Before;
-import org.junit.Test;
-
-public class ModulesXMLFilterTest extends AbstractXMLFilterTests {
-
-	private ModulesXMLFilter filter;
-	
-	@Before
-	public void setup() {
-		filter = new ModulesXMLFilter();
-	}
-	
-	@Test
-	public void testEmptyModules() throws Exception {
-		String input = "<project><modules/></project>";
-        String expected = "<project/>";
-        String actual = transform( input, filter );
-        assertThat( actual ).and( expected ).areIdentical();
-	}
-
-	@Test
-	public void testSetOfModules() throws Exception {
-		String input = "<project><modules>"
-				+ "<module>ab</module>"
-				+ "<module>../cd</module>"
-				+ "</modules></project>";
-		String expected = "<project/>";
-		String actual = transform( input, filter );
-		assertThat( actual ).and( expected ).areIdentical();
-	}
-	
-	@Test
-    public void testNoModules() throws Exception {
-        String input = "<project><name>NAME</name></project>";
-        String expected = input;
-        String actual = transform( input, filter );
-        assertThat( actual ).and( expected ).areIdentical();
-    }
-}
diff --git a/maven-core/src/test/java/org/apache/maven/xml/filter/RelativePathXMLFilterTest.java b/maven-core/src/test/java/org/apache/maven/xml/filter/RelativePathXMLFilterTest.java
deleted file mode 100644
index 298197d..0000000
--- a/maven-core/src/test/java/org/apache/maven/xml/filter/RelativePathXMLFilterTest.java
+++ /dev/null
@@ -1,61 +0,0 @@
-package org.apache.maven.xml.filter;
-
-import static org.xmlunit.assertj.XmlAssert.assertThat;
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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 org.junit.Before;
-import org.junit.Test;
-
-public class RelativePathXMLFilterTest extends AbstractXMLFilterTests
-{
-    private RelativePathXMLFilter filter;
-    
-    @Before
-    public void setup() {
-        filter = new RelativePathXMLFilter();
-    }
-    
-    @Test
-    public void testRelativePath() throws Exception
-    {
-        String input = "<project>\n"
-                        + "  <parent>\n"
-                        + "    <groupId>GROUPID</groupId>\n"
-                        + "    <artifactId>PARENT</artifactId>\n"
-                        + "    <version>VERSION</version>\n"
-                        + "    <relativePath>../pom.xml</relativePath>\n"
-                        + "  </parent>\n"
-                        + "  <artifactId>PROJECT</artifactId>\n"
-                        + "</project>";
-           String expected = "<project>\n"
-                           + "  <parent>\n"
-                           + "    <groupId>GROUPID</groupId>\n"
-                           + "    <artifactId>PARENT</artifactId>\n"
-                           + "    <version>VERSION</version>\n"
-                           + "    <relativePath/>\n"
-                           + "  </parent>\n"
-                           + "  <artifactId>PROJECT</artifactId>\n"
-                           + "</project>";
-           String actual = transform( input, filter );
-           assertThat( actual ).and( expected ).areIdentical();
-    }
-
-}
diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java
index 5efb532..bc13d33 100644
--- a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java
+++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java
@@ -92,7 +92,7 @@ import org.apache.maven.model.resolution.WorkspaceModelResolver;
 import org.apache.maven.model.superpom.SuperPomProvider;
 import org.apache.maven.model.validation.ModelValidator;
 import org.apache.maven.xml.Factories;
-import org.apache.maven.xml.filter.BuildPomXMLFilterFactory;
+import org.apache.maven.xml.sax.filter.BuildPomXMLFilterFactory;
 import org.codehaus.plexus.interpolation.MapBasedValueSource;
 import org.codehaus.plexus.interpolation.StringSearchInterpolator;
 import org.eclipse.sisu.Nullable;
diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelCacheManager.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelCacheManager.java
index 96ca420..e205749 100644
--- a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelCacheManager.java
+++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelCacheManager.java
@@ -27,7 +27,7 @@ import javax.inject.Named;
 import javax.inject.Singleton;
 
 import org.apache.maven.model.Model;
-import org.apache.maven.xml.filter.DependencyKey;
+import org.apache.maven.xml.sax.filter.DependencyKey;
 
 /**
  * 
diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelCacheManager.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelCacheManager.java
index f28116f..f6e6515 100644
--- a/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelCacheManager.java
+++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelCacheManager.java
@@ -22,7 +22,7 @@ package org.apache.maven.model.building;
 import java.nio.file.Path;
 
 import org.apache.maven.model.Model;
-import org.apache.maven.xml.filter.DependencyKey;
+import org.apache.maven.xml.sax.filter.DependencyKey;
 
 /**
  * Registers models for usage later on
diff --git a/maven-xml/src/main/java/org/apache/maven/xml/Factories.java b/maven-xml/src/main/java/org/apache/maven/xml/Factories.java
new file mode 100644
index 0000000..eb2166a
--- /dev/null
+++ b/maven-xml/src/main/java/org/apache/maven/xml/Factories.java
@@ -0,0 +1,118 @@
+package org.apache.maven.xml;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 javax.xml.XMLConstants;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import javax.xml.transform.TransformerFactory;
+
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXNotRecognizedException;
+import org.xml.sax.SAXNotSupportedException;
+import org.xml.sax.XMLReader;
+
+/**
+ * Creates XML related factories with OWASP advices applied
+ * 
+ * @author Robert Scholte
+ * @since 3.7.0
+ */
+public final class Factories
+{
+    private Factories()
+    {
+    }
+    
+    /**
+     * 
+     * @return
+     * @see https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html#transformerfactory
+     */
+    public static TransformerFactory newTransformerFactory() 
+    {
+        TransformerFactory tf = TransformerFactory.newInstance();
+        tf.setAttribute( XMLConstants.ACCESS_EXTERNAL_DTD, "" );
+        tf.setAttribute( XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "" );
+
+        return tf;
+    }
+    
+    public static SAXParserFactory newSAXParserFactory()
+    {
+        SAXParserFactory spf = SAXParserFactory.newInstance();
+
+        try
+        {
+            // Xerces 1 - http://xerces.apache.org/xerces-j/features.html#external-general-entities
+            // Xerces 2 - http://xerces.apache.org/xerces2-j/features.html#external-general-entities
+   
+            // Using the SAXParserFactory's setFeature
+            spf.setFeature( "http://xml.org/sax/features/external-general-entities", false );
+            
+            // Xerces 2 only - http://xerces.apache.org/xerces-j/features.html#external-general-entities
+            spf.setFeature( "http://apache.org/xml/features/disallow-doctype-decl", true );
+        }
+        catch ( ParserConfigurationException e )
+        {
+            // Tried an unsupported feature.
+        }
+        catch ( SAXNotRecognizedException e )
+        {
+            // Tried an unknown feature.
+        }
+        catch ( SAXNotSupportedException e )
+        {
+            // Tried a feature known to the parser but unsupported.
+        }
+        return spf;
+    }
+    
+    public static SAXParser newSAXParser() throws ParserConfigurationException, SAXException
+    {
+        SAXParser saxParser = newSAXParserFactory().newSAXParser();
+        
+        return saxParser;
+    }
+    
+    public static XMLReader newXMLReader() throws SAXException, ParserConfigurationException
+    {
+        XMLReader reader = newSAXParser().getXMLReader();
+        
+        try
+        {
+            // Xerces 1 - http://xerces.apache.org/xerces-j/features.html#external-general-entities
+            // Xerces 2 - http://xerces.apache.org/xerces2-j/features.html#external-general-entities
+       
+            // Using the XMLReader's setFeature
+            reader.setFeature( "http://xml.org/sax/features/external-general-entities", false );
+        }
+        catch ( SAXNotRecognizedException e )
+        {
+            // Tried an unknown feature.
+        }
+        catch ( SAXNotSupportedException e )
+        {
+            // Tried a feature known to the parser but unsupported.
+        }
+        return reader;
+    }
+}
diff --git a/maven-xml/src/main/java/org/apache/maven/xml/filter/ModulesXMLFilter.java b/maven-xml/src/main/java/org/apache/maven/xml/filter/ModulesXMLFilter.java
deleted file mode 100644
index 9e7b6fd..0000000
--- a/maven-xml/src/main/java/org/apache/maven/xml/filter/ModulesXMLFilter.java
+++ /dev/null
@@ -1,102 +0,0 @@
-package org.apache.maven.xml.filter;
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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 org.xml.sax.Attributes;
-import org.xml.sax.SAXException;
-import org.xml.sax.XMLReader;
-import org.xml.sax.helpers.XMLFilterImpl;
-
-/**
- * Remove all modules, this is just buildtime information
- * 
- * @author Robert Scholte
- * @since 3.7.0
- */
-class ModulesXMLFilter
-    extends XMLFilterImpl
-{
-    /**
-     * Using 3 to also remove whitespace-block after closing tag
-     * -1 none
-     *  1 started
-     *  2 ended
-     */
-    int modulesStatus = -1;
-    
-    ModulesXMLFilter()
-    {
-        super();
-    }
-
-    ModulesXMLFilter( XMLReader parent )
-    {
-        super( parent );
-    }
-
-    @Override
-    public void startElement( String uri, String localName, String qName, Attributes atts )
-        throws SAXException
-    {
-        if ( modulesStatus == -1 && "modules".equals( localName ) )
-        {
-            modulesStatus = 1;
-        }
-        else if ( modulesStatus == 2 )
-        {
-            modulesStatus = -1;
-        }
-
-        if ( modulesStatus != 1 )
-        {
-            super.startElement( uri, localName, qName, atts );
-        }
-    }
-
-    @Override
-    public void endElement( String uri, String localName, String qName )
-        throws SAXException
-    {
-        if ( modulesStatus == 1 && "modules".equals( localName ) )
-        {
-            modulesStatus = 2;
-        }
-        else if ( modulesStatus == 2 )
-        {
-            modulesStatus = -1;
-        }
-        
-        if ( modulesStatus == -1 )
-        {
-            super.endElement( uri, localName, qName );
-        }
-    }
-
-    @Override
-    public void characters( char[] ch, int start, int length )
-        throws SAXException
-    {
-        if ( modulesStatus == -1 )
-        {
-            super.characters( ch, start, length );
-        }
-    }
-
-}
diff --git a/maven-xml/src/main/java/org/apache/maven/xml/SAXEvent.java b/maven-xml/src/main/java/org/apache/maven/xml/sax/SAXEvent.java
similarity index 96%
rename from maven-xml/src/main/java/org/apache/maven/xml/SAXEvent.java
rename to maven-xml/src/main/java/org/apache/maven/xml/sax/SAXEvent.java
index 11aad6d..8126957 100644
--- a/maven-xml/src/main/java/org/apache/maven/xml/SAXEvent.java
+++ b/maven-xml/src/main/java/org/apache/maven/xml/sax/SAXEvent.java
@@ -1,4 +1,4 @@
-package org.apache.maven.xml;
+package org.apache.maven.xml.sax;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
diff --git a/maven-xml/src/main/java/org/apache/maven/xml/SAXEventFactory.java b/maven-xml/src/main/java/org/apache/maven/xml/sax/SAXEventFactory.java
similarity index 65%
rename from maven-xml/src/main/java/org/apache/maven/xml/SAXEventFactory.java
rename to maven-xml/src/main/java/org/apache/maven/xml/sax/SAXEventFactory.java
index 6d525f6..84e13e5 100644
--- a/maven-xml/src/main/java/org/apache/maven/xml/SAXEventFactory.java
+++ b/maven-xml/src/main/java/org/apache/maven/xml/sax/SAXEventFactory.java
@@ -1,4 +1,4 @@
-package org.apache.maven.xml;
+package org.apache.maven.xml.sax;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -22,6 +22,7 @@ package org.apache.maven.xml;
 import org.xml.sax.Attributes;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.Locator;
+import org.xml.sax.ext.LexicalHandler;
 
 /**
  * Factory for SAXEvents
@@ -32,25 +33,19 @@ import org.xml.sax.Locator;
 public final class SAXEventFactory
 {
     private final ContentHandler contentHandler;
+    
+    private final LexicalHandler lexicalHandler;
 
-    protected SAXEventFactory( ContentHandler contentHandler )
+    protected SAXEventFactory( ContentHandler contentHandler, LexicalHandler lexicalHandler )
     {
         this.contentHandler = contentHandler;
+        this.lexicalHandler = lexicalHandler;
     }
 
     public SAXEvent characters( final char[] ch, final int start, final int length )
     {
-        final char[] txt;
-        if ( start > 0 )
-        {
-            txt = new char[length];
-            System.arraycopy( ch, start, txt, 0, length );
-        }
-        else
-        {
-            txt = ch;
-        }
-            
+        final char[] txt = new char[length];
+        System.arraycopy( ch, start, txt, 0, length );
         return () -> contentHandler.characters( txt, 0, length );
     }
 
@@ -104,8 +99,46 @@ public final class SAXEventFactory
         return () -> contentHandler.startPrefixMapping( prefix, uri );
     }
     
-    public static SAXEventFactory newInstance( ContentHandler handler )
+    public static SAXEventFactory newInstance( ContentHandler contentHandler, LexicalHandler lexicalHandler )
+    {
+        return new SAXEventFactory( contentHandler, lexicalHandler );
+    }
+
+    public SAXEvent startDTD( String name, String publicId, String systemId )
+    {
+        return () -> lexicalHandler.startDTD( name, publicId, systemId );
+    }
+
+    public SAXEvent endDTD()
+    {
+        return () -> lexicalHandler.endDTD();
+    }
+
+    public SAXEvent startEntity( String name )
+    {
+        return () -> lexicalHandler.startEntity( name );
+    }
+
+    public SAXEvent endEntity( String name )
+    {
+        return () -> lexicalHandler.endEntity( name );
+        
+    }
+
+    public SAXEvent startCDATA()
+    {
+        return () -> lexicalHandler.startCDATA();
+    }
+
+    public SAXEvent endCDATA()
+    {
+        return () -> lexicalHandler.endCDATA();
+    }
+
+    public SAXEvent comment( char[] ch, int start, int length )
     {
-        return new SAXEventFactory( handler );
+        final char[] txt = new char[length];
+        System.arraycopy( ch, start, txt, 0, length );
+        return () -> lexicalHandler.comment(  txt, 0, length );
     }
 }
diff --git a/maven-xml/src/main/java/org/apache/maven/xml/SAXEventUtils.java b/maven-xml/src/main/java/org/apache/maven/xml/sax/SAXEventUtils.java
similarity index 97%
rename from maven-xml/src/main/java/org/apache/maven/xml/SAXEventUtils.java
rename to maven-xml/src/main/java/org/apache/maven/xml/sax/SAXEventUtils.java
index 2f3644d..483bb7d 100644
--- a/maven-xml/src/main/java/org/apache/maven/xml/SAXEventUtils.java
+++ b/maven-xml/src/main/java/org/apache/maven/xml/sax/SAXEventUtils.java
@@ -1,4 +1,4 @@
-package org.apache.maven.xml;
+package org.apache.maven.xml.sax;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
diff --git a/maven-xml/src/main/java/org/apache/maven/xml/sax/filter/AbstractEventXMLFilter.java b/maven-xml/src/main/java/org/apache/maven/xml/sax/filter/AbstractEventXMLFilter.java
new file mode 100644
index 0000000..d52cb7c
--- /dev/null
+++ b/maven-xml/src/main/java/org/apache/maven/xml/sax/filter/AbstractEventXMLFilter.java
@@ -0,0 +1,272 @@
+package org.apache.maven.xml.sax.filter;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.maven.xml.sax.SAXEvent;
+import org.apache.maven.xml.sax.SAXEventFactory;
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.ext.LexicalHandler;
+
+/**
+ * Builds up a list of SAXEvents, which will be executed with {@link #executeEvents()}
+ * 
+ * @author Robert Scholte
+ * @since 3.7.0
+ */
+abstract class AbstractEventXMLFilter extends AbstractSAXFilter
+{
+    private List<SAXEvent> saxEvents = new ArrayList<>();
+    
+    private SAXEventFactory eventFactory;
+    
+    // characters BEFORE startElement must get state of startingElement
+    // this way removing based on state keeps formatting
+    private SAXEvent characters;
+    
+    // map of characters AFTER starting element, will be cleaned up after closeElement
+    private Map<String, SAXEvent> charactersMap = new HashMap<>();
+    
+    protected abstract boolean isParsing();
+    
+    protected abstract String getState();
+    
+    protected boolean acceptEvent( String state )
+    {
+        return true;
+    }
+    
+//    protected final void applyCharacters() throws SAXException
+//    {
+//        if ( characters != null )
+//        {
+//            processEvent( characters );
+//        }
+//    }
+    
+    AbstractEventXMLFilter()
+    {
+        super();
+    }
+
+    <T extends XMLReader & LexicalHandler> AbstractEventXMLFilter( T parent )
+    {
+        setParent( parent );
+    }
+
+    private SAXEventFactory getEventFactory()
+    {
+        if ( eventFactory == null )
+        {
+            eventFactory = SAXEventFactory.newInstance( getContentHandler(), getLexicalHandler() );
+        }
+        return eventFactory;
+    }
+    
+    private void processEvent( final SAXEvent event )
+                    throws SAXException
+    {
+        if ( isParsing() )
+        {
+            final String eventState = getState();
+            final SAXEvent charactersEvent = characters;
+            
+            if ( charactersEvent != null )
+            {
+                saxEvents.add( () -> 
+                {
+                    if ( acceptEvent( eventState ) )
+                    {
+                        charactersEvent.execute();
+                    }
+                } );
+                characters = null;
+            }
+
+            saxEvents.add( () -> 
+            {
+                if ( acceptEvent( eventState ) )
+                {
+                    event.execute();
+                }
+            } );
+        }
+        else
+        {
+            event.execute();
+        }
+    }
+    
+    protected final void executeEvents() throws SAXException
+    {
+        final String eventState = getState();
+        final SAXEvent charactersEvent = characters;
+        if ( charactersEvent != null )
+        {
+            saxEvents.add( () -> 
+            {
+                if ( acceptEvent( eventState ) )
+                {
+                    charactersEvent.execute();
+                }
+            } );
+            characters = null;
+        }
+        
+        // not with streams due to checked SAXException
+        for ( SAXEvent saxEvent : saxEvents )
+        {
+            saxEvent.execute();
+        }
+    }
+    
+    @Override
+    public void setDocumentLocator( Locator locator )
+    {
+        try
+        {
+            processEvent( getEventFactory().setDocumentLocator( locator ) );
+        }
+        catch ( SAXException e )
+        {
+            // noop, setDocumentLocator can never throw a SAXException
+        }
+    }
+
+    @Override
+    public void startDocument() throws SAXException
+    {
+        processEvent( getEventFactory().startDocument() );
+    }
+
+    @Override
+    public void endDocument() throws SAXException
+    {
+        processEvent( getEventFactory().endDocument() );
+    }
+
+    @Override
+    public void startPrefixMapping( String prefix, String uri ) throws SAXException
+    {
+        processEvent( getEventFactory().startPrefixMapping( prefix, uri ) );
+    }
+
+    @Override
+    public void endPrefixMapping( String prefix ) throws SAXException
+    {
+        processEvent( getEventFactory().endPrefixMapping( prefix ) );
+    }
+
+    @Override
+    public void startElement( String uri, String localName, String qName, Attributes atts ) throws SAXException
+    {
+        processEvent( getEventFactory().startElement( uri, localName, qName, atts ) );
+    }
+
+    @Override
+    public void endElement( String uri, String localName, String qName ) throws SAXException
+    {
+        processEvent( getEventFactory().endElement( uri, localName, qName ) );
+    }
+
+    @Override
+    public void characters( char[] ch, int start, int length ) throws SAXException
+    {
+        if ( isParsing() )
+        {
+            this.characters = getEventFactory().characters( ch, start, length );
+            this.charactersMap.put( getState(), characters );
+        }
+        else
+        {
+            super.characters( ch, start, length );
+        }
+    }
+
+    @Override
+    public void ignorableWhitespace( char[] ch, int start, int length ) throws SAXException
+    {
+        processEvent( getEventFactory().ignorableWhitespace( ch, start, length ) );
+    }
+
+    @Override
+    public void processingInstruction( String target, String data ) throws SAXException
+    {
+        processEvent( getEventFactory().processingInstruction( target, data ) );
+    }
+
+    @Override
+    public void skippedEntity( String name ) throws SAXException
+    {
+        processEvent( getEventFactory().skippedEntity( name ) );
+    }
+
+    @Override
+    public void startDTD( String name, String publicId, String systemId ) throws SAXException
+    {
+        processEvent( getEventFactory().startCDATA() );
+    }
+
+    @Override
+    public void endDTD() throws SAXException
+    {
+        processEvent( getEventFactory().endDTD() );
+    }
+
+    @Override
+    public void startEntity( String name ) throws SAXException
+    {
+        processEvent( getEventFactory().startEntity( name ) );
+    }
+
+    @Override
+    public void endEntity( String name ) throws SAXException
+    {
+        processEvent( getEventFactory().endEntity( name ) );
+    }
+
+    @Override
+    public void startCDATA()
+        throws SAXException
+    {
+        processEvent( getEventFactory().startCDATA() );
+    }
+
+    @Override
+    public void endCDATA()
+        throws SAXException
+    {
+        processEvent( getEventFactory().endCDATA() );        
+    }
+
+    @Override
+    public void comment( char[] ch, int start, int length )
+        throws SAXException
+    {
+        processEvent( getEventFactory().comment( ch, start, length ) );
+    }
+}
diff --git a/maven-xml/src/main/java/org/apache/maven/xml/sax/filter/AbstractSAXFilter.java b/maven-xml/src/main/java/org/apache/maven/xml/sax/filter/AbstractSAXFilter.java
new file mode 100644
index 0000000..89de519
--- /dev/null
+++ b/maven-xml/src/main/java/org/apache/maven/xml/sax/filter/AbstractSAXFilter.java
@@ -0,0 +1,130 @@
+package org.apache.maven.xml.sax.filter;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.ext.LexicalHandler;
+import org.xml.sax.helpers.XMLFilterImpl;
+
+/**
+ * XMLFilter with LexicalHandler.
+ * Since some filters collect events before processing them, the LexicalHandler events must be collected too.
+ * Otherwise the LexicalHandler events might end up before all collected XMLReader events.
+ * 
+ * @author Robert Scholte
+ * @since 3.7.0 
+ */
+public class AbstractSAXFilter extends XMLFilterImpl implements LexicalHandler
+{
+    private LexicalHandler lexicalHandler;
+    
+    AbstractSAXFilter()
+    {
+        super();
+    }
+
+    public <T extends XMLReader & LexicalHandler> AbstractSAXFilter( T parent )
+    {
+        setParent( parent );
+        setLexicalHandler( parent );
+    }
+    
+    public LexicalHandler getLexicalHandler()
+    {
+        return lexicalHandler;
+    }
+    
+    public void setLexicalHandler( LexicalHandler lexicalHandler )
+    {
+        this.lexicalHandler = lexicalHandler;
+    }
+    
+    @Override
+    public void startDTD( String name, String publicId, String systemId )
+        throws SAXException
+    {
+        if ( lexicalHandler != null ) 
+        {
+            lexicalHandler.startDTD( name, publicId, systemId );
+        }
+    }
+
+    @Override
+    public void endDTD()
+        throws SAXException
+    {
+        if ( lexicalHandler != null ) 
+        {
+            lexicalHandler.endDTD();
+        }
+    }
+
+    @Override
+    public void startEntity( String name )
+        throws SAXException
+    {
+        if ( lexicalHandler != null ) 
+        {
+            lexicalHandler.startEntity( name );
+        }
+    }
+
+    @Override
+    public void endEntity( String name )
+        throws SAXException
+    {
+        if ( lexicalHandler != null ) 
+        {
+            lexicalHandler.endEntity( name );
+        }
+    }
+
+    @Override
+    public void startCDATA()
+        throws SAXException
+    {
+        if ( lexicalHandler != null ) 
+        {
+            lexicalHandler.startCDATA();
+        }
+    }
+
+    @Override
+    public void endCDATA()
+        throws SAXException
+    {
+        if ( lexicalHandler != null ) 
+        {
+            lexicalHandler.endCDATA();
+        }
+    }
+
+    @Override
+    public void comment( char[] ch, int start, int length )
+        throws SAXException
+    {
+        if ( lexicalHandler != null ) 
+        {
+            lexicalHandler.comment( ch, start, length );
+        }
+    }
+
+}
diff --git a/maven-xml/src/main/java/org/apache/maven/xml/filter/BuildPomXMLFilter.java b/maven-xml/src/main/java/org/apache/maven/xml/sax/filter/BuildPomXMLFilter.java
similarity index 87%
rename from maven-xml/src/main/java/org/apache/maven/xml/filter/BuildPomXMLFilter.java
rename to maven-xml/src/main/java/org/apache/maven/xml/sax/filter/BuildPomXMLFilter.java
index ab846a0..041d5c1 100644
--- a/maven-xml/src/main/java/org/apache/maven/xml/filter/BuildPomXMLFilter.java
+++ b/maven-xml/src/main/java/org/apache/maven/xml/sax/filter/BuildPomXMLFilter.java
@@ -1,4 +1,4 @@
-package org.apache.maven.xml.filter;
+package org.apache.maven.xml.sax.filter;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -20,7 +20,7 @@ package org.apache.maven.xml.filter;
  */
 
 import org.xml.sax.XMLReader;
-import org.xml.sax.helpers.XMLFilterImpl;
+import org.xml.sax.ext.LexicalHandler;
 
 /**
  * Filter to adjust pom on filesystem before being processed for effective pom.
@@ -32,14 +32,14 @@ import org.xml.sax.helpers.XMLFilterImpl;
  * @author Robert Scholte
  * @since 3.7.0
  */
-public class BuildPomXMLFilter extends XMLFilterImpl 
+public class BuildPomXMLFilter extends AbstractSAXFilter 
 {
     BuildPomXMLFilter()
     {
         super();
     }
 
-    BuildPomXMLFilter( XMLReader parent )
+    <T extends XMLReader & LexicalHandler> BuildPomXMLFilter( T parent )
     {
         super( parent );
     }
diff --git a/maven-xml/src/main/java/org/apache/maven/xml/filter/BuildPomXMLFilterFactory.java b/maven-xml/src/main/java/org/apache/maven/xml/sax/filter/BuildPomXMLFilterFactory.java
similarity index 66%
rename from maven-xml/src/main/java/org/apache/maven/xml/filter/BuildPomXMLFilterFactory.java
rename to maven-xml/src/main/java/org/apache/maven/xml/sax/filter/BuildPomXMLFilterFactory.java
index 1df17ba..d115673 100644
--- a/maven-xml/src/main/java/org/apache/maven/xml/filter/BuildPomXMLFilterFactory.java
+++ b/maven-xml/src/main/java/org/apache/maven/xml/sax/filter/BuildPomXMLFilterFactory.java
@@ -1,4 +1,4 @@
-package org.apache.maven.xml.filter;
+package org.apache.maven.xml.sax.filter;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -24,12 +24,18 @@ import java.util.Optional;
 import java.util.function.Function;
 
 import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.sax.SAXTransformerFactory;
 
 import org.apache.maven.xml.Factories;
 import org.xml.sax.SAXException;
 import org.xml.sax.XMLReader;
+import org.xml.sax.ext.LexicalHandler;
 
 /**
+ * Base class for third parties to extend. When annotating it with Named("somecustomname"),
+ * Maven will pick this up as  instead of the DefaultBuildPomXMLFilterFactory 
  * 
  * @author Robert Scholte
  * 
@@ -38,15 +44,18 @@ import org.xml.sax.XMLReader;
 public abstract class BuildPomXMLFilterFactory
 {
     public final BuildPomXMLFilter get( Path projectFile )
-        throws SAXException, ParserConfigurationException
+        throws SAXException, ParserConfigurationException, TransformerConfigurationException
     {
-        XMLReader parent = getParent();
+        AbstractSAXFilter parent = new AbstractSAXFilter();
+        parent.setParent( getXMLReader() );
+        parent.setLexicalHandler( getLexicalHander() );
 
         if ( getDependencyKeyToVersionMapper() != null )
         {
             ReactorDependencyXMLFilter reactorDependencyXMLFilter =
                 new ReactorDependencyXMLFilter( getDependencyKeyToVersionMapper() );
             reactorDependencyXMLFilter.setParent( parent );
+            reactorDependencyXMLFilter.setLexicalHandler( parent );
             parent = reactorDependencyXMLFilter;
         }
 
@@ -55,6 +64,7 @@ public abstract class BuildPomXMLFilterFactory
             ParentXMLFilter parentFilter = new ParentXMLFilter( getRelativePathMapper() );
             parentFilter.setProjectPath( projectFile.getParent() );
             parentFilter.setParent( parent );
+            parentFilter.setLexicalHandler( parent );
             parent = parentFilter;
         }
         
@@ -66,19 +76,32 @@ public abstract class BuildPomXMLFilterFactory
         if ( ciFriendlyFilter.isSet() )
         {
             ciFriendlyFilter.setParent( parent );
+            ciFriendlyFilter.setLexicalHandler( parent );
             parent = ciFriendlyFilter;
         }
 
         return new BuildPomXMLFilter( parent );
     }
     
-    protected XMLReader getParent() throws SAXException, ParserConfigurationException 
+    protected XMLReader getXMLReader() throws SAXException, ParserConfigurationException 
     {
         XMLReader xmlReader = Factories.newXMLReader();
         xmlReader.setFeature( "http://xml.org/sax/features/namespaces", true );
         return xmlReader;
     }
     
+    protected LexicalHandler getLexicalHander() throws TransformerConfigurationException 
+    {
+        TransformerFactory transformerFactory = Factories.newTransformerFactory();
+        if ( transformerFactory instanceof SAXTransformerFactory )
+        {
+            SAXTransformerFactory saxTransformerFactory = (SAXTransformerFactory) transformerFactory;
+            return saxTransformerFactory.newTransformerHandler();
+        }
+        throw new TransformerConfigurationException( "Failed to get LexicalHandler via TransformerFactory:"
+            + " it is not an instance of SAXTransformerFactory" );
+    }
+    
     // getters for the 3 magic properties of CIFriendly versions ( https://maven.apache.org/maven-ci-friendly.html )
     
     protected abstract Optional<String> getChangelist();
diff --git a/maven-xml/src/main/java/org/apache/maven/xml/filter/CiFriendlyXMLFilter.java b/maven-xml/src/main/java/org/apache/maven/xml/sax/filter/CiFriendlyXMLFilter.java
similarity index 95%
rename from maven-xml/src/main/java/org/apache/maven/xml/filter/CiFriendlyXMLFilter.java
rename to maven-xml/src/main/java/org/apache/maven/xml/sax/filter/CiFriendlyXMLFilter.java
index 7f8c206..a13e4d4 100644
--- a/maven-xml/src/main/java/org/apache/maven/xml/filter/CiFriendlyXMLFilter.java
+++ b/maven-xml/src/main/java/org/apache/maven/xml/sax/filter/CiFriendlyXMLFilter.java
@@ -1,4 +1,4 @@
-package org.apache.maven.xml.filter;
+package org.apache.maven.xml.sax.filter;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -22,7 +22,6 @@ package org.apache.maven.xml.filter;
 import java.util.function.Function;
 
 import org.xml.sax.SAXException;
-import org.xml.sax.helpers.XMLFilterImpl;
 
 /**
  * Resolves all ci-friendly properties occurrences
@@ -31,7 +30,7 @@ import org.xml.sax.helpers.XMLFilterImpl;
  * @since 3.7.0
  */
 class CiFriendlyXMLFilter
-    extends XMLFilterImpl
+    extends AbstractSAXFilter
 {
     private Function<String, String> replaceChain = Function.identity();
     
diff --git a/maven-xml/src/main/java/org/apache/maven/xml/filter/ConsumerPomXMLFilter.java b/maven-xml/src/main/java/org/apache/maven/xml/sax/filter/ConsumerPomXMLFilter.java
similarity index 87%
rename from maven-xml/src/main/java/org/apache/maven/xml/filter/ConsumerPomXMLFilter.java
rename to maven-xml/src/main/java/org/apache/maven/xml/sax/filter/ConsumerPomXMLFilter.java
index 8993d1d..1c227a7 100644
--- a/maven-xml/src/main/java/org/apache/maven/xml/filter/ConsumerPomXMLFilter.java
+++ b/maven-xml/src/main/java/org/apache/maven/xml/sax/filter/ConsumerPomXMLFilter.java
@@ -1,4 +1,4 @@
-package org.apache.maven.xml.filter;
+package org.apache.maven.xml.sax.filter;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -20,7 +20,7 @@ package org.apache.maven.xml.filter;
  */
 
 import org.xml.sax.XMLReader;
-import org.xml.sax.helpers.XMLFilterImpl;
+import org.xml.sax.ext.LexicalHandler;
 
 /**
  * XML Filter to transform pom.xml to consumer pom.
@@ -33,9 +33,9 @@ import org.xml.sax.helpers.XMLFilterImpl;
  * @author Robert Scholte
  * @since 3.7.0
  */
-public class ConsumerPomXMLFilter extends XMLFilterImpl
+public class ConsumerPomXMLFilter extends AbstractSAXFilter
 {
-    ConsumerPomXMLFilter( XMLReader filter )
+    <T extends XMLReader & LexicalHandler> ConsumerPomXMLFilter( T filter )
     {
         super( filter );
     }
diff --git a/maven-xml/src/main/java/org/apache/maven/xml/filter/ConsumerPomXMLFilterFactory.java b/maven-xml/src/main/java/org/apache/maven/xml/sax/filter/ConsumerPomXMLFilterFactory.java
similarity index 83%
rename from maven-xml/src/main/java/org/apache/maven/xml/filter/ConsumerPomXMLFilterFactory.java
rename to maven-xml/src/main/java/org/apache/maven/xml/sax/filter/ConsumerPomXMLFilterFactory.java
index dfea9ba..612ddb9 100644
--- a/maven-xml/src/main/java/org/apache/maven/xml/filter/ConsumerPomXMLFilterFactory.java
+++ b/maven-xml/src/main/java/org/apache/maven/xml/sax/filter/ConsumerPomXMLFilterFactory.java
@@ -1,4 +1,4 @@
-package org.apache.maven.xml.filter;
+package org.apache.maven.xml.sax.filter;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -24,10 +24,9 @@ import java.nio.file.Path;
 import javax.inject.Inject;
 import javax.inject.Provider;
 import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.TransformerConfigurationException;
 
 import org.xml.sax.SAXException;
-import org.xml.sax.XMLFilter;
-import org.xml.sax.XMLReader;
 
 /**
  * 
@@ -50,12 +49,12 @@ public abstract class ConsumerPomXMLFilterFactory
     }
     
     public final ConsumerPomXMLFilter get( Path projectPath )
-        throws SAXException, ParserConfigurationException
+        throws SAXException, ParserConfigurationException, TransformerConfigurationException
     {
-        XMLFilter parent = buildPomXMLFilterFactory.get().get( projectPath );
+        BuildPomXMLFilter parent = buildPomXMLFilterFactory.get().get( projectPath );
         
         // Ensure that xs:any elements aren't touched by next filters
-        XMLReader filter = new FastForwardFilter( parent );
+        AbstractSAXFilter filter = new FastForwardFilter( parent );
         
         // Strip modules
         filter = new ModulesXMLFilter( filter );
diff --git a/maven-xml/src/main/java/org/apache/maven/xml/filter/DependencyKey.java b/maven-xml/src/main/java/org/apache/maven/xml/sax/filter/DependencyKey.java
similarity index 98%
rename from maven-xml/src/main/java/org/apache/maven/xml/filter/DependencyKey.java
rename to maven-xml/src/main/java/org/apache/maven/xml/sax/filter/DependencyKey.java
index 6b5e905..1168351 100644
--- a/maven-xml/src/main/java/org/apache/maven/xml/filter/DependencyKey.java
+++ b/maven-xml/src/main/java/org/apache/maven/xml/sax/filter/DependencyKey.java
@@ -1,4 +1,4 @@
-package org.apache.maven.xml.filter;
+package org.apache.maven.xml.sax.filter;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
diff --git a/maven-xml/src/main/java/org/apache/maven/xml/filter/FastForwardFilter.java b/maven-xml/src/main/java/org/apache/maven/xml/sax/filter/FastForwardFilter.java
similarity index 94%
rename from maven-xml/src/main/java/org/apache/maven/xml/filter/FastForwardFilter.java
rename to maven-xml/src/main/java/org/apache/maven/xml/sax/filter/FastForwardFilter.java
index 6e03a34..0100e6b 100644
--- a/maven-xml/src/main/java/org/apache/maven/xml/filter/FastForwardFilter.java
+++ b/maven-xml/src/main/java/org/apache/maven/xml/sax/filter/FastForwardFilter.java
@@ -1,4 +1,4 @@
-package org.apache.maven.xml.filter;
+package org.apache.maven.xml.sax.filter;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -27,7 +27,7 @@ import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 import org.xml.sax.XMLFilter;
 import org.xml.sax.XMLReader;
-import org.xml.sax.helpers.XMLFilterImpl;
+import org.xml.sax.ext.LexicalHandler;
 
 /**
  * This filter will skip all following filters and write directly to the output.
@@ -36,7 +36,7 @@ import org.xml.sax.helpers.XMLFilterImpl;
  * @author Robert Scholte
  * @since 3.7.0
  */
-class FastForwardFilter extends XMLFilterImpl
+class FastForwardFilter extends AbstractSAXFilter
 {
     /**
      * DOM elements of pom
@@ -61,7 +61,7 @@ class FastForwardFilter extends XMLFilterImpl
         super();
     }
 
-    FastForwardFilter( XMLReader parent )
+    <T extends XMLReader & LexicalHandler> FastForwardFilter( T parent )
     {
         super( parent );
     }
diff --git a/maven-core/src/main/java/org/apache/maven/xml/filter/ModulesXMLFilter.java b/maven-xml/src/main/java/org/apache/maven/xml/sax/filter/ModulesXMLFilter.java
similarity index 85%
rename from maven-core/src/main/java/org/apache/maven/xml/filter/ModulesXMLFilter.java
rename to maven-xml/src/main/java/org/apache/maven/xml/sax/filter/ModulesXMLFilter.java
index 9e7b6fd..16ca2ed 100644
--- a/maven-core/src/main/java/org/apache/maven/xml/filter/ModulesXMLFilter.java
+++ b/maven-xml/src/main/java/org/apache/maven/xml/sax/filter/ModulesXMLFilter.java
@@ -1,4 +1,4 @@
-package org.apache.maven.xml.filter;
+package org.apache.maven.xml.sax.filter;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -22,7 +22,7 @@ package org.apache.maven.xml.filter;
 import org.xml.sax.Attributes;
 import org.xml.sax.SAXException;
 import org.xml.sax.XMLReader;
-import org.xml.sax.helpers.XMLFilterImpl;
+import org.xml.sax.ext.LexicalHandler;
 
 /**
  * Remove all modules, this is just buildtime information
@@ -31,7 +31,7 @@ import org.xml.sax.helpers.XMLFilterImpl;
  * @since 3.7.0
  */
 class ModulesXMLFilter
-    extends XMLFilterImpl
+    extends AbstractSAXFilter
 {
     /**
      * Using 3 to also remove whitespace-block after closing tag
@@ -46,7 +46,7 @@ class ModulesXMLFilter
         super();
     }
 
-    ModulesXMLFilter( XMLReader parent )
+    <T extends XMLReader & LexicalHandler> ModulesXMLFilter( T parent )
     {
         super( parent );
     }
@@ -98,5 +98,14 @@ class ModulesXMLFilter
             super.characters( ch, start, length );
         }
     }
-
+    
+    @Override
+    public void comment( char[] ch, int start, int length )
+        throws SAXException
+    {
+        if ( modulesStatus != 1 )
+        {
+            super.comment( ch, start, length );
+        }
+    }
 }
diff --git a/maven-xml/src/main/java/org/apache/maven/xml/filter/ParentXMLFilter.java b/maven-xml/src/main/java/org/apache/maven/xml/sax/filter/ParentXMLFilter.java
similarity index 59%
rename from maven-xml/src/main/java/org/apache/maven/xml/filter/ParentXMLFilter.java
rename to maven-xml/src/main/java/org/apache/maven/xml/sax/filter/ParentXMLFilter.java
index 7bb744d..9cb6ff2 100644
--- a/maven-xml/src/main/java/org/apache/maven/xml/filter/ParentXMLFilter.java
+++ b/maven-xml/src/main/java/org/apache/maven/xml/sax/filter/ParentXMLFilter.java
@@ -1,4 +1,4 @@
-package org.apache.maven.xml.filter;
+package org.apache.maven.xml.sax.filter;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -22,19 +22,13 @@ package org.apache.maven.xml.filter;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 
-import java.util.ArrayList;
-import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.function.Function;
 
-import org.apache.maven.xml.SAXEvent;
-import org.apache.maven.xml.SAXEventFactory;
-import org.apache.maven.xml.SAXEventUtils;
+import org.apache.maven.xml.sax.SAXEventUtils;
 import org.xml.sax.Attributes;
-import org.xml.sax.Locator;
 import org.xml.sax.SAXException;
-import org.xml.sax.helpers.XMLFilterImpl;
 
 /**
  * <p>
@@ -46,7 +40,7 @@ import org.xml.sax.helpers.XMLFilterImpl;
  * @since 3.7.0
  */
 class ParentXMLFilter
-    extends XMLFilterImpl
+    extends AbstractEventXMLFilter
 {
     private boolean parsingParent;
 
@@ -63,12 +57,6 @@ class ParentXMLFilter
     
     private Optional<RelativeProject> resolvedParent;
 
-    private char[] linebreak;
-    
-    private List<SAXEvent> saxEvents = new ArrayList<>();
-
-    private SAXEventFactory eventFactory;
-    
     private final Function<Path, Optional<RelativeProject>> relativePathMapper;
     
     private Path projectPath;
@@ -88,37 +76,25 @@ class ParentXMLFilter
         this.projectPath = projectPath;
     }
     
-    private SAXEventFactory getEventFactory()
+    @Override
+    protected boolean isParsing()
     {
-        if ( eventFactory == null )
-        {
-            eventFactory = SAXEventFactory.newInstance( getContentHandler() );
-        }
-        return eventFactory;
+        return parsingParent;
     }
 
-    private void processEvent( final SAXEvent event )
-        throws SAXException
+    @Override
+    protected String getState()
     {
-        if ( parsingParent )
-        {
-            final String eventState = state;
-
-            saxEvents.add( () -> 
-            {
-                if ( !( "relativePath".equals( eventState ) && resolvedParent.isPresent() ) )
-                {
-                    event.execute();
-                }
-            } );
-        }
-        else
-        {
-            event.execute();
-        }
+        return state;
     }
 
     @Override
+    protected boolean acceptEvent( String eventState )
+    {
+        return  !( "relativePath".equals( eventState ) && resolvedParent.isPresent() );
+    }
+    
+    @Override
     public void startElement( String uri, final String localName, String qName, Attributes atts )
         throws SAXException
     {
@@ -134,7 +110,7 @@ class ParentXMLFilter
             hasVersion |= "version".equals( localName );
         }
         
-        processEvent( getEventFactory().startElement( uri, localName, qName, atts ) );
+        super.startElement( uri, localName, qName, atts );
     }
 
     @Override
@@ -157,9 +133,6 @@ class ParentXMLFilter
                             break;
                         }
                     }
-                    
-                    linebreak = new char[l];
-                    System.arraycopy( ch, start, linebreak, 0, l );
                     break;
                 case "relativePath":
                     relativePath = new String( ch, start, length );
@@ -175,14 +148,7 @@ class ParentXMLFilter
             }
         }
         
-        processEvent( getEventFactory().characters( ch, start, length ) );
-    }
-
-    @Override
-    public void endDocument()
-        throws SAXException
-    {
-        processEvent( getEventFactory().endDocument() );
+        super.characters( ch, start, length );
     }
 
     @Override
@@ -200,30 +166,20 @@ class ParentXMLFilter
                             resolveRelativePath( Paths.get( Objects.toString( relativePath, "../pom.xml" ) ) );
                     }
                     
-                    // not with streams due to checked SAXException
-                    for ( SAXEvent saxEvent : saxEvents )
-                    {
-                        saxEvent.execute();
-                    }
-                    
                     if ( !hasVersion && resolvedParent.isPresent() )
                     {
                         String versionQName = SAXEventUtils.renameQName( qName, "version" );
                         
-                        getEventFactory().startElement( uri, "version", versionQName, null ).execute();
+                        super.startElement( uri, "version", versionQName, null );
                         
                         String resolvedParentVersion = resolvedParent.get().getVersion();
                         
-                        getEventFactory().characters( resolvedParentVersion.toCharArray(), 0,
-                                                      resolvedParentVersion.length() ).execute();
-                        
-                        getEventFactory().endElement( uri, "version", versionQName ).execute();
+                        super.characters( resolvedParentVersion.toCharArray(), 0,
+                                                      resolvedParentVersion.length() );
                         
-                        if ( linebreak != null )
-                        {
-                            getEventFactory().characters( linebreak, 0, linebreak.length ).execute();
-                        }
+                        super.endElement( uri, "version", versionQName );
                     }
+                    executeEvents();
                     
                     parsingParent = false;
                     break;
@@ -232,68 +188,12 @@ class ParentXMLFilter
             }
         }
         
-        processEvent( getEventFactory().endElement( uri, localName, qName ) );
+        super.endElement( uri, localName, qName );
 
         // for this simple structure resetting to parent it sufficient
         state = "parent";
     }
 
-    @Override
-    public void endPrefixMapping( String prefix )
-        throws SAXException
-    {
-        processEvent( getEventFactory().endPrefixMapping( prefix ) );
-    }
-
-    @Override
-    public void ignorableWhitespace( char[] ch, int start, int length )
-        throws SAXException
-    {
-        processEvent( getEventFactory().ignorableWhitespace( ch, start, length ) );
-    }
-
-    @Override
-    public void processingInstruction( String target, String data )
-        throws SAXException
-    {
-        processEvent( getEventFactory().processingInstruction( target, data ) );
-
-    }
-
-    @Override
-    public void setDocumentLocator( Locator locator )
-    {
-        try
-        {
-            processEvent( getEventFactory().setDocumentLocator( locator ) );
-        }
-        catch ( SAXException e )
-        {
-            // noop
-        }
-    }
-
-    @Override
-    public void skippedEntity( String name )
-        throws SAXException
-    {
-        processEvent( getEventFactory().skippedEntity( name ) );
-    }
-
-    @Override
-    public void startDocument()
-        throws SAXException
-    {
-        processEvent( getEventFactory().startDocument() );
-    }
-
-    @Override
-    public void startPrefixMapping( String prefix, String uri )
-        throws SAXException
-    {
-        processEvent( getEventFactory().startPrefixMapping( prefix, uri ) );
-    }
-
     protected Optional<RelativeProject> resolveRelativePath( Path relativePath )
     {
         Optional<RelativeProject> mappedProject =
diff --git a/maven-xml/src/main/java/org/apache/maven/xml/filter/ReactorDependencyXMLFilter.java b/maven-xml/src/main/java/org/apache/maven/xml/sax/filter/ReactorDependencyXMLFilter.java
similarity index 90%
rename from maven-xml/src/main/java/org/apache/maven/xml/filter/ReactorDependencyXMLFilter.java
rename to maven-xml/src/main/java/org/apache/maven/xml/sax/filter/ReactorDependencyXMLFilter.java
index fa8b1e5..b37092f 100644
--- a/maven-xml/src/main/java/org/apache/maven/xml/filter/ReactorDependencyXMLFilter.java
+++ b/maven-xml/src/main/java/org/apache/maven/xml/sax/filter/ReactorDependencyXMLFilter.java
@@ -1,4 +1,4 @@
-package org.apache.maven.xml.filter;
+package org.apache.maven.xml.sax.filter;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -21,10 +21,9 @@ package org.apache.maven.xml.filter;
 
 import java.util.function.Function;
 
-import org.apache.maven.xml.SAXEventUtils;
+import org.apache.maven.xml.sax.SAXEventUtils;
 import org.xml.sax.Attributes;
 import org.xml.sax.SAXException;
-import org.xml.sax.helpers.XMLFilterImpl;
 
 /**
  * Will apply the version if the dependency is part of the reactor
@@ -32,7 +31,7 @@ import org.xml.sax.helpers.XMLFilterImpl;
  * @author Robert Scholte
  * @since 3.7.0
  */
-public class ReactorDependencyXMLFilter extends XMLFilterImpl
+public class ReactorDependencyXMLFilter extends AbstractEventXMLFilter
 {
     private boolean parsingDependency;
 
@@ -109,11 +108,14 @@ public class ReactorDependencyXMLFilter extends XMLFilterImpl
                         if ( version != null )
                         {
                             String versionQName = SAXEventUtils.renameQName( qName, "version" );
+                            
                             super.startElement( uri, "version", versionQName, null );
                             super.characters( version.toCharArray(), 0, version.length() );
                             super.endElement( uri, "version", versionQName );
                         }
                     }
+                    super.executeEvents();
+                    
                     parsingDependency = false;
                     
                     // reset
@@ -136,5 +138,17 @@ public class ReactorDependencyXMLFilter extends XMLFilterImpl
     {
         return reactorVersionMapper.apply( new DependencyKey( groupId, artifactId ) );
     }
+
+    @Override
+    protected boolean isParsing()
+    {
+        return parsingDependency;
+    }
+
+    @Override
+    protected String getState()
+    {
+        return state;
+    }
     
 }
diff --git a/maven-xml/src/main/java/org/apache/maven/xml/filter/RelativePathXMLFilter.java b/maven-xml/src/main/java/org/apache/maven/xml/sax/filter/RelativePathXMLFilter.java
similarity index 57%
rename from maven-xml/src/main/java/org/apache/maven/xml/filter/RelativePathXMLFilter.java
rename to maven-xml/src/main/java/org/apache/maven/xml/sax/filter/RelativePathXMLFilter.java
index 5dc367f..ebbc890 100644
--- a/maven-xml/src/main/java/org/apache/maven/xml/filter/RelativePathXMLFilter.java
+++ b/maven-xml/src/main/java/org/apache/maven/xml/sax/filter/RelativePathXMLFilter.java
@@ -1,4 +1,4 @@
-package org.apache.maven.xml.filter;
+package org.apache.maven.xml.sax.filter;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -22,7 +22,7 @@ package org.apache.maven.xml.filter;
 import org.xml.sax.Attributes;
 import org.xml.sax.SAXException;
 import org.xml.sax.XMLReader;
-import org.xml.sax.helpers.XMLFilterImpl;
+import org.xml.sax.ext.LexicalHandler;
 
 /**
  * Remove content from relativePath
@@ -33,67 +33,78 @@ import org.xml.sax.helpers.XMLFilterImpl;
  * @since 3.7.0
  */
 class RelativePathXMLFilter
-    extends XMLFilterImpl
+    extends AbstractEventXMLFilter
 {
-    /**
-     * Using 3 to also remove whitespace-block after closing tag
-     * -1 none
-     *  1 started
-     *  2 ended
-     */
-    int relativePathStatus = -1;
+    private boolean parsingParent;
+    
+    private String state;
     
     RelativePathXMLFilter()
     {
         super();
     }
 
-    RelativePathXMLFilter( XMLReader parent )
+    <T extends XMLReader & LexicalHandler> RelativePathXMLFilter( T parent )
     {
         super( parent );
     }
-
+    
     @Override
-    public void startElement( String uri, String localName, String qName, Attributes atts )
+    public void startElement( String uri, final String localName, String qName, Attributes atts )
         throws SAXException
     {
-        
-        if ( relativePathStatus == -1 && "relativePath".equals( localName ) )
+        if ( !parsingParent && "parent".equals( localName ) )
         {
-            relativePathStatus = 1;
+            parsingParent = true;
         }
-        else if ( relativePathStatus == 2 )
+
+        if ( parsingParent )
         {
-            relativePathStatus = -1;
+            state = localName;
         }
-
+        
         super.startElement( uri, localName, qName, atts );
     }
-
+    
     @Override
     public void endElement( String uri, String localName, String qName )
         throws SAXException
     {
-        if ( relativePathStatus == 1 && "relativePath".equals( localName ) )
-        {
-            relativePathStatus = 2;
-        }
-        else if ( relativePathStatus == 2 )
+        if ( parsingParent )
         {
-            relativePathStatus = -1;
+            switch ( localName )
+            {
+                case "parent":
+                    executeEvents();
+                    
+                    parsingParent = false;
+                    break;
+                default:
+                    break;
+            }
         }
         
         super.endElement( uri, localName, qName );
+
+        // for this simple structure resetting to parent it sufficient
+        state = "parent";
     }
 
     @Override
-    public void characters( char[] ch, int start, int length )
-        throws SAXException
+    protected boolean isParsing()
     {
-        if ( relativePathStatus != 1 )
-        {
-            super.characters( ch, start, length );
-        }
+        return parsingParent;
     }
 
+    @Override
+    protected String getState()
+    {
+        return state;
+    }
+
+    @Override
+    protected boolean acceptEvent( String state )
+    {
+        return !"relativePath".equals( state );
+    }
 }
diff --git a/maven-xml/src/main/java/org/apache/maven/xml/filter/RelativeProject.java b/maven-xml/src/main/java/org/apache/maven/xml/sax/filter/RelativeProject.java
similarity index 97%
rename from maven-xml/src/main/java/org/apache/maven/xml/filter/RelativeProject.java
rename to maven-xml/src/main/java/org/apache/maven/xml/sax/filter/RelativeProject.java
index a158d38..067e170 100644
--- a/maven-xml/src/main/java/org/apache/maven/xml/filter/RelativeProject.java
+++ b/maven-xml/src/main/java/org/apache/maven/xml/sax/filter/RelativeProject.java
@@ -1,4 +1,4 @@
-package org.apache.maven.xml.filter;
+package org.apache.maven.xml.sax.filter;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
diff --git a/maven-xml/src/test/java/org/apache/maven/xml/filter/ConsumerPomXMLFilterTest.java b/maven-xml/src/test/java/org/apache/maven/xml/filter/ConsumerPomXMLFilterTest.java
deleted file mode 100644
index 4ed4ab6..0000000
--- a/maven-xml/src/test/java/org/apache/maven/xml/filter/ConsumerPomXMLFilterTest.java
+++ /dev/null
@@ -1,168 +0,0 @@
-package org.apache.maven.xml.filter;
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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 static org.xmlunit.assertj.XmlAssert.assertThat;
-
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.Optional;
-import java.util.function.Function;
-
-import javax.inject.Provider;
-import javax.xml.parsers.ParserConfigurationException;
-
-import org.junit.Test;
-import org.xml.sax.SAXException;
-import org.xml.sax.XMLFilter;
-
-public class ConsumerPomXMLFilterTest extends AbstractXMLFilterTests
-{
-    @Override
-    protected XMLFilter getFilter() throws SAXException, ParserConfigurationException
-    {
-        final BuildPomXMLFilterFactory buildPomXMLFilterFactory = new BuildPomXMLFilterFactory()
-        {
-            @Override
-            protected Optional<String> getSha1()
-            {
-                return Optional.empty();
-            }
-            
-            @Override
-            protected Optional<String> getRevision()
-            {
-                return Optional.empty();
-            }
-            
-            @Override
-            protected Optional<String> getChangelist()
-            {
-                return Optional.of( "CL" );
-            }
-            
-            @Override
-            protected Function<Path, Optional<RelativeProject>> getRelativePathMapper()
-            {
-                return null;
-            }
-            
-            @Override
-            protected Function<DependencyKey, String> getDependencyKeyToVersionMapper()
-            {
-                return null;
-            }
-        };
-        
-        Provider<BuildPomXMLFilterFactory> provider = new Provider<BuildPomXMLFilterFactory>()
-        {
-
-            @Override
-            public BuildPomXMLFilterFactory get()
-            {
-                return buildPomXMLFilterFactory;
-            }
-        };
-        
-        XMLFilter filter = new ConsumerPomXMLFilterFactory( provider )
-        {
-        }.get( Paths.get( "pom.xml" ) );
-        filter.setFeature( "http://xml.org/sax/features/namespaces", true );
-        return filter;
-    }
-    
-    @Test
-    public void testAllFilters() throws Exception {
-        String input = "<project>\n"
-                     + "  <parent>\n"
-                     + "    <groupId>GROUPID</groupId>\n"
-                     + "    <artifactId>PARENT</artifactId>\n"
-                     + "    <version>VERSION</version>\n"
-                     + "    <relativePath>../pom.xml</relativePath>\n"
-                     + "  </parent>\n"
-                     + "  <artifactId>PROJECT</artifactId>\n"
-                     + "  <modules>\n"
-                     + "    <module>ab</module>\n"
-                     + "    <module>../cd</module>\n"
-                     + "  </modules>\n"
-                     + "</project>";
-        String expected = "<project>\n"
-                        + "  <parent>\n"
-                        + "    <groupId>GROUPID</groupId>\n"
-                        + "    <artifactId>PARENT</artifactId>\n"
-                        + "    <version>VERSION</version>\n"
-                        + "    <relativePath/>\n"
-                        + "  </parent>\n"
-                        + "  <artifactId>PROJECT</artifactId>\n"
-                        + "</project>";
-        String actual = transform( input );
-        assertThat( actual ).and( expected ).ignoreWhitespace().areIdentical();
-    }
-    
-    @Test
-    public void testMe() throws Exception {
-        String input = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" + 
-            "<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\r\n" +
-            "       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\r\n" +
-            "       xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0\r\n" +
-            "                           http://maven.apache.org/xsd/maven-4.0.0.xsd\">\r\n" + 
-            "  <modelVersion>4.0.0</modelVersion>\r\n" + 
-            "  <groupId>org.sonatype.mavenbook.multispring</groupId>\r\n" + 
-            "  <artifactId>parent</artifactId>\r\n" + 
-            "  <version>0.9-${changelist}-SNAPSHOT</version>\r\n" + 
-            "  <packaging>pom</packaging>\r\n" + 
-            "  <name>Multi-Spring Chapter Parent Project</name>\r\n" + 
-            "  <modules>\r\n" + 
-            "    <module>simple-parent</module>\r\n" + 
-            "  </modules>\r\n" + 
-            "  \r\n" + 
-            "  <pluginRepositories>\r\n" + 
-            "    <pluginRepository>\r\n" + 
-            "      <id>apache.snapshots</id>\r\n" + 
-            "      <url>http://repository.apache.org/snapshots/</url>\r\n" + 
-            "    </pluginRepository>\r\n" + 
-            "  </pluginRepositories>\r\n" + 
-            "</project>";
-        String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" + 
-            "<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\r\n" +
-            "       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\r\n" +
-            "       xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0\r\n" +
-            "                           http://maven.apache.org/xsd/maven-4.0.0.xsd\">\r\n" + 
-            "  <modelVersion>4.0.0</modelVersion>\r\n" + 
-            "  <groupId>org.sonatype.mavenbook.multispring</groupId>\r\n" + 
-            "  <artifactId>parent</artifactId>\r\n" + 
-            "  <version>0.9-CL-SNAPSHOT</version>\r\n" + 
-            "  <packaging>pom</packaging>\r\n" + 
-            "  <name>Multi-Spring Chapter Parent Project</name>\r\n" + 
-            "  \r\n" + 
-            "  <pluginRepositories>\r\n" + 
-            "    <pluginRepository>\r\n" + 
-            "      <id>apache.snapshots</id>\r\n" + 
-            "      <url>http://repository.apache.org/snapshots/</url>\r\n" + 
-            "    </pluginRepository>\r\n" + 
-            "  </pluginRepositories>\r\n" + 
-            "</project>";
-        String actual = transform( input );
-        assertThat( actual ).and( expected ).ignoreWhitespace().areIdentical();
-    }
-    
-    
-
-}
diff --git a/maven-xml/src/test/java/org/apache/maven/xml/sax/LexicalHandlerVerifier.java b/maven-xml/src/test/java/org/apache/maven/xml/sax/LexicalHandlerVerifier.java
new file mode 100644
index 0000000..49eb772
--- /dev/null
+++ b/maven-xml/src/test/java/org/apache/maven/xml/sax/LexicalHandlerVerifier.java
@@ -0,0 +1,277 @@
+package org.apache.maven.xml.sax;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 static org.xmlunit.assertj.XmlAssert.assertThat;
+
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.Arrays;
+
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Source;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.sax.SAXResult;
+import javax.xml.transform.sax.SAXSource;
+import javax.xml.transform.sax.SAXTransformerFactory;
+import javax.xml.transform.sax.TransformerHandler;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+
+import org.apache.maven.xml.Factories;
+import org.apache.maven.xml.sax.filter.AbstractSAXFilter;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLFilter;
+import org.xml.sax.XMLReader;
+import org.xml.sax.ext.LexicalHandler;
+import org.xml.sax.helpers.XMLFilterImpl;
+
+/**
+ * Some tests to help understand the chain of events regarding XMLFilters, XMLReaders, LexicalHandlers and Transformers
+ * 
+ * @author Robert Scholte
+ *
+ */
+public class LexicalHandlerVerifier
+{
+    private static final String SAX_PROPERTIES_LEXICAL_HANDLER = "http://xml.org/sax/properties/lexical-handler";
+    
+    @Rule
+    public ExpectedException expectedException = ExpectedException.none();
+
+    @Test
+    public void parseXmlReader() throws Exception
+    {
+        expectedException.expect( UnsupportedOperationException.class );
+        expectedException.expectMessage( "LexicalHandlerVerifier" );
+
+        XMLReader reader = Factories.newXMLReader();
+        reader.setProperty( SAX_PROPERTIES_LEXICAL_HANDLER, new UnsupportedOperationExceptionLexicalHandler() );
+
+        InputSource inputSource = new InputSource( new StringReader( "<root><!-- COMMENT --></root>" ) ); 
+        reader.parse( inputSource );
+    }
+
+    @Test
+    public void parseXmlFilter() throws Exception
+    {
+        expectedException.expect( UnsupportedOperationException.class );
+        expectedException.expectMessage( "LexicalHandlerVerifier" );
+
+        XMLReader reader = Factories.newXMLReader();
+        reader.setProperty( SAX_PROPERTIES_LEXICAL_HANDLER, new UnsupportedOperationExceptionLexicalHandler() );
+        
+        XMLFilter filter = new XMLFilterImpl( reader );
+
+        InputSource inputSource = new InputSource( new StringReader( "<root><!-- COMMENT --></root>" ) ); 
+        filter.parse( inputSource );
+    }
+    
+    @Test
+    public void transformXmlReader() throws Exception
+    {
+        Writer writer = new StringWriter();
+        StreamResult result = new StreamResult( writer );
+
+        SAXTransformerFactory transformerFactory = (SAXTransformerFactory) Factories.newTransformerFactory();
+        TransformerHandler transformerHandler = transformerFactory.newTransformerHandler();
+        transformerHandler.setResult( result );
+        Transformer transformer = transformerFactory.newTransformer();
+        transformer.setOutputProperty( OutputKeys.OMIT_XML_DECLARATION, "yes" );
+        
+        Source xmlSource = new StreamSource( new StringReader( "<root><!--COMMENT--></root>" ) );
+        
+        SAXResult transformResult = new SAXResult( transformerHandler );
+        transformResult.setLexicalHandler( new SortCommentLexicalHandler( transformerHandler ) );
+        transformer.transform( xmlSource, transformResult );
+        
+        assertThat( writer.toString() ).and( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+            + "<root><!--CEMMNOT--></root>" ).areIdentical();
+    }
+
+    @Test
+    public void transformXmlFilter() throws Exception
+    {
+        Writer writer = new StringWriter();
+        StreamResult result = new StreamResult( writer );
+
+        SAXTransformerFactory transformerFactory = (SAXTransformerFactory) Factories.newTransformerFactory();
+        TransformerHandler transformerHandler = transformerFactory.newTransformerHandler();
+        transformerHandler.getTransformer().setOutputProperty( OutputKeys.OMIT_XML_DECLARATION, "yes" );
+        transformerHandler.setResult( result );
+        Transformer transformer = transformerFactory.newTransformer();
+
+        XMLReader reader = Factories.newXMLReader();
+        // in wrong order result will be CONNENT -->
+        AbstractSAXFilter filter2 = new M2NLexicalXMLFilter();
+        filter2.setParent( reader );
+        filter2.setLexicalHandler( transformerHandler );
+        
+        AbstractSAXFilter filter1 = new N2CLexicalXMLFilter( filter2 );
+
+        SAXSource transformSource = new SAXSource( filter1, new InputSource( new StringReader( "<root><!-- COMMENT --></root>" ) ) );
+
+        SAXResult transformResult = new SAXResult( transformerHandler );
+        transformResult.setLexicalHandler( filter1 );
+        transformer.transform( transformSource, transformResult );
+
+        assertThat( writer.toString() ).and( "<root><!-- CONNECT --></root>" ).areIdentical();
+    }
+
+    /**
+     * A LexicalHandler that'll throw an UnsupportedOperationException on every call.
+     * 
+     * @author Robert Scholte
+     */
+    static class UnsupportedOperationExceptionLexicalHandler implements LexicalHandler
+    {
+        @Override
+        public void startDTD( String name, String publicId, String systemId )
+            throws SAXException
+        {
+            throw new UnsupportedOperationException( "LexicalHandlerVerifier" );
+        }
+
+        @Override
+        public void endDTD()
+            throws SAXException
+        {
+            throw new UnsupportedOperationException( "LexicalHandlerVerifier" );
+        }
+
+        @Override
+        public void startEntity( String name )
+            throws SAXException
+        {
+            throw new UnsupportedOperationException( "LexicalHandlerVerifier" );
+        }
+
+        @Override
+        public void endEntity( String name )
+            throws SAXException
+        {
+            throw new UnsupportedOperationException( "LexicalHandlerVerifier" );
+        }
+
+        @Override
+        public void startCDATA()
+            throws SAXException
+        {
+            throw new UnsupportedOperationException( "LexicalHandlerVerifier" );
+        }
+
+        @Override
+        public void endCDATA()
+            throws SAXException
+        {
+            throw new UnsupportedOperationException( "LexicalHandlerVerifier" );
+        }
+
+        @Override
+        public void comment( char[] ch, int start, int length )
+            throws SAXException
+        {
+            throw new UnsupportedOperationException( "LexicalHandlerVerifier" );
+        }
+    }
+    
+    /**
+     * Sorts the comment chars,will throw an UnsupportedOperationException on every other method
+     * 
+     * @author Robert Scholte
+     *
+     */
+    static class SortCommentLexicalHandler extends UnsupportedOperationExceptionLexicalHandler
+    {
+        private final LexicalHandler lexicalHandler;
+        
+        public SortCommentLexicalHandler( LexicalHandler lexicalHandler )
+        {
+            this.lexicalHandler = lexicalHandler;
+        }
+
+
+        @Override
+        public void comment( char[] ch, int start, int length )
+            throws SAXException
+        {
+            char[] chars = new String( ch, start, length ).toCharArray();
+            Arrays.sort(chars);
+            lexicalHandler.comment( chars, 0, chars.length );
+        }
+    }
+    
+    /**
+     * AbstractSAXFilter implements both XMLReader and LexicalHandler
+     * 
+     * @author Robert Scholte
+     *
+     */
+    static class N2CLexicalXMLFilter extends AbstractSAXFilter
+    {
+        public N2CLexicalXMLFilter()
+        {
+            super( null );
+        }
+        
+        public <T extends XMLReader & LexicalHandler> N2CLexicalXMLFilter( T parent )
+        {
+            super( parent );
+        }
+
+        @Override
+        public void comment( char[] ch, int start, int length )
+            throws SAXException
+        {
+            super.comment( new String( ch, start, length ).replace( 'N', 'C' ).toCharArray(), start, length );
+        }
+    }
+    
+    /**
+     * AbstractSAXFilter implements both XMLReader and LexicalHandler
+     * 
+     * @author Robert Scholte
+     *
+     */
+    static class M2NLexicalXMLFilter extends AbstractSAXFilter
+    {
+        public M2NLexicalXMLFilter()
+        {
+            super( null );
+        }
+        
+        public <T extends XMLReader & LexicalHandler> M2NLexicalXMLFilter( T parent )
+        {
+            super( parent );
+        }
+
+        @Override
+        public void comment( char[] ch, int start, int length )
+            throws SAXException
+        {
+            super.comment( new String( ch, start, length ).replace( 'M', 'N' ).toCharArray(), start, length );
+        }
+    }
+}
diff --git a/maven-core/src/main/java/org/apache/maven/xml/filter/BuildPomXMLFilter.java b/maven-xml/src/test/java/org/apache/maven/xml/sax/SAXEventUtilsTest.java
similarity index 63%
rename from maven-core/src/main/java/org/apache/maven/xml/filter/BuildPomXMLFilter.java
rename to maven-xml/src/test/java/org/apache/maven/xml/sax/SAXEventUtilsTest.java
index 60f0c49..02e55dc 100644
--- a/maven-core/src/main/java/org/apache/maven/xml/filter/BuildPomXMLFilter.java
+++ b/maven-xml/src/test/java/org/apache/maven/xml/sax/SAXEventUtilsTest.java
@@ -1,4 +1,4 @@
-package org.apache.maven.xml.filter;
+package org.apache.maven.xml.sax;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -19,26 +19,25 @@ package org.apache.maven.xml.filter;
  * under the License.
  */
 
-import org.xml.sax.XMLReader;
-import org.xml.sax.helpers.XMLFilterImpl;
+import static org.junit.Assert.assertThat;
 
-/**
- * Filter to adjust pom on filesystem before being processed for effective pom.
- * 
- * @author Robert Scholte
- * @since 3.7.0
- */
-public class BuildPomXMLFilter extends XMLFilterImpl
+import org.apache.maven.xml.sax.SAXEventUtils;
+
+import static org.hamcrest.CoreMatchers.is;
+
+import org.junit.Test;
+
+public class SAXEventUtilsTest
 {
-    public BuildPomXMLFilter()
+    @Test
+    public void replaceWithNamespace()
     {
-        super();
+        assertThat( SAXEventUtils.renameQName( "org:bar", "com" ), is( "org:com" ) );
     }
 
-    public BuildPomXMLFilter( XMLReader parent )
+    @Test
+    public void replaceWithoutNamespace()
     {
-        super( parent );
+        assertThat( SAXEventUtils.renameQName( "bar", "com" ), is( "com" ) );
     }
-
-    
 }
diff --git a/maven-xml/src/test/java/org/apache/maven/xml/filter/AbstractXMLFilterTests.java b/maven-xml/src/test/java/org/apache/maven/xml/sax/filter/AbstractXMLFilterTests.java
similarity index 57%
rename from maven-xml/src/test/java/org/apache/maven/xml/filter/AbstractXMLFilterTests.java
rename to maven-xml/src/test/java/org/apache/maven/xml/sax/filter/AbstractXMLFilterTests.java
index 0d9b779..4fa3b0d 100644
--- a/maven-xml/src/test/java/org/apache/maven/xml/filter/AbstractXMLFilterTests.java
+++ b/maven-xml/src/test/java/org/apache/maven/xml/sax/filter/AbstractXMLFilterTests.java
@@ -1,4 +1,4 @@
-package org.apache.maven.xml.filter;
+package org.apache.maven.xml.sax.filter;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -28,14 +28,17 @@ import javax.xml.parsers.ParserConfigurationException;
 import javax.xml.transform.OutputKeys;
 import javax.xml.transform.Transformer;
 import javax.xml.transform.TransformerException;
-import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.sax.SAXResult;
 import javax.xml.transform.sax.SAXSource;
+import javax.xml.transform.sax.SAXTransformerFactory;
+import javax.xml.transform.sax.TransformerHandler;
 import javax.xml.transform.stream.StreamResult;
 
 import org.apache.maven.xml.Factories;
+import org.apache.maven.xml.sax.filter.AbstractSAXFilter;
 import org.xml.sax.InputSource;
 import org.xml.sax.SAXException;
-import org.xml.sax.XMLFilter;
+import org.xml.sax.XMLReader;
 
 public abstract class AbstractXMLFilterTests
 {
@@ -44,16 +47,26 @@ public abstract class AbstractXMLFilterTests
         super();
     }
     
-    protected abstract XMLFilter getFilter() throws TransformerException, SAXException, ParserConfigurationException;
+    protected abstract AbstractSAXFilter getFilter() throws TransformerException, SAXException, ParserConfigurationException;
     
-    private void setParent( XMLFilter filter ) throws SAXException, ParserConfigurationException
+    private void setParent( AbstractSAXFilter filter ) throws SAXException, ParserConfigurationException
     {
         if( filter.getParent() == null )
         {
-            filter.setParent( Factories.newXMLReader() );
+            XMLReader r = Factories.newXMLReader();
+            
+            filter.setParent( r );
             filter.setFeature( "http://xml.org/sax/features/namespaces", true );
         }
     }
+    
+    protected String omitXmlDeclaration() {
+        return "yes";
+    }
+    
+    protected String indentAmount() {
+        return null;
+    }
 
     protected String transform( String input )
         throws TransformerException, SAXException, ParserConfigurationException
@@ -63,32 +76,43 @@ public abstract class AbstractXMLFilterTests
 
     protected String transform( Reader input ) throws TransformerException, SAXException, ParserConfigurationException
     {
-        XMLFilter filter = getFilter();
+        AbstractSAXFilter filter = getFilter();
         setParent( filter );
+
         return transform( input, filter );
     }
     
-    protected String transform( String input, XMLFilter filter ) 
+    protected String transform( String input, AbstractSAXFilter filter ) 
         throws TransformerException, SAXException, ParserConfigurationException
     {
         setParent( filter );
         return transform( new StringReader( input ), filter );
     }
 
-    protected String transform( Reader input, XMLFilter filter )
+    protected String transform( Reader input, AbstractSAXFilter filter )
         throws TransformerException, SAXException, ParserConfigurationException
     {
-
         Writer writer = new StringWriter();
         StreamResult result = new StreamResult( writer );
 
-        TransformerFactory transformerFactory = Factories.newTransformerFactory();
+        SAXTransformerFactory transformerFactory = (SAXTransformerFactory) Factories.newTransformerFactory();
+        TransformerHandler transformerHandler = transformerFactory.newTransformerHandler();
+        filter.setLexicalHandler( transformerHandler );
+        transformerHandler.getTransformer().setOutputProperty( OutputKeys.OMIT_XML_DECLARATION, omitXmlDeclaration() );
+        if ( indentAmount() != null )
+        {
+            transformerHandler.getTransformer().setOutputProperty( OutputKeys.INDENT, "yes" );
+            transformerHandler.getTransformer().setOutputProperty( "{http://xml.apache.org/xslt}indent-amount",
+                                                                   indentAmount() );
+        }
+        transformerHandler.setResult( result );
         Transformer transformer = transformerFactory.newTransformer();
-        transformer.setOutputProperty( OutputKeys.OMIT_XML_DECLARATION, "yes" );
 
         SAXSource transformSource = new SAXSource( filter, new InputSource( input ) );
 
-        transformer.transform( transformSource, result );
+        SAXResult transformResult = new SAXResult( transformerHandler );
+        transformResult.setLexicalHandler( filter );
+        transformer.transform( transformSource, transformResult );
 
         return writer.toString();
     }
diff --git a/maven-xml/src/test/java/org/apache/maven/xml/sax/filter/ConsumerPomXMLFilterTest.java b/maven-xml/src/test/java/org/apache/maven/xml/sax/filter/ConsumerPomXMLFilterTest.java
new file mode 100644
index 0000000..50abfc8
--- /dev/null
+++ b/maven-xml/src/test/java/org/apache/maven/xml/sax/filter/ConsumerPomXMLFilterTest.java
@@ -0,0 +1,252 @@
+package org.apache.maven.xml.sax.filter;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 static org.xmlunit.assertj.XmlAssert.assertThat;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Optional;
+import java.util.function.Function;
+
+import javax.inject.Provider;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.TransformerConfigurationException;
+
+import org.apache.maven.xml.sax.filter.AbstractSAXFilter;
+import org.apache.maven.xml.sax.filter.BuildPomXMLFilterFactory;
+import org.apache.maven.xml.sax.filter.ConsumerPomXMLFilter;
+import org.apache.maven.xml.sax.filter.ConsumerPomXMLFilterFactory;
+import org.apache.maven.xml.sax.filter.DependencyKey;
+import org.apache.maven.xml.sax.filter.RelativeProject;
+import org.junit.Test;
+import org.xml.sax.SAXException;
+
+public class ConsumerPomXMLFilterTest extends AbstractXMLFilterTests
+{
+    @Override
+        protected String omitXmlDeclaration()
+        {
+            return "no";
+        }
+    
+    @Override
+    protected AbstractSAXFilter getFilter() throws SAXException, ParserConfigurationException, TransformerConfigurationException
+    {
+        final BuildPomXMLFilterFactory buildPomXMLFilterFactory = new BuildPomXMLFilterFactory()
+        {
+            @Override
+            protected Optional<String> getSha1()
+            {
+                return Optional.empty();
+            }
+            
+            @Override
+            protected Optional<String> getRevision()
+            {
+                return Optional.empty();
+            }
+            
+            @Override
+            protected Optional<String> getChangelist()
+            {
+                return Optional.of( "CL" );
+            }
+            
+            @Override
+            protected Function<Path, Optional<RelativeProject>> getRelativePathMapper()
+            {
+                return null;
+            }
+            
+            @Override
+            protected Function<DependencyKey, String> getDependencyKeyToVersionMapper()
+            {
+                return null;
+            }
+        };
+        
+        Provider<BuildPomXMLFilterFactory> provider = new Provider<BuildPomXMLFilterFactory>()
+        {
+
+            @Override
+            public BuildPomXMLFilterFactory get()
+            {
+                return buildPomXMLFilterFactory;
+            }
+        };
+        
+        ConsumerPomXMLFilter filter = new ConsumerPomXMLFilterFactory( provider )
+        {
+        }.get( Paths.get( "pom.xml" ) );
+        filter.setFeature( "http://xml.org/sax/features/namespaces", true );
+        return filter;
+    }
+    
+    @Test
+    public void aggregatorWithParent() throws Exception {
+        String input = "<project>\n"
+                     + "  <parent>\n"
+                     + "    <groupId>GROUPID</groupId>\n"
+                     + "    <artifactId>PARENT</artifactId>\n"
+                     + "    <version>VERSION</version>\n"
+                     + "    <relativePath>../pom.xml</relativePath>\n"
+                     + "  </parent>\n"
+                     + "  <artifactId>PROJECT</artifactId>\n"
+                     + "  <modules>\n"
+                     + "    <module>ab</module>\n"
+                     + "    <module>../cd</module>\n"
+                     + "  </modules>\n"
+                     + "</project>";
+        String expected = "<project>\n"
+                        + "  <parent>\n"
+                        + "    <groupId>GROUPID</groupId>\n"
+                        + "    <artifactId>PARENT</artifactId>\n"
+                        + "    <version>VERSION</version>\n"
+                        + "  </parent>\n"
+                        + "  <artifactId>PROJECT</artifactId>\n"
+                        + "</project>";
+        String actual = transform( input );
+        assertThat( actual ).and( expected ).ignoreWhitespace().areIdentical();
+    }
+    
+    @Test
+    public void aggregatorWithCliFriendlyVersion() throws Exception {
+        String input = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + 
+            "<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n" +
+            "       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" +
+            "       xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0\n" +
+            "                           http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n" + 
+            "  <modelVersion>4.0.0</modelVersion>\n" + 
+            "  <groupId>org.sonatype.mavenbook.multispring</groupId>\n" + 
+            "  <artifactId>parent</artifactId>\n" + 
+            "  <version>0.9-${changelist}-SNAPSHOT</version>\n" + 
+            "  <packaging>pom</packaging>\n" + 
+            "  <name>Multi-Spring Chapter Parent Project</name>\n" + 
+            "  <modules>\n" + 
+            "    <module>simple-parent</module>\n" + 
+            "  </modules>\n" + 
+            "  \n" + 
+            "  <pluginRepositories>\n" + 
+            "    <pluginRepository>\n" + 
+            "      <id>apache.snapshots</id>\n" + 
+            "      <url>http://repository.apache.org/snapshots/</url>\n" + 
+            "    </pluginRepository>\n" + 
+            "  </pluginRepositories>\n" + 
+            "</project>";
+        String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + 
+            "<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n" +
+            "       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" +
+            "       xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0\n" +
+            "                           http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n" + 
+            "  <modelVersion>4.0.0</modelVersion>\n" + 
+            "  <groupId>org.sonatype.mavenbook.multispring</groupId>\n" + 
+            "  <artifactId>parent</artifactId>\n" + 
+            "  <version>0.9-CL-SNAPSHOT</version>\n" + 
+            "  <packaging>pom</packaging>\n" + 
+            "  <name>Multi-Spring Chapter Parent Project</name>\n" + 
+            "  \n" + 
+            "  <pluginRepositories>\n" + 
+            "    <pluginRepository>\n" + 
+            "      <id>apache.snapshots</id>\n" + 
+            "      <url>http://repository.apache.org/snapshots/</url>\n" + 
+            "    </pluginRepository>\n" + 
+            "  </pluginRepositories>\n" + 
+            "</project>";
+        String actual = transform( input );
+        assertThat( actual ).and( expected ).ignoreWhitespace().areIdentical();
+    }
+    
+    @Test
+    public void licenseHeader() throws Exception {
+        String input = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + 
+            "\n" + 
+            "<!--\n" + 
+            "Licensed to the Apache Software Foundation (ASF) under one\n" + 
+            "or more contributor license agreements.  See the NOTICE file\n" + 
+            "distributed with this work for additional information\n" + 
+            "regarding copyright ownership.  The ASF licenses this file\n" + 
+            "to you under the Apache License, Version 2.0 (the\n" + 
+            "\"License\"); you may not use this file except in compliance\n" + 
+            "with the License.  You may obtain a copy of the License at\n" + 
+            "\n" + 
+            "    http://www.apache.org/licenses/LICENSE-2.0\n" + 
+            "\n" + 
+            "Unless required by applicable law or agreed to in writing,\n" + 
+            "software distributed under the License is distributed on an\n" + 
+            "\"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n" + 
+            "KIND, either express or implied.  See the License for the\n" + 
+            "specific language governing permissions and limitations\n" + 
+            "under the License.\n" + 
+            "-->\n" + 
+            "\n" + 
+            "<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n" + 
+            "  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" + 
+            "  xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n" + 
+            "  <modelVersion>4.0.0</modelVersion>\n" + 
+            "  <parent>\n" + 
+            "    <groupId>org.apache.maven</groupId>\n" + 
+            "    <artifactId>maven</artifactId>\n" + 
+            "    <version>3.7.0-SNAPSHOT</version>\n" + 
+            "  </parent>\n" + 
+            "  <artifactId>maven-xml</artifactId>\n" + 
+            "  <name>Maven XML</name>\n" + 
+            "  \n" + 
+            "  <properties>\n" + 
+            "    <maven.compiler.source>1.8</maven.compiler.source>\n" + 
+            "    <maven.compiler.target>1.8</maven.compiler.target>\n" + 
+            "  </properties>\n" + 
+            "\n" + 
+            "  <build>\n" + 
+            "    <plugins>\n" + 
+            "      <plugin>\n" + 
+            "        <groupId>org.codehaus.mojo</groupId>\n" + 
+            "        <artifactId>animal-sniffer-maven-plugin</artifactId>\n" + 
+            "        <configuration>\n" + 
+            "          <signature>\n" + 
+            "            <groupId>org.codehaus.mojo.signature</groupId>\n" + 
+            "            <artifactId>java18</artifactId>\n" + 
+            "            <version>1.0</version>\n" + 
+            "          </signature>\n" + 
+            "        </configuration>\n" + 
+            "      </plugin>\n" + 
+            "    </plugins>\n" + 
+            "  </build>\n" + 
+            "  \n" + 
+            "  <dependencies>\n" + 
+            "    <dependency>\n" + 
+            "      <groupId>javax.inject</groupId>\n" + 
+            "      <artifactId>javax.inject</artifactId>\n" + 
+            "      <optional>true</optional>\n" + 
+            "    </dependency>\n" + 
+            "    <dependency>\n" + 
+            "      <groupId>org.xmlunit</groupId>\n" + 
+            "      <artifactId>xmlunit-assertj</artifactId>\n" + 
+            "      <scope>test</scope>\n" + 
+            "    </dependency>\n" + 
+            "  </dependencies>\n" + 
+            "</project>";
+        String expected = input;
+        
+        String actual = transform( input );
+        assertThat( actual ).and( expected ).areIdentical();
+    }
+
+}
diff --git a/maven-xml/src/test/java/org/apache/maven/xml/filter/ModulesXMLFilterTest.java b/maven-xml/src/test/java/org/apache/maven/xml/sax/filter/ModulesXMLFilterTest.java
similarity index 67%
rename from maven-xml/src/test/java/org/apache/maven/xml/filter/ModulesXMLFilterTest.java
rename to maven-xml/src/test/java/org/apache/maven/xml/sax/filter/ModulesXMLFilterTest.java
index 63880ed..a3477df 100644
--- a/maven-xml/src/test/java/org/apache/maven/xml/filter/ModulesXMLFilterTest.java
+++ b/maven-xml/src/test/java/org/apache/maven/xml/sax/filter/ModulesXMLFilterTest.java
@@ -1,4 +1,4 @@
-package org.apache.maven.xml.filter;
+package org.apache.maven.xml.sax.filter;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -21,32 +21,19 @@ package org.apache.maven.xml.filter;
 
 import static org.xmlunit.assertj.XmlAssert.assertThat;
 
-import javax.xml.parsers.SAXParserFactory;
-
-import org.junit.Before;
+import org.apache.maven.xml.sax.filter.ModulesXMLFilter;
 import org.junit.Test;
-import org.xml.sax.XMLFilter;
-import org.xml.sax.XMLReader;
 
 public class ModulesXMLFilterTest extends AbstractXMLFilterTests {
 
-	private ModulesXMLFilter filter;
-
 	@Override
-	protected XMLFilter getFilter()
+	protected ModulesXMLFilter getFilter()
 	{
 	    return new ModulesXMLFilter();
 	}
 	
-	@Before
-	public void setup() throws Exception {
-        XMLReader xmlReader = SAXParserFactory.newInstance().newSAXParser().getXMLReader();
-        filter = new ModulesXMLFilter( xmlReader );
-        filter.setFeature( "http://xml.org/sax/features/namespaces", true );
-	}
-	
 	@Test
-	public void testEmptyModules() throws Exception {
+	public void emptyModules() throws Exception {
 		String input = "<project><modules/></project>";
         String expected = "<project/>";
         String actual = transform( input );
@@ -54,7 +41,7 @@ public class ModulesXMLFilterTest extends AbstractXMLFilterTests {
 	}
 
 	@Test
-	public void testSetOfModules() throws Exception {
+	public void setOfModules() throws Exception {
 		String input = "<project><modules>"
 				+ "<module>ab</module>"
 				+ "<module>../cd</module>"
@@ -65,10 +52,25 @@ public class ModulesXMLFilterTest extends AbstractXMLFilterTests {
 	}
 	
 	@Test
-    public void testNoModules() throws Exception {
+    public void noModules() throws Exception {
         String input = "<project><name>NAME</name></project>";
         String expected = input;
         String actual = transform( input );
         assertThat( actual ).and( expected ).areIdentical();
     }
+	
+	@Test
+	public void comment() throws Exception {
+	    
+	    String input = "<project><!--before--><modules>"
+                        + "<!--pre-in-->"
+	                    + "<module><!--in-->ab</module>"
+	                    + "<module>../cd</module>"
+                        + "<!--post-in-->"
+	                    + "</modules>"
+	                    + "<!--after--></project>";
+	    String expected = "<project><!--before--><!--after--></project>";
+	    String actual = transform( input );
+	    assertThat( actual ).and( expected ).areIdentical();
+	}
 }
diff --git a/maven-xml/src/test/java/org/apache/maven/xml/filter/ParentXMLFilterTest.java b/maven-xml/src/test/java/org/apache/maven/xml/sax/filter/ParentXMLFilterTest.java
similarity index 86%
rename from maven-xml/src/test/java/org/apache/maven/xml/filter/ParentXMLFilterTest.java
rename to maven-xml/src/test/java/org/apache/maven/xml/sax/filter/ParentXMLFilterTest.java
index aa70b0d..8f25c10 100644
--- a/maven-xml/src/test/java/org/apache/maven/xml/filter/ParentXMLFilterTest.java
+++ b/maven-xml/src/test/java/org/apache/maven/xml/sax/filter/ParentXMLFilterTest.java
@@ -1,4 +1,4 @@
-package org.apache.maven.xml.filter;
+package org.apache.maven.xml.sax.filter;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -27,14 +27,15 @@ import java.util.Optional;
 import javax.xml.parsers.ParserConfigurationException;
 import javax.xml.transform.TransformerException;
 
+import org.apache.maven.xml.sax.filter.ParentXMLFilter;
+import org.apache.maven.xml.sax.filter.RelativeProject;
 import org.junit.Test;
 import org.xml.sax.SAXException;
-import org.xml.sax.XMLFilter;
 
 public class ParentXMLFilterTest extends AbstractXMLFilterTests
 {
     @Override
-    protected XMLFilter getFilter()
+    protected ParentXMLFilter getFilter()
         throws TransformerException, SAXException, ParserConfigurationException
     {
         ParentXMLFilter filter = new ParentXMLFilter( x -> Optional.of( new RelativeProject( "GROUPID", 
@@ -162,24 +163,27 @@ public class ParentXMLFilterTest extends AbstractXMLFilterTests
 
         assertEquals( expected, actual );
     }
-
     
     @Test
-    public void testIndent() throws Exception
+    public void comment() throws Exception
     {
-        String input = "<parent>\n"
-                    + "  <groupId>GROUPID</groupId>\n"
-                    + "  <artifactId>ARTIFACTID</artifactId>\n"
-                    + "  </parent>";
-        // transformer is responsible for line separator and indents
-        String expected = "<parent>" + System.lineSeparator()
-                    + "  <groupId>GROUPID</groupId>" + System.lineSeparator()
-                    + "  <artifactId>ARTIFACTID</artifactId>" + System.lineSeparator()
-                    + "  <version>1.0.0</version>" + System.lineSeparator()
-                    + "</parent>";
+        String input = "<project><!--before--><parent>"
+                    + "<groupId>GROUPID</groupId>"
+                    + "<artifactId>ARTIFACTID</artifactId>"
+                    + "<!--version here-->"
+                    + "</parent>"
+                    + "</project>";
+        String expected = "<project><!--before--><parent>"
+                        + "<groupId>GROUPID</groupId>"
+                        + "<artifactId>ARTIFACTID</artifactId>"
+                        + "<!--version here-->"
+                        + "<version>1.0.0</version>"
+                        + "</parent>"
+                        + "</project>";
 
         String actual = transform( input );
 
         assertEquals( expected, actual );
     }
+    
 }
diff --git a/maven-xml/src/test/java/org/apache/maven/xml/filter/ReactorDependencyXMLFilterTest.java b/maven-xml/src/test/java/org/apache/maven/xml/sax/filter/ReactorDependencyXMLFilterTest.java
similarity index 66%
rename from maven-xml/src/test/java/org/apache/maven/xml/filter/ReactorDependencyXMLFilterTest.java
rename to maven-xml/src/test/java/org/apache/maven/xml/sax/filter/ReactorDependencyXMLFilterTest.java
index 6a5998e..9cf713c 100644
--- a/maven-xml/src/test/java/org/apache/maven/xml/filter/ReactorDependencyXMLFilterTest.java
+++ b/maven-xml/src/test/java/org/apache/maven/xml/sax/filter/ReactorDependencyXMLFilterTest.java
@@ -1,4 +1,4 @@
-package org.apache.maven.xml.filter;
+package org.apache.maven.xml.sax.filter;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -19,19 +19,19 @@ package org.apache.maven.xml.filter;
  * under the License.
  */
 
-import static org.junit.Assert.*;
+import static org.xmlunit.assertj.XmlAssert.*;
 
 import javax.xml.parsers.ParserConfigurationException;
 import javax.xml.transform.TransformerException;
 
+import org.apache.maven.xml.sax.filter.ReactorDependencyXMLFilter;
 import org.junit.Test;
 import org.xml.sax.SAXException;
-import org.xml.sax.XMLFilter;
 
 public class ReactorDependencyXMLFilterTest extends AbstractXMLFilterTests
 {
     @Override
-    protected XMLFilter getFilter()
+    protected ReactorDependencyXMLFilter getFilter()
         throws TransformerException, SAXException, ParserConfigurationException
     {
         return new ReactorDependencyXMLFilter( r -> "1.0.0" );
@@ -49,13 +49,13 @@ public class ReactorDependencyXMLFilterTest extends AbstractXMLFilterTests
         
         String actual = transform( input );
         
-        assertEquals( expected, actual );
+        assertThat( actual ).isEqualTo( expected );
     }
 
     @Test
     public void testManagedDependency() throws Exception
     {
-        XMLFilter filter = new ReactorDependencyXMLFilter( r -> null );
+        ReactorDependencyXMLFilter filter = new ReactorDependencyXMLFilter( r -> null );
         
         String input = "<dependency>"
             + "<groupId>GROUPID</groupId>"
@@ -65,7 +65,7 @@ public class ReactorDependencyXMLFilterTest extends AbstractXMLFilterTests
         
         String actual = transform( input, filter );
         
-        assertEquals( expected, actual );
+        assertThat( actual ).isEqualTo( expected );
     }
 
     @Test
@@ -83,7 +83,27 @@ public class ReactorDependencyXMLFilterTest extends AbstractXMLFilterTests
         
         String actual = transform( input );
         
-        assertEquals( expected, actual );
+        assertThat( actual ).isEqualTo( expected );
+    }
+
+    @Test
+    public void testReactorDependencyLF() throws Exception
+    {
+        String input = "<dependency>\n"
+                        + "  <groupId>GROUPID</groupId>\n"
+                        + "  <artifactId>ARTIFACTID</artifactId>\n"
+                        + "  <!-- include version here --> " 
+                        + "</dependency>";
+        String expected = "<dependency>\n"
+                        + "  <groupId>GROUPID</groupId>\n"
+                        + "  <artifactId>ARTIFACTID</artifactId>\n"
+                        + "  <!-- include version here -->\n" 
+                        + "  <version>1.0.0</version>\n"
+                        + "</dependency>";
+        
+        String actual = transform( input );
+        
+        assertThat( actual ).and( expected ).ignoreWhitespace().areIdentical();
     }
 
 }
diff --git a/maven-xml/src/test/java/org/apache/maven/xml/filter/RelativePathXMLFilterTest.java b/maven-xml/src/test/java/org/apache/maven/xml/sax/filter/RelativePathXMLFilterTest.java
similarity index 95%
rename from maven-xml/src/test/java/org/apache/maven/xml/filter/RelativePathXMLFilterTest.java
rename to maven-xml/src/test/java/org/apache/maven/xml/sax/filter/RelativePathXMLFilterTest.java
index 1fed1ca..00655b3 100644
--- a/maven-xml/src/test/java/org/apache/maven/xml/filter/RelativePathXMLFilterTest.java
+++ b/maven-xml/src/test/java/org/apache/maven/xml/sax/filter/RelativePathXMLFilterTest.java
@@ -1,4 +1,4 @@
-package org.apache.maven.xml.filter;
+package org.apache.maven.xml.sax.filter;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -21,13 +21,13 @@ package org.apache.maven.xml.filter;
 
 import static org.xmlunit.assertj.XmlAssert.assertThat;
 
+import org.apache.maven.xml.sax.filter.RelativePathXMLFilter;
 import org.junit.Test;
-import org.xml.sax.XMLFilter;
 
 public class RelativePathXMLFilterTest extends AbstractXMLFilterTests
 {
     @Override
-    protected XMLFilter getFilter()
+    protected RelativePathXMLFilter getFilter()
     {
         return new RelativePathXMLFilter();
     }
@@ -49,7 +49,6 @@ public class RelativePathXMLFilterTest extends AbstractXMLFilterTests
                            + "    <groupId>GROUPID</groupId>\n"
                            + "    <artifactId>PARENT</artifactId>\n"
                            + "    <version>VERSION</version>\n"
-                           + "    <relativePath/>\n"
                            + "  </parent>\n"
                            + "  <artifactId>PROJECT</artifactId>\n"
                            + "</project>";
@@ -78,7 +77,6 @@ public class RelativePathXMLFilterTest extends AbstractXMLFilterTests
                            + "    <groupId>GROUPID</groupId>\n"
                            + "    <artifactId>PARENT</artifactId>\n"
                            + "    <version>VERSION</version>\n"
-                           + "    <relativePath/>\n"
                            + "  </parent>\n"
                            + "  <artifactId>PROJECT</artifactId>\n"
                            + "</project>";
@@ -107,7 +105,6 @@ public class RelativePathXMLFilterTest extends AbstractXMLFilterTests
                            + "    <p:groupId>GROUPID</p:groupId>\n"
                            + "    <p:artifactId>PARENT</p:artifactId>\n"
                            + "    <p:version>VERSION</p:version>\n"
-                           + "    <p:relativePath/>\n"
                            + "  </p:parent>\n"
                            + "  <p:artifactId>PROJECT</p:artifactId>\n"
                            + "</p:project>";


Mime
View raw message