You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by gn...@apache.org on 2023/09/13 11:39:56 UTC

[maven] branch master updated: [MNG-7877] Attach the build POM and simplify build/consumer implementation (#1240)

This is an automated email from the ASF dual-hosted git repository.

gnodet pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven.git


The following commit(s) were added to refs/heads/master by this push:
     new c6380108b1 [MNG-7877] Attach the build POM and simplify build/consumer implementation (#1240)
c6380108b1 is described below

commit c6380108b11b91f46f6562cd7830da115ea2a049
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Wed Sep 13 13:39:50 2023 +0200

    [MNG-7877] Attach the build POM and simplify build/consumer implementation (#1240)
    
    .. at the cost of loosing xml formatting in the consumer POMs
---
 .../main/java/org/apache/maven/api/Repository.java |   5 +
 maven-bom/pom.xml                                  |   5 -
 maven-core/pom.xml                                 |   4 -
 .../ConsumerPomArtifactTransformer.java            | 206 +++++---
 .../transformation/TransformedArtifact.java        |  12 +-
 .../maven/project/DefaultProjectBuilder.java       |   4 +-
 .../DefaultConsumerPomXMLFilterFactory.java        |  34 --
 .../ConsumerPomArtifactTransformerTest.java        |  27 +-
 .../org/apache/maven/model/ModelBuilderTest.java   |  63 +++
 .../apache/maven/project/ProjectBuilderTest.java   |   5 +-
 .../test/resources/projects/transform/after.pom    |  37 +-
 .../test/resources/projects/transform/before.pom   |   9 +-
 .../projects/transform/consumer-after.pom          |  81 ----
 .../projects/transform/consumer-before.pom         |  87 ----
 .../test/resources/projects/tree/consumer/pom.xml  |  16 +
 .../src/test/resources/projects/tree/dep/pom.xml   |   9 +
 .../src/test/resources/projects/tree/pom.xml       |  10 +
 maven-model-builder/pom.xml                        |  18 +-
 .../building/AbstractModelSourceTransformer.java   |  27 --
 .../building/BuildModelSourceTransformer.java      | 158 ++++++-
 .../building/DefaultBuildPomXMLFilterFactory.java  | 102 ----
 .../maven/model/building/DefaultModelBuilder.java  | 113 +++--
 .../model/building/DefaultModelBuilderFactory.java |  13 +-
 .../model/building/DefaultModelProcessor.java      |   1 -
 .../building/DefaultModelSourceTransformer.java    |  38 --
 .../model/building/DefaultTransformerContext.java  |   5 +
 .../apache/maven/model/building/ModelBuilder.java  |   3 +
 .../model/building/ModelSourceTransformer.java     |  13 +-
 .../maven/model/building/TransformerContext.java   |  13 +-
 .../apache/maven/model/io/DefaultModelReader.java  |  57 +--
 .../org/apache/maven/model/io/ModelReader.java     |   6 -
 .../building/BuildModelSourceTransformerTest.java  | 150 ++++++
 .../DefaultInheritanceAssemblerTest.java           |  14 +-
 maven-model-transform/pom.xml                      |  64 ---
 .../transform/BuildToRawPomXMLFilterFactory.java   | 100 ----
 .../maven/model/transform/CiFriendlyXMLFilter.java |  74 ---
 .../maven/model/transform/FastForwardFilter.java   | 102 ----
 .../transform/ModelVersionDowngradeXMLFilter.java  | 113 -----
 .../model/transform/ModelVersionXMLFilter.java     |  84 ----
 .../maven/model/transform/ModulesXMLFilter.java    |  40 --
 .../maven/model/transform/ParentXMLFilter.java     | 151 ------
 .../RawToConsumerPomXMLFilterFactory.java          |  52 --
 .../transform/ReactorDependencyXMLFilter.java      |  97 ----
 .../model/transform/RelativePathXMLFilter.java     |  66 ---
 .../maven/model/transform/RelativeProject.java     |  49 --
 .../maven/model/transform/RootXMLFilter.java       |  60 ---
 .../model/transform/stax/BufferingParser.java      | 523 ---------------------
 .../model/transform/stax/NodeBufferingParser.java  |  72 ---
 .../maven/model/transform/stax/XmlUtils.java       | 187 --------
 maven-model-transform/src/site/apt/index.apt       |  73 ---
 maven-model-transform/src/site/site.xml            |  38 --
 .../model/transform/AbstractXMLFilterTests.java    |  49 --
 .../model/transform/CiFriendlyXMLFilterTest.java   |  53 ---
 .../model/transform/ConsumerPomXMLFilterTest.java  | 307 ------------
 .../model/transform/ModelVersionXMLFilterTest.java |  72 ---
 .../model/transform/ModulesXMLFilterTest.java      |  87 ----
 .../maven/model/transform/ParentXMLFilterTest.java | 331 -------------
 .../transform/ReactorDependencyXMLFilterTest.java  | 142 ------
 .../model/transform/RelativePathXMLFilterTest.java | 109 -----
 pom.xml                                            |   1 -
 src/mdo/model.vm                                   |  19 +
 src/mdo/reader-stax.vm                             |   1 +
 src/site/xdoc/index.xml                            |   1 -
 63 files changed, 733 insertions(+), 3729 deletions(-)

diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/Repository.java b/api/maven-api-core/src/main/java/org/apache/maven/api/Repository.java
index a46f7a1c7e..f437a6e873 100644
--- a/api/maven-api-core/src/main/java/org/apache/maven/api/Repository.java
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/Repository.java
@@ -33,6 +33,11 @@ import org.apache.maven.api.annotations.Nonnull;
 @Immutable
 public interface Repository {
 
+    /**
+     * The reserved id for Maven Central
+     */
+    String CENTRAL_ID = "central";
+
     /**
      * Gets the identifier of this repository.
      *
diff --git a/maven-bom/pom.xml b/maven-bom/pom.xml
index 3bc10c82f5..0e43dc90a9 100644
--- a/maven-bom/pom.xml
+++ b/maven-bom/pom.xml
@@ -132,11 +132,6 @@ under the License.
         <artifactId>maven-model-builder</artifactId>
         <version>${project.version}</version>
       </dependency>
-      <dependency>
-        <groupId>org.apache.maven</groupId>
-        <artifactId>maven-model-transform</artifactId>
-        <version>${project.version}</version>
-      </dependency>
       <dependency>
         <groupId>org.apache.maven</groupId>
         <artifactId>maven-plugin-api</artifactId>
diff --git a/maven-core/pom.xml b/maven-core/pom.xml
index aa134c54c7..50c87aaf77 100644
--- a/maven-core/pom.xml
+++ b/maven-core/pom.xml
@@ -71,10 +71,6 @@ under the License.
       <groupId>org.apache.maven</groupId>
       <artifactId>maven-model-builder</artifactId>
     </dependency>
-    <dependency>
-      <groupId>org.apache.maven</groupId>
-      <artifactId>maven-model-transform</artifactId>
-    </dependency>
     <dependency>
       <groupId>org.apache.maven</groupId>
       <artifactId>maven-resolver-provider</artifactId>
diff --git a/maven-core/src/main/java/org/apache/maven/internal/transformation/ConsumerPomArtifactTransformer.java b/maven-core/src/main/java/org/apache/maven/internal/transformation/ConsumerPomArtifactTransformer.java
index 41ac09d023..c37a5e50ff 100644
--- a/maven-core/src/main/java/org/apache/maven/internal/transformation/ConsumerPomArtifactTransformer.java
+++ b/maven-core/src/main/java/org/apache/maven/internal/transformation/ConsumerPomArtifactTransformer.java
@@ -19,31 +19,35 @@
 package org.apache.maven.internal.transformation;
 
 import javax.annotation.PreDestroy;
+import javax.inject.Inject;
 import javax.inject.Named;
 import javax.inject.Singleton;
 import javax.xml.stream.XMLStreamException;
-import javax.xml.stream.XMLStreamReader;
 
 import java.io.IOException;
-import java.io.InputStream;
+import java.io.Writer;
+import java.lang.reflect.Method;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
-import java.nio.file.StandardCopyOption;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Set;
 import java.util.concurrent.CopyOnWriteArraySet;
-import java.util.function.BiConsumer;
 
 import org.apache.maven.api.feature.Features;
-import org.apache.maven.model.building.DefaultBuildPomXMLFilterFactory;
+import org.apache.maven.api.model.Model;
+import org.apache.maven.model.building.FileModelSource;
+import org.apache.maven.model.building.ModelBuilder;
+import org.apache.maven.model.building.ModelBuildingRequest;
+import org.apache.maven.model.building.ModelCache;
+import org.apache.maven.model.building.Result;
 import org.apache.maven.model.building.TransformerContext;
-import org.apache.maven.model.transform.RawToConsumerPomXMLFilterFactory;
-import org.apache.maven.model.transform.stax.XmlUtils;
+import org.apache.maven.model.v4.MavenModelVersion;
+import org.apache.maven.model.v4.MavenStaxWriter;
 import org.apache.maven.project.MavenProject;
 import org.apache.maven.project.artifact.ProjectArtifact;
-import org.codehaus.stax2.XMLInputFactory2;
+import org.apache.maven.repository.internal.DefaultModelCache;
 import org.eclipse.aether.RepositorySystemSession;
 import org.eclipse.aether.artifact.Artifact;
 import org.eclipse.aether.artifact.DefaultArtifact;
@@ -61,32 +65,49 @@ public final class ConsumerPomArtifactTransformer {
 
     private static final String CONSUMER_POM_CLASSIFIER = "consumer";
 
+    private static final String BUILD_POM_CLASSIFIER = "build";
+
+    private static final String NAMESPACE_FORMAT = "http://maven.apache.org/POM/%s";
+
+    private static final String SCHEMA_LOCATION_FORMAT = "https://maven.apache.org/xsd/maven-%s.xsd";
+
     private final Set<Path> toDelete = new CopyOnWriteArraySet<>();
 
+    private final ModelBuilder modelBuilder;
+
+    @Inject
+    ConsumerPomArtifactTransformer(ModelBuilder modelBuilder) {
+        this.modelBuilder = modelBuilder;
+    }
+
     public void injectTransformedArtifacts(MavenProject project, RepositorySystemSession session) throws IOException {
         if (project.getFile() == null) {
             // If there is no build POM there is no reason to inject artifacts for the consumer POM.
             return;
         }
         if (Features.buildConsumer(session.getUserProperties())) {
-            Path generatedFile;
-            String buildDirectory =
-                    project.getBuild() != null ? project.getBuild().getDirectory() : null;
-            if (buildDirectory == null) {
-                generatedFile = Files.createTempFile(CONSUMER_POM_CLASSIFIER, "pom");
-            } else {
-                Path buildDir = Paths.get(buildDirectory);
+            Path buildDir =
+                    project.getBuild() != null ? Paths.get(project.getBuild().getDirectory()) : null;
+            if (buildDir != null) {
                 Files.createDirectories(buildDir);
-                generatedFile = Files.createTempFile(buildDir, CONSUMER_POM_CLASSIFIER, "pom");
             }
-            deferDeleteFile(generatedFile);
-            project.addAttachedArtifact(new ConsumerPomArtifact(project, generatedFile, session));
+            Path consumer = buildDir != null
+                    ? Files.createTempFile(buildDir, CONSUMER_POM_CLASSIFIER + "-", ".pom")
+                    : Files.createTempFile(CONSUMER_POM_CLASSIFIER + "-", ".pom");
+            deferDeleteFile(consumer);
+
+            project.addAttachedArtifact(createConsumerPomArtifact(project, consumer, session));
         } else if (project.getModel().isRoot()) {
             throw new IllegalStateException(
                     "The use of the root attribute on the model requires the buildconsumer feature to be active");
         }
     }
 
+    public ConsumerPomArtifact createConsumerPomArtifact(
+            MavenProject project, Path consumer, RepositorySystemSession session) {
+        return new ConsumerPomArtifact(project, consumer, session);
+    }
+
     private void deferDeleteFile(Path generatedFile) {
         toDelete.add(generatedFile.toAbsolutePath());
     }
@@ -117,75 +138,130 @@ public final class ConsumerPomArtifactTransformer {
     }
 
     private boolean consumerPomPresent(Collection<Artifact> artifacts) {
-        return artifacts.stream().anyMatch(a -> CONSUMER_POM_CLASSIFIER.equals(a.getClassifier()));
+        return artifacts.stream()
+                .anyMatch(a -> "pom".equals(a.getExtension()) && CONSUMER_POM_CLASSIFIER.equals(a.getClassifier()));
     }
 
     private Collection<Artifact> replacePom(Collection<Artifact> artifacts) {
-        ArrayList<Artifact> result = new ArrayList<>(artifacts.size());
+        Artifact consumer = null;
+        Artifact main = null;
         for (Artifact artifact : artifacts) {
-            if (CONSUMER_POM_CLASSIFIER.equals(artifact.getClassifier())) {
-                // if under CONSUMER_POM_CLASSIFIER, move it to "" classifier
-                DefaultArtifact remapped = new DefaultArtifact(
-                        artifact.getGroupId(),
-                        artifact.getArtifactId(),
-                        "",
-                        artifact.getExtension(),
-                        artifact.getVersion(),
-                        artifact.getProperties(),
-                        artifact.getFile());
-                result.add(remapped);
-            } else if ("".equals(artifact.getClassifier())
-                            && (artifact.getExtension().equals("pom"))
-                    || artifact.getExtension().startsWith("pom.")) {
-                // skip POM and POM subordinates
-                continue;
-            } else {
-                // everything else: add as is
-                result.add(artifact);
+            if ("pom".equals(artifact.getExtension())) {
+                if (CONSUMER_POM_CLASSIFIER.equals(artifact.getClassifier())) {
+                    consumer = artifact;
+                } else if ("".equals(artifact.getClassifier())) {
+                    main = artifact;
+                }
             }
         }
-        return result;
+        if (main != null && consumer != null) {
+            ArrayList<Artifact> result = new ArrayList<>(artifacts);
+            result.remove(main);
+            result.remove(consumer);
+            result.add(new DefaultArtifact(
+                    consumer.getGroupId(),
+                    consumer.getArtifactId(),
+                    "",
+                    consumer.getExtension(),
+                    consumer.getVersion(),
+                    consumer.getProperties(),
+                    consumer.getFile()));
+            result.add(new DefaultArtifact(
+                    main.getGroupId(),
+                    main.getArtifactId(),
+                    BUILD_POM_CLASSIFIER,
+                    main.getExtension(),
+                    main.getVersion(),
+                    main.getProperties(),
+                    main.getFile()));
+            artifacts = result;
+        }
+        return artifacts;
     }
 
     /**
      * Consumer POM is transformed from original POM.
      */
-    private static class ConsumerPomArtifact extends TransformedArtifact {
+    class ConsumerPomArtifact extends TransformedArtifact {
 
-        private ConsumerPomArtifact(MavenProject mavenProject, Path target, RepositorySystemSession session) {
+        private MavenProject project;
+        private RepositorySystemSession session;
+
+        ConsumerPomArtifact(MavenProject mavenProject, Path target, RepositorySystemSession session) {
             super(
                     new ProjectArtifact(mavenProject),
                     () -> mavenProject.getFile().toPath(),
                     CONSUMER_POM_CLASSIFIER,
                     "pom",
-                    target,
-                    transformer(session));
+                    target);
+            this.project = mavenProject;
+            this.session = session;
+        }
+
+        @Override
+        public void transform(Path src, Path dest) {
+            Model model = project.getModel().getDelegate();
+            transform(src, dest, model);
         }
 
-        private static BiConsumer<Path, Path> transformer(RepositorySystemSession session) {
-            TransformerContext context = (TransformerContext) session.getData().get(TransformerContext.KEY);
-            return (src, dest) -> {
-                try (InputStream inputStream = transform(src, context)) {
-                    Files.createDirectories(dest.getParent());
-                    Files.copy(inputStream, dest, StandardCopyOption.REPLACE_EXISTING);
-                } catch (XMLStreamException | IOException e) {
+        void transform(Path src, Path dest, Model model) {
+            Model consumer = null;
+            String version;
+
+            // This is a bit of a hack, but all models are cached, so not sure why we'd need to parse it again
+            ModelCache cache = DefaultModelCache.newInstance(session);
+            Object modelData = cache.get(new FileModelSource(src.toFile()), "raw");
+            if (modelData != null) {
+                try {
+                    Method getModel = modelData.getClass().getMethod("getModel");
+                    getModel.setAccessible(true);
+                    org.apache.maven.model.Model cachedModel =
+                            (org.apache.maven.model.Model) getModel.invoke(modelData);
+                    consumer = cachedModel.getDelegate();
+                } catch (Exception e) {
                     throw new RuntimeException(e);
                 }
-            };
-        }
-    }
+            }
 
-    /**
-     * The actual transformation: visible for testing.
-     */
-    static InputStream transform(Path pomFile, TransformerContext context) throws IOException, XMLStreamException {
-        try (InputStream input = Files.newInputStream(pomFile)) {
-            XMLInputFactory2 factory = new com.ctc.wstx.stax.WstxInputFactory();
-            factory.configureForRoundTripping();
-            XMLStreamReader reader = factory.createXMLStreamReader(input);
-            reader = new RawToConsumerPomXMLFilterFactory(new DefaultBuildPomXMLFilterFactory(context, true))
-                    .get(reader, pomFile);
-            return XmlUtils.writeDocument(reader);
+            if (consumer == null) {
+                TransformerContext context =
+                        (TransformerContext) session.getData().get(TransformerContext.KEY);
+                Result<? extends org.apache.maven.model.Model> result = modelBuilder.buildRawModel(
+                        src.toFile(), ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL, false, context);
+                if (result.hasErrors()) {
+                    throw new IllegalStateException(
+                            "Unable to build POM " + src,
+                            result.getProblems().iterator().next().getException());
+                }
+                consumer = result.get().getDelegate();
+            }
+
+            // raw to consumer transform
+            consumer = consumer.withRoot(false).withModules(null);
+            if (consumer.getParent() != null) {
+                consumer = consumer.withParent(consumer.getParent().withRelativePath(null));
+            }
+
+            if (!consumer.isPreserveModelVersion()) {
+                consumer = consumer.withPreserveModelVersion(false);
+                version = new MavenModelVersion().getModelVersion(consumer);
+                consumer = consumer.withModelVersion(version);
+            } else {
+                version = consumer.getModelVersion();
+            }
+
+            try {
+                Files.createDirectories(dest.getParent());
+                try (Writer w = Files.newBufferedWriter(dest)) {
+                    MavenStaxWriter writer = new MavenStaxWriter();
+                    writer.setNamespace(String.format(NAMESPACE_FORMAT, version));
+                    writer.setSchemaLocation(String.format(SCHEMA_LOCATION_FORMAT, version));
+                    writer.setAddLocationInformation(false);
+                    writer.write(w, consumer);
+                }
+            } catch (XMLStreamException | IOException e) {
+                throw new RuntimeException(e);
+            }
         }
     }
 }
diff --git a/maven-core/src/main/java/org/apache/maven/internal/transformation/TransformedArtifact.java b/maven-core/src/main/java/org/apache/maven/internal/transformation/TransformedArtifact.java
index 84e88c399b..9c88336558 100644
--- a/maven-core/src/main/java/org/apache/maven/internal/transformation/TransformedArtifact.java
+++ b/maven-core/src/main/java/org/apache/maven/internal/transformation/TransformedArtifact.java
@@ -23,7 +23,6 @@ import java.io.InputStream;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.security.MessageDigest;
-import java.util.function.BiConsumer;
 import java.util.function.Supplier;
 
 import org.apache.maven.artifact.Artifact;
@@ -42,12 +41,7 @@ abstract class TransformedArtifact extends DefaultArtifact {
     private final OnChangeTransformer onChangeTransformer;
 
     TransformedArtifact(
-            Artifact source,
-            Supplier<Path> sourcePathProvider,
-            String classifier,
-            String extension,
-            Path targetPath,
-            BiConsumer<Path, Path> transformerConsumer) {
+            Artifact source, Supplier<Path> sourcePathProvider, String classifier, String extension, Path targetPath) {
         super(
                 source.getGroupId(),
                 source.getArtifactId(),
@@ -58,7 +52,7 @@ abstract class TransformedArtifact extends DefaultArtifact {
                 new TransformedArtifactHandler(
                         classifier, extension, source.getArtifactHandler().getPackaging()));
         this.onChangeTransformer =
-                new OnChangeTransformer(sourcePathProvider, targetPath, TransformedArtifact::sha1, transformerConsumer);
+                new OnChangeTransformer(sourcePathProvider, targetPath, TransformedArtifact::sha1, this::transform);
     }
 
     @Override
@@ -102,6 +96,8 @@ abstract class TransformedArtifact extends DefaultArtifact {
         }
     }
 
+    protected abstract void transform(Path src, Path dst);
+
     private static class TransformedArtifactHandler implements ArtifactHandler {
         private final String classifier;
 
diff --git a/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java b/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java
index 6b52e4eade..5edf2e88bc 100644
--- a/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java
+++ b/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java
@@ -131,7 +131,8 @@ public class DefaultProjectBuilder implements ProjectBuilder {
 
     @Override
     public ProjectBuildingResult build(File pomFile, ProjectBuildingRequest request) throws ProjectBuildingException {
-        return build(pomFile, new FileModelSource(pomFile), new InternalConfig(request, null, null));
+        InternalConfig config = new InternalConfig(request, null, modelBuilder.newTransformerContextBuilder());
+        return build(pomFile, new FileModelSource(pomFile), config);
     }
 
     @Override
@@ -160,6 +161,7 @@ public class DefaultProjectBuilder implements ProjectBuilder {
 
                 DefaultModelBuildingListener listener =
                         new DefaultModelBuildingListener(project, projectBuildingHelper, projectBuildingRequest);
+
                 request.setModelBuildingListener(listener);
 
                 request.setPomFile(pomFile);
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
deleted file mode 100644
index 10bf5e13a1..0000000000
--- a/maven-core/src/main/java/org/apache/maven/xml/internal/DefaultConsumerPomXMLFilterFactory.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * 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.
- */
-package org.apache.maven.xml.internal;
-
-import org.apache.maven.model.building.DefaultBuildPomXMLFilterFactory;
-import org.apache.maven.model.transform.RawToConsumerPomXMLFilterFactory;
-
-/**
- * The default implementation of the {@link RawToConsumerPomXMLFilterFactory}
- * It will provide several values for the consumer pom based on its context.
- *
- * @since 4.0.0
- */
-public class DefaultConsumerPomXMLFilterFactory extends RawToConsumerPomXMLFilterFactory {
-    public DefaultConsumerPomXMLFilterFactory(DefaultBuildPomXMLFilterFactory buildPomXMLFilterFactory) {
-        super(buildPomXMLFilterFactory);
-    }
-}
diff --git a/maven-core/src/test/java/org/apache/maven/internal/transformation/ConsumerPomArtifactTransformerTest.java b/maven-core/src/test/java/org/apache/maven/internal/transformation/ConsumerPomArtifactTransformerTest.java
index 36b0774ef5..0c177964dd 100644
--- a/maven-core/src/test/java/org/apache/maven/internal/transformation/ConsumerPomArtifactTransformerTest.java
+++ b/maven-core/src/test/java/org/apache/maven/internal/transformation/ConsumerPomArtifactTransformerTest.java
@@ -25,7 +25,10 @@ import java.nio.file.Path;
 import java.nio.file.Paths;
 
 import org.apache.maven.model.Model;
+import org.apache.maven.model.building.DefaultModelBuilderFactory;
+import org.apache.maven.model.building.ModelBuilder;
 import org.apache.maven.model.building.TransformerContext;
+import org.apache.maven.model.v4.MavenStaxReader;
 import org.apache.maven.project.MavenProject;
 import org.eclipse.aether.RepositorySystemSession;
 import org.eclipse.aether.SessionData;
@@ -38,18 +41,30 @@ import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.when;
 
 class ConsumerPomArtifactTransformerTest {
+
+    ModelBuilder modelBuilder = new DefaultModelBuilderFactory().newInstance();
+
     @Test
     void transform() throws Exception {
+        RepositorySystemSession systemSessionMock = Mockito.mock(RepositorySystemSession.class);
+        SessionData sessionDataMock = Mockito.mock(SessionData.class);
+        when(systemSessionMock.getData()).thenReturn(sessionDataMock);
+        when(sessionDataMock.get(any())).thenReturn(new NoTransformerContext());
+
         Path beforePomFile =
                 Paths.get("src/test/resources/projects/transform/before.pom").toAbsolutePath();
         Path afterPomFile =
                 Paths.get("src/test/resources/projects/transform/after.pom").toAbsolutePath();
-
-        try (InputStream expected = Files.newInputStream(afterPomFile);
-                InputStream result =
-                        ConsumerPomArtifactTransformer.transform(beforePomFile, new NoTransformerContext())) {
-            XmlAssert.assertThat(result).and(expected).areIdentical();
+        Path tempFile = Files.createTempFile("", ".pom");
+        Files.delete(tempFile);
+        try (InputStream expected = Files.newInputStream(beforePomFile)) {
+            Model model = new Model(new MavenStaxReader().read(expected));
+            MavenProject project = new MavenProject(model);
+            ConsumerPomArtifactTransformer t = new ConsumerPomArtifactTransformer(modelBuilder);
+            t.createConsumerPomArtifact(project, tempFile, systemSessionMock)
+                    .transform(beforePomFile, tempFile, model.getDelegate());
         }
+        XmlAssert.assertThat(afterPomFile.toFile()).and(tempFile.toFile()).areIdentical();
     }
 
     @Test
@@ -61,7 +76,7 @@ class ConsumerPomArtifactTransformerTest {
         when(systemSessionMock.getData()).thenReturn(sessionDataMock);
         when(sessionDataMock.get(any())).thenReturn(new NoTransformerContext());
 
-        new ConsumerPomArtifactTransformer().injectTransformedArtifacts(emptyProject, systemSessionMock);
+        new ConsumerPomArtifactTransformer(modelBuilder).injectTransformedArtifacts(emptyProject, systemSessionMock);
 
         assertThat(emptyProject.getAttachedArtifacts()).isEmpty();
     }
diff --git a/maven-core/src/test/java/org/apache/maven/model/ModelBuilderTest.java b/maven-core/src/test/java/org/apache/maven/model/ModelBuilderTest.java
new file mode 100644
index 0000000000..269289933d
--- /dev/null
+++ b/maven-core/src/test/java/org/apache/maven/model/ModelBuilderTest.java
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+package org.apache.maven.model;
+
+import javax.inject.Inject;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.maven.bridge.MavenRepositorySystem;
+import org.apache.maven.execution.DefaultMavenExecutionRequest;
+import org.apache.maven.execution.MavenExecutionRequest;
+import org.apache.maven.internal.aether.DefaultRepositorySystemSessionFactory;
+import org.apache.maven.project.DefaultProjectBuildingRequest;
+import org.apache.maven.project.ProjectBuilder;
+import org.apache.maven.project.ProjectBuildingResult;
+import org.codehaus.plexus.testing.PlexusTest;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+@PlexusTest
+public class ModelBuilderTest {
+
+    @Inject
+    ProjectBuilder projectBuilder;
+
+    @Inject
+    MavenRepositorySystem repositorySystem;
+
+    @Inject
+    DefaultRepositorySystemSessionFactory repositorySessionFactory;
+
+    @Test
+    void testModelBuilder() throws Exception {
+        MavenExecutionRequest mavenRequest = new DefaultMavenExecutionRequest();
+        mavenRequest.setLocalRepository(repositorySystem.createLocalRepository(new File("target/test-repo/")));
+
+        DefaultProjectBuildingRequest request = new DefaultProjectBuildingRequest();
+        request.setRepositorySession(repositorySessionFactory.newRepositorySession(mavenRequest));
+        List<ProjectBuildingResult> results = projectBuilder.build(
+                Collections.singletonList(new File("src/test/resources/projects/tree/pom.xml")), true, request);
+
+        assertEquals(3, results.size());
+    }
+}
diff --git a/maven-core/src/test/java/org/apache/maven/project/ProjectBuilderTest.java b/maven-core/src/test/java/org/apache/maven/project/ProjectBuilderTest.java
index 50254bda92..182df18fac 100644
--- a/maven-core/src/test/java/org/apache/maven/project/ProjectBuilderTest.java
+++ b/maven-core/src/test/java/org/apache/maven/project/ProjectBuilderTest.java
@@ -227,7 +227,7 @@ class ProjectBuilderTest extends AbstractCoreMavenComponentTestCase {
         File pomFile = new File("src/test/resources/projects/badPom.xml").getAbsoluteFile();
         MavenSession mavenSession = createMavenSession(null);
         ProjectBuildingRequest configuration = new DefaultProjectBuildingRequest();
-        configuration.setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL);
+        configuration.setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_STRICT);
         configuration.setRepositorySession(mavenSession.getRepositorySession());
         org.apache.maven.project.ProjectBuilder projectBuilder =
                 getContainer().lookup(org.apache.maven.project.ProjectBuilder.class);
@@ -245,7 +245,8 @@ class ProjectBuilderTest extends AbstractCoreMavenComponentTestCase {
         assertThat(pex.getResults().get(0).getProblems().size(), greaterThan(0));
         assertThat(
                 pex.getResults(),
-                contains(projectBuildingResultWithProblemMessage("expected START_TAG or END_TAG not CHARACTERS")));
+                contains(projectBuildingResultWithProblemMessage(
+                        "Received non-all-whitespace CHARACTERS or CDATA event in nextTag()")));
     }
 
     @Test
diff --git a/maven-core/src/test/resources/projects/transform/after.pom b/maven-core/src/test/resources/projects/transform/after.pom
index a66fca885d..1b92bc255e 100644
--- a/maven-core/src/test/resources/projects/transform/after.pom
+++ b/maven-core/src/test/resources/projects/transform/after.pom
@@ -1,45 +1,18 @@
 <?xml version="1.0" encoding="UTF-8"?>
-
-<!--
-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.
--->
-
-<project xmlns="http://maven.apache.org/POM/4.0.0"
-         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
-
   <groupId>test</groupId>
   <artifactId>test</artifactId>
   <version>0.1-SNAPSHOT</version>
   <packaging>pom</packaging>
-
-  
-
   <build>
     <pluginManagement>
       <plugins>
         <plugin>
-          <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-compiler-plugin</artifactId>
           <version>2.1</version>
           <configuration>
-            <source>  1.5  </source>
+            <source>1.5</source>
             <target xml:space="preserve">  1.5  </target>
           </configuration>
         </plugin>
@@ -47,7 +20,6 @@ under the License.
     </pluginManagement>
     <plugins>
       <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-compiler-plugin</artifactId>
         <executions>
           <execution>
@@ -57,10 +29,9 @@ under the License.
       </plugin>
     </plugins>
   </build>
-
   <profiles>
     <profile>
-      <id>default</id>
+      <id>default-active</id>
       <activation>
         <activeByDefault>true</activeByDefault>
       </activation>
@@ -80,4 +51,4 @@ under the License.
       </properties>
     </profile>
   </profiles>
-</project>
+</project>
\ No newline at end of file
diff --git a/maven-core/src/test/resources/projects/transform/before.pom b/maven-core/src/test/resources/projects/transform/before.pom
index c6b2bc2295..60d0ccedf3 100644
--- a/maven-core/src/test/resources/projects/transform/before.pom
+++ b/maven-core/src/test/resources/projects/transform/before.pom
@@ -19,10 +19,11 @@ specific language governing permissions and limitations
 under the License.
 -->
 
-<project xmlns="http://maven.apache.org/POM/4.0.0"
+<project xmlns="http://maven.apache.org/POM/4.1.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
-  <modelVersion>4.0.0</modelVersion>
+         xsi:schemaLocation="http://maven.apache.org/POM/4.1.0 https://maven.apache.org/xsd/maven-4.1.0.xsd"
+         root="true">
+  <modelVersion>4.1.0</modelVersion>
 
   <groupId>test</groupId>
   <artifactId>test</artifactId>
@@ -63,7 +64,7 @@ under the License.
 
   <profiles>
     <profile>
-      <id>default</id>
+      <id>default-active</id>
       <activation>
         <activeByDefault>true</activeByDefault>
       </activation>
diff --git a/maven-core/src/test/resources/projects/transform/consumer-after.pom b/maven-core/src/test/resources/projects/transform/consumer-after.pom
deleted file mode 100644
index 2f7ef91990..0000000000
--- a/maven-core/src/test/resources/projects/transform/consumer-after.pom
+++ /dev/null
@@ -1,81 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!--
-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.
--->
-
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
-  <modelVersion>4.0.0</modelVersion>
-
-  <groupId>test</groupId>
-  <artifactId>test</artifactId>
-  <version>0.1-SNAPSHOT</version>
-  <packaging>pom</packaging>
-
-  
-
-  <build>
-    <pluginManagement>
-      <plugins>
-        <plugin>
-          <groupId>org.apache.maven.plugins</groupId>
-          <artifactId>maven-compiler-plugin</artifactId>
-          <version>2.1</version>
-          <configuration>
-            <source>  1.5  </source>
-            <target xml:space="preserve">  1.5  </target>
-          </configuration>
-        </plugin>
-      </plugins>
-    </pluginManagement>
-    <plugins>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-compiler-plugin</artifactId>
-        <executions>
-          <execution>
-            <id>test</id>
-          </execution>
-        </executions>
-      </plugin>
-    </plugins>
-  </build>
-
-  <profiles>
-    <profile>
-      <id>default-active</id>
-      <activation>
-        <activeByDefault>true</activeByDefault>
-      </activation>
-      <properties>
-        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-      </properties>
-    </profile>
-    <profile>
-      <id>file</id>
-      <activation>
-        <file>
-          <exists>simple.xml</exists>
-        </file>
-      </activation>
-      <properties>
-        <profile.file>activated</profile.file>
-      </properties>
-    </profile>
-  </profiles>
-</project>
diff --git a/maven-core/src/test/resources/projects/transform/consumer-before.pom b/maven-core/src/test/resources/projects/transform/consumer-before.pom
deleted file mode 100644
index 60d0ccedf3..0000000000
--- a/maven-core/src/test/resources/projects/transform/consumer-before.pom
+++ /dev/null
@@ -1,87 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!--
-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.
--->
-
-<project xmlns="http://maven.apache.org/POM/4.1.0"
-         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.1.0 https://maven.apache.org/xsd/maven-4.1.0.xsd"
-         root="true">
-  <modelVersion>4.1.0</modelVersion>
-
-  <groupId>test</groupId>
-  <artifactId>test</artifactId>
-  <version>0.1-SNAPSHOT</version>
-  <packaging>pom</packaging>
-
-  <modules>
-    <module>lib</module> <!-- the library -->
-    <module>app</module> <!-- the application -->
-  </modules>
-
-  <build>
-    <pluginManagement>
-      <plugins>
-        <plugin>
-          <groupId>org.apache.maven.plugins</groupId>
-          <artifactId>maven-compiler-plugin</artifactId>
-          <version>2.1</version>
-          <configuration>
-            <source>  1.5  </source>
-            <target xml:space="preserve">  1.5  </target>
-          </configuration>
-        </plugin>
-      </plugins>
-    </pluginManagement>
-    <plugins>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-compiler-plugin</artifactId>
-        <executions>
-          <execution>
-            <id>test</id>
-          </execution>
-        </executions>
-      </plugin>
-    </plugins>
-  </build>
-
-  <profiles>
-    <profile>
-      <id>default-active</id>
-      <activation>
-        <activeByDefault>true</activeByDefault>
-      </activation>
-      <properties>
-        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-      </properties>
-    </profile>
-    <profile>
-      <id>file</id>
-      <activation>
-        <file>
-          <exists>simple.xml</exists>
-        </file>
-      </activation>
-      <properties>
-        <profile.file>activated</profile.file>
-      </properties>
-    </profile>
-  </profiles>
-</project>
diff --git a/maven-core/src/test/resources/projects/tree/consumer/pom.xml b/maven-core/src/test/resources/projects/tree/consumer/pom.xml
new file mode 100644
index 0000000000..c527112307
--- /dev/null
+++ b/maven-core/src/test/resources/projects/tree/consumer/pom.xml
@@ -0,0 +1,16 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0">
+
+    <parent>
+        <groupId>org.apache.maven.ut</groupId>
+        <artifactId>parent</artifactId>
+    </parent>
+    <artifactId>consumer</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.maven.ut</groupId>
+            <artifactId>dep</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/maven-core/src/test/resources/projects/tree/dep/pom.xml b/maven-core/src/test/resources/projects/tree/dep/pom.xml
new file mode 100644
index 0000000000..09ccb88d1d
--- /dev/null
+++ b/maven-core/src/test/resources/projects/tree/dep/pom.xml
@@ -0,0 +1,9 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0">
+
+    <parent>
+        <groupId>org.apache.maven.ut</groupId>
+        <artifactId>parent</artifactId>
+    </parent>
+    <artifactId>dep</artifactId>
+
+</project>
diff --git a/maven-core/src/test/resources/projects/tree/pom.xml b/maven-core/src/test/resources/projects/tree/pom.xml
new file mode 100644
index 0000000000..8534ac440e
--- /dev/null
+++ b/maven-core/src/test/resources/projects/tree/pom.xml
@@ -0,0 +1,10 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0">
+    <groupId>org.apache.maven.ut</groupId>
+    <artifactId>parent</artifactId>
+    <version>1.0-SNAPSHOT</version>
+    <packaging>pom</packaging>
+    <modules>
+        <module>dep</module>
+        <module>consumer</module>
+    </modules>
+</project>
diff --git a/maven-model-builder/pom.xml b/maven-model-builder/pom.xml
index dccf9056f9..35efe28000 100644
--- a/maven-model-builder/pom.xml
+++ b/maven-model-builder/pom.xml
@@ -36,6 +36,10 @@ under the License.
       <groupId>org.apache.maven</groupId>
       <artifactId>maven-api-core</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-api-spi</artifactId>
+    </dependency>
     <dependency>
       <groupId>org.apache.maven</groupId>
       <artifactId>maven-xml-impl</artifactId>
@@ -64,10 +68,6 @@ under the License.
       <groupId>org.apache.maven</groupId>
       <artifactId>maven-builder-support</artifactId>
     </dependency>
-    <dependency>
-      <groupId>org.apache.maven</groupId>
-      <artifactId>maven-model-transform</artifactId>
-    </dependency>
     <!-- Testing -->
     <dependency>
       <groupId>org.eclipse.sisu</groupId>
@@ -105,6 +105,16 @@ under the License.
       <artifactId>xmlunit-matchers</artifactId>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-core</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.xmlunit</groupId>
+      <artifactId>xmlunit-assertj</artifactId>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
   <build>
diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/AbstractModelSourceTransformer.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/AbstractModelSourceTransformer.java
deleted file mode 100644
index bd6afc8822..0000000000
--- a/maven-model-builder/src/main/java/org/apache/maven/model/building/AbstractModelSourceTransformer.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * 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.
- */
-package org.apache.maven.model.building;
-
-/**
- * Offers a transformation implementation based on PipelineStreams.
- * Subclasses are responsible for providing the right SAXFilter.
- *
- * @since 4.0.0
- */
-public abstract class AbstractModelSourceTransformer implements ModelSourceTransformer {}
diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/BuildModelSourceTransformer.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/BuildModelSourceTransformer.java
index 2608ca774f..f0a8cb79da 100644
--- a/maven-model-builder/src/main/java/org/apache/maven/model/building/BuildModelSourceTransformer.java
+++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/BuildModelSourceTransformer.java
@@ -20,12 +20,17 @@ package org.apache.maven.model.building;
 
 import javax.inject.Named;
 import javax.inject.Singleton;
-import javax.xml.stream.XMLStreamReader;
 
-import java.io.IOException;
+import java.nio.file.Files;
 import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.Optional;
 
-import org.apache.maven.model.transform.BuildToRawPomXMLFilterFactory;
+import org.apache.maven.model.Dependency;
+import org.apache.maven.model.Model;
+import org.apache.maven.model.Parent;
 
 /**
  * ModelSourceTransformer for the build pom
@@ -35,11 +40,150 @@ import org.apache.maven.model.transform.BuildToRawPomXMLFilterFactory;
 @Named
 @Singleton
 class BuildModelSourceTransformer implements ModelSourceTransformer {
+
+    public static final String NAMESPACE_PREFIX = "http://maven.apache.org/POM/";
+
     @Override
-    public XMLStreamReader transform(XMLStreamReader parser, Path pomFile, TransformerContext context)
-            throws IOException, TransformerException {
-        BuildToRawPomXMLFilterFactory buildPomXMLFilterFactory = new DefaultBuildPomXMLFilterFactory(context, false);
+    public void transform(Path pomFile, TransformerContext context, Model model) {
+        handleModelVersion(model);
+        handleParent(pomFile, context, model);
+        handleReactorDependencies(context, model);
+        handleCiFriendlyVersion(context, model);
+    }
+
+    //
+    // Infer modelVersion from namespace URI
+    //
+    void handleModelVersion(Model model) {
+        String namespace = model.getDelegate().getNamespaceUri();
+        if (model.getModelVersion() == null && namespace != null && namespace.startsWith(NAMESPACE_PREFIX)) {
+            model.setModelVersion(namespace.substring(NAMESPACE_PREFIX.length()));
+        }
+    }
+
+    //
+    // Infer parent information
+    //
+    void handleParent(Path pomFile, TransformerContext context, Model model) {
+        Parent parent = model.getParent();
+        if (parent != null) {
+            String version = parent.getVersion();
+            String path = Optional.ofNullable(parent.getRelativePath()).orElse("..");
+            if (version == null && !path.isEmpty()) {
+                Optional<RelativeProject> resolvedParent = resolveRelativePath(
+                        pomFile.getParent(), context, Paths.get(path), parent.getGroupId(), parent.getArtifactId());
+                resolvedParent.ifPresent(relativeProject -> parent.setVersion(relativeProject.getVersion()));
+            }
+        }
+    }
+
+    //
+    // CI friendly versions
+    //
+    void handleCiFriendlyVersion(TransformerContext context, Model model) {
+        String version = model.getVersion();
+        String modVersion = replaceCiFriendlyVersion(context, version);
+        model.setVersion(modVersion);
+
+        Parent parent = model.getParent();
+        if (parent != null) {
+            version = parent.getVersion();
+            modVersion = replaceCiFriendlyVersion(context, version);
+            parent.setVersion(modVersion);
+        }
+    }
+
+    //
+    // Infer inner reactor dependencies version
+    //
+    void handleReactorDependencies(TransformerContext context, Model model) {
+        for (Dependency dep : model.getDependencies()) {
+            if (dep.getVersion() == null) {
+                Model depModel = context.getRawModel(dep.getGroupId(), dep.getArtifactId());
+                if (depModel != null) {
+                    String v = depModel.getVersion();
+                    if (v == null && depModel.getParent() != null) {
+                        v = depModel.getParent().getVersion();
+                    }
+                    dep.setVersion(v);
+                }
+            }
+        }
+    }
+
+    protected String replaceCiFriendlyVersion(TransformerContext context, String version) {
+        if (version != null) {
+            for (String key : Arrays.asList("changelist", "revision", "sha1")) {
+                String val = context.getUserProperty(key);
+                if (val != null) {
+                    version = version.replace("${" + key + "}", val);
+                }
+            }
+        }
+        return version;
+    }
+
+    protected Optional<RelativeProject> resolveRelativePath(
+            Path projectPath, TransformerContext context, Path relativePath, String groupId, String artifactId) {
+        Path pomPath = projectPath.resolve(relativePath).normalize();
+        if (Files.isDirectory(pomPath)) {
+            pomPath = context.locate(pomPath);
+        }
+
+        if (pomPath == null || !Files.isRegularFile(pomPath)) {
+            return Optional.empty();
+        }
+
+        Optional<RelativeProject> mappedProject = Optional.ofNullable(context.getRawModel(pomPath.normalize()))
+                .map(BuildModelSourceTransformer::toRelativeProject);
+
+        if (mappedProject.isPresent()) {
+            RelativeProject project = mappedProject.get();
+
+            if (Objects.equals(groupId, project.getGroupId()) && Objects.equals(artifactId, project.getArtifactId())) {
+                return mappedProject;
+            }
+        }
+        return Optional.empty();
+    }
+
+    private static RelativeProject toRelativeProject(final org.apache.maven.model.Model m) {
+        String groupId = m.getGroupId();
+        if (groupId == null && m.getParent() != null) {
+            groupId = m.getParent().getGroupId();
+        }
+
+        String version = m.getVersion();
+        if (version == null && m.getParent() != null) {
+            version = m.getParent().getVersion();
+        }
+
+        return new RelativeProject(groupId, m.getArtifactId(), version);
+    }
+
+    static class RelativeProject {
+        private final String groupId;
+
+        private final String artifactId;
+
+        private final String version;
+
+        RelativeProject(String groupId, String artifactId, String version) {
+            this.groupId = groupId;
+            this.artifactId = artifactId;
+            this.version = version;
+        }
+
+        public String getGroupId() {
+            return groupId;
+        }
+
+        public String getArtifactId() {
+            return artifactId;
+        }
 
-        return buildPomXMLFilterFactory.get(parser, pomFile);
+        public String getVersion() {
+            return version;
+        }
     }
 }
diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultBuildPomXMLFilterFactory.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultBuildPomXMLFilterFactory.java
deleted file mode 100644
index 5caeadc3ac..0000000000
--- a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultBuildPomXMLFilterFactory.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * 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.
- */
-package org.apache.maven.model.building;
-
-import java.nio.file.Path;
-import java.util.Optional;
-import java.util.function.BiFunction;
-import java.util.function.Function;
-
-import org.apache.maven.model.Model;
-import org.apache.maven.model.transform.BuildToRawPomXMLFilterFactory;
-import org.apache.maven.model.transform.RelativeProject;
-
-/**
- * A BuildPomXMLFilterFactory which is context aware
- *
- * @since 4.0.0
- */
-public class DefaultBuildPomXMLFilterFactory extends BuildToRawPomXMLFilterFactory {
-    private final TransformerContext context;
-
-    /**
-     *
-     * @param context a set of data to extract values from as required for the build pom
-     * @param consume {@code true} if this factory is being used for creating the consumer pom, otherwise {@code false}
-     */
-    public DefaultBuildPomXMLFilterFactory(TransformerContext context, boolean consume) {
-        super(consume);
-        this.context = context;
-    }
-
-    @Override
-    protected Function<Path, Optional<RelativeProject>> getRelativePathMapper() {
-        return p -> Optional.ofNullable(context.getRawModel(p)).map(DefaultBuildPomXMLFilterFactory::toRelativeProject);
-    }
-
-    @Override
-    protected Function<Path, Path> getModelLocator() {
-        return context::locate;
-    }
-
-    @Override
-    protected BiFunction<String, String, String> getDependencyKeyToVersionMapper() {
-        return (g, a) -> Optional.ofNullable(context.getRawModel(g, a))
-                .map(DefaultBuildPomXMLFilterFactory::toVersion)
-                .orElse(null);
-    }
-
-    @Override
-    protected Optional<String> getChangelist() {
-        return Optional.ofNullable(context.getUserProperty("changelist"));
-    }
-
-    @Override
-    protected Optional<String> getRevision() {
-        return Optional.ofNullable(context.getUserProperty("revision"));
-    }
-
-    @Override
-    protected Optional<String> getSha1() {
-        return Optional.ofNullable(context.getUserProperty("sha1"));
-    }
-
-    private static RelativeProject toRelativeProject(final Model m) {
-        String groupId = m.getGroupId();
-        if (groupId == null && m.getParent() != null) {
-            groupId = m.getParent().getGroupId();
-        }
-
-        String version = m.getVersion();
-        if (version == null && m.getParent() != null) {
-            version = m.getParent().getVersion();
-        }
-
-        return new RelativeProject(groupId, m.getArtifactId(), version);
-    }
-
-    private static String toVersion(final Model m) {
-        String version = m.getVersion();
-        if (version == null && m.getParent() != null) {
-            version = m.getParent().getVersion();
-        }
-
-        return version;
-    }
-}
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 ed76d6065c..8f3bb819c7 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
@@ -67,7 +67,6 @@ import org.apache.maven.model.inheritance.InheritanceAssembler;
 import org.apache.maven.model.interpolation.ModelInterpolator;
 import org.apache.maven.model.interpolation.ModelVersionProcessor;
 import org.apache.maven.model.io.ModelParseException;
-import org.apache.maven.model.io.ModelReader;
 import org.apache.maven.model.management.DependencyManagementInjector;
 import org.apache.maven.model.management.PluginManagementInjector;
 import org.apache.maven.model.normalization.ModelNormalizer;
@@ -87,7 +86,6 @@ import org.apache.maven.model.resolution.ModelResolver;
 import org.apache.maven.model.resolution.UnresolvableModelException;
 import org.apache.maven.model.resolution.WorkspaceModelResolver;
 import org.apache.maven.model.superpom.SuperPomProvider;
-import org.apache.maven.model.v4.MavenMerger;
 import org.apache.maven.model.validation.ModelValidator;
 import org.codehaus.plexus.interpolation.InterpolationException;
 import org.codehaus.plexus.interpolation.MapBasedValueSource;
@@ -102,7 +100,6 @@ import static org.apache.maven.model.building.Result.newResult;
 @Named
 @Singleton
 public class DefaultModelBuilder implements ModelBuilder {
-    private final MavenMerger modelMerger = new FileToRawModelMerger();
 
     private final ModelProcessor modelProcessor;
     private final ModelValidator modelValidator;
@@ -122,6 +119,7 @@ public class DefaultModelBuilder implements ModelBuilder {
     private final ReportConfigurationExpander reportConfigurationExpander;
     private final ProfileActivationFilePathInterpolator profileActivationFilePathInterpolator;
     private final ModelVersionProcessor versionProcessor;
+    private final ModelSourceTransformer transformer;
 
     @SuppressWarnings("checkstyle:ParameterNumber")
     @Inject
@@ -143,7 +141,8 @@ public class DefaultModelBuilder implements ModelBuilder {
             PluginConfigurationExpander pluginConfigurationExpander,
             ReportConfigurationExpander reportConfigurationExpander,
             ProfileActivationFilePathInterpolator profileActivationFilePathInterpolator,
-            ModelVersionProcessor versionProcessor) {
+            ModelVersionProcessor versionProcessor,
+            ModelSourceTransformer transformer) {
         this.modelProcessor = modelProcessor;
         this.modelValidator = modelValidator;
         this.modelNormalizer = modelNormalizer;
@@ -162,6 +161,7 @@ public class DefaultModelBuilder implements ModelBuilder {
         this.reportConfigurationExpander = reportConfigurationExpander;
         this.profileActivationFilePathInterpolator = profileActivationFilePathInterpolator;
         this.versionProcessor = versionProcessor;
+        this.transformer = transformer;
     }
 
     /**
@@ -188,7 +188,8 @@ public class DefaultModelBuilder implements ModelBuilder {
                 pluginConfigurationExpander,
                 reportConfigurationExpander,
                 profileActivationFilePathInterpolator,
-                versionProcessor);
+                versionProcessor,
+                transformer);
     }
 
     /**
@@ -215,7 +216,8 @@ public class DefaultModelBuilder implements ModelBuilder {
                 pluginConfigurationExpander,
                 reportConfigurationExpander,
                 profileActivationFilePathInterpolator,
-                versionProcessor);
+                versionProcessor,
+                transformer);
     }
 
     /**
@@ -242,7 +244,8 @@ public class DefaultModelBuilder implements ModelBuilder {
                 pluginConfigurationExpander,
                 reportConfigurationExpander,
                 profileActivationFilePathInterpolator,
-                versionProcessor);
+                versionProcessor,
+                transformer);
     }
 
     /**
@@ -269,7 +272,8 @@ public class DefaultModelBuilder implements ModelBuilder {
                 pluginConfigurationExpander,
                 reportConfigurationExpander,
                 profileActivationFilePathInterpolator,
-                versionProcessor);
+                versionProcessor,
+                transformer);
     }
 
     /**
@@ -296,7 +300,8 @@ public class DefaultModelBuilder implements ModelBuilder {
                 pluginConfigurationExpander,
                 reportConfigurationExpander,
                 profileActivationFilePathInterpolator,
-                versionProcessor);
+                versionProcessor,
+                transformer);
     }
 
     /**
@@ -323,7 +328,8 @@ public class DefaultModelBuilder implements ModelBuilder {
                 pluginConfigurationExpander,
                 reportConfigurationExpander,
                 profileActivationFilePathInterpolator,
-                versionProcessor);
+                versionProcessor,
+                transformer);
     }
 
     /**
@@ -350,7 +356,8 @@ public class DefaultModelBuilder implements ModelBuilder {
                 pluginConfigurationExpander,
                 reportConfigurationExpander,
                 profileActivationFilePathInterpolator,
-                versionProcessor);
+                versionProcessor,
+                transformer);
     }
 
     /**
@@ -377,7 +384,8 @@ public class DefaultModelBuilder implements ModelBuilder {
                 pluginConfigurationExpander,
                 reportConfigurationExpander,
                 profileActivationFilePathInterpolator,
-                versionProcessor);
+                versionProcessor,
+                transformer);
     }
 
     /**
@@ -404,7 +412,8 @@ public class DefaultModelBuilder implements ModelBuilder {
                 pluginConfigurationExpander,
                 reportConfigurationExpander,
                 profileActivationFilePathInterpolator,
-                versionProcessor);
+                versionProcessor,
+                transformer);
     }
 
     /**
@@ -431,7 +440,8 @@ public class DefaultModelBuilder implements ModelBuilder {
                 pluginConfigurationExpander,
                 reportConfigurationExpander,
                 profileActivationFilePathInterpolator,
-                versionProcessor);
+                versionProcessor,
+                transformer);
     }
 
     /**
@@ -458,7 +468,8 @@ public class DefaultModelBuilder implements ModelBuilder {
                 pluginConfigurationExpander,
                 reportConfigurationExpander,
                 profileActivationFilePathInterpolator,
-                versionProcessor);
+                versionProcessor,
+                transformer);
     }
 
     /**
@@ -486,7 +497,8 @@ public class DefaultModelBuilder implements ModelBuilder {
                 pluginConfigurationExpander,
                 reportConfigurationExpander,
                 profileActivationFilePathInterpolator,
-                versionProcessor);
+                versionProcessor,
+                transformer);
     }
 
     /**
@@ -514,7 +526,8 @@ public class DefaultModelBuilder implements ModelBuilder {
                 pluginConfigurationExpander,
                 reportConfigurationExpander,
                 profileActivationFilePathInterpolator,
-                versionProcessor);
+                versionProcessor,
+                transformer);
     }
 
     /**
@@ -541,7 +554,8 @@ public class DefaultModelBuilder implements ModelBuilder {
                 pluginConfigurationExpander,
                 reportConfigurationExpander,
                 profileActivationFilePathInterpolator,
-                versionProcessor);
+                versionProcessor,
+                transformer);
     }
 
     /**
@@ -568,7 +582,8 @@ public class DefaultModelBuilder implements ModelBuilder {
                 pluginConfigurationExpander,
                 reportConfigurationExpander,
                 profileActivationFilePathInterpolator,
-                versionProcessor);
+                versionProcessor,
+                transformer);
     }
 
     /**
@@ -595,7 +610,8 @@ public class DefaultModelBuilder implements ModelBuilder {
                 pluginConfigurationExpander,
                 reportConfigurationExpander,
                 profileActivationFilePathInterpolator,
-                versionProcessor);
+                versionProcessor,
+                transformer);
     }
 
     /**
@@ -623,7 +639,8 @@ public class DefaultModelBuilder implements ModelBuilder {
                 pluginConfigurationExpander,
                 reportConfigurationExpander,
                 profileActivationFilePathInterpolator,
-                versionProcessor);
+                versionProcessor,
+                transformer);
     }
 
     /**
@@ -950,16 +967,31 @@ public class DefaultModelBuilder implements ModelBuilder {
 
     @Override
     public Result<? extends Model> buildRawModel(File pomFile, int validationLevel, boolean locationTracking) {
+        return buildRawModel(pomFile, validationLevel, locationTracking, null);
+    }
+
+    @Override
+    public Result<? extends Model> buildRawModel(
+            File pomFile, int validationLevel, boolean locationTracking, TransformerContext context) {
         final ModelBuildingRequest request = new DefaultModelBuildingRequest()
                 .setValidationLevel(validationLevel)
                 .setLocationTracking(locationTracking)
                 .setModelSource(new FileModelSource(pomFile));
-        final DefaultModelProblemCollector collector =
-                new DefaultModelProblemCollector(new DefaultModelBuildingResult());
+        DefaultModelProblemCollector problems = new DefaultModelProblemCollector(new DefaultModelBuildingResult());
         try {
-            return newResult(readFileModel(request, collector), collector.getProblems());
+            Model model = readFileModel(request, problems);
+
+            try {
+                if (transformer != null && context != null) {
+                    transformer.transform(pomFile.toPath(), context, model);
+                }
+            } catch (TransformerException e) {
+                problems.add(new ModelProblemCollectorRequest(Severity.FATAL, Version.V40).setException(e));
+            }
+
+            return newResult(model, problems.getProblems());
         } catch (ModelBuildingException e) {
-            return error(collector.getProblems());
+            return error(problems.getProblems());
         }
     }
 
@@ -1097,31 +1129,13 @@ public class DefaultModelBuilder implements ModelBuilder {
             rawModel = readFileModel(request, problems);
             File pomFile = ((FileModelSource) modelSource).getFile();
 
-            TransformerContext context = null;
-            if (request.getTransformerContextBuilder() != null) {
-                context = request.getTransformerContextBuilder().initialize(request, problems);
-            }
-
             try {
-                // must implement TransformContext, but should use request to access properties/model cache
-                Map<String, Object> options = new HashMap<>();
-                if (request.isLocationTracking()) {
-                    org.apache.maven.api.model.InputLocation location =
-                            rawModel.getDelegate().getLocation("");
-                    if (location != null) {
-                        options.put(
-                                ModelProcessor.INPUT_SOURCE,
-                                new org.apache.maven.model.InputSource(location.getSource()));
-                    }
+                if (request.getTransformerContextBuilder() != null) {
+                    TransformerContext context =
+                            request.getTransformerContextBuilder().initialize(request, problems);
+                    transformer.transform(pomFile.toPath(), context, rawModel);
                 }
-                options.put(ModelReader.TRANSFORMER_CONTEXT, context);
-                Model transformedFileModel = modelProcessor.read(pomFile, options);
-                // rawModel with locationTrackers, required for proper feedback during validations
-
-                // Apply enriched data
-                rawModel = new Model(
-                        modelMerger.merge(rawModel.getDelegate(), transformedFileModel.getDelegate(), false, null));
-            } catch (IOException e) {
+            } catch (TransformerException e) {
                 problems.add(new ModelProblemCollectorRequest(Severity.FATAL, Version.V40).setException(e));
             }
         } else if (request.getFileModel() == null) {
@@ -1613,7 +1627,8 @@ public class DefaultModelBuilder implements ModelBuilder {
         for (Iterator<Dependency> it = depMgmt.getDependencies().iterator(); it.hasNext(); ) {
             Dependency dependency = it.next();
 
-            if (!"pom".equals(dependency.getType()) || !"import".equals(dependency.getScope())) {
+            if (!("pom".equals(dependency.getType()) && "import".equals(dependency.getScope()))
+                    || "bom".equals(dependency.getType())) {
                 continue;
             }
 
diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilderFactory.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilderFactory.java
index d131feaf31..a4bdaaab57 100644
--- a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilderFactory.java
+++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilderFactory.java
@@ -73,7 +73,7 @@ import org.apache.maven.model.validation.ModelValidator;
 
 /**
  * A factory to create model builder instances when no dependency injection is available. <em>Note:</em> This class is
- * only meant as a utility for developers that want to employ the model builder outside of the Maven build system, Maven
+ * only meant as a utility for developers that want to employ the model builder outside the Maven build system, Maven
  * plugins should always acquire model builder instances via dependency injection. Developers might want to subclass
  * this factory to provide custom implementations for some of the components used by the model builder, or use the
  * builder API to inject custom instances.
@@ -99,6 +99,7 @@ public class DefaultModelBuilderFactory {
     private ReportConfigurationExpander reportConfigurationExpander;
     private ProfileActivationFilePathInterpolator profileActivationFilePathInterpolator;
     private ModelVersionProcessor versionProcessor;
+    private ModelSourceTransformer transformer;
 
     private RootLocator rootLocator;
 
@@ -208,6 +209,11 @@ public class DefaultModelBuilderFactory {
         return this;
     }
 
+    public DefaultModelBuilderFactory setTransformer(ModelSourceTransformer transformer) {
+        this.transformer = transformer;
+        return this;
+    }
+
     protected ModelProcessor newModelProcessor() {
         return new DefaultModelProcessor(newModelLocator(), newModelReader());
     }
@@ -319,7 +325,7 @@ public class DefaultModelBuilderFactory {
     }
 
     private ModelSourceTransformer newModelSourceTransformer() {
-        return new DefaultModelSourceTransformer();
+        return new BuildModelSourceTransformer();
     }
 
     /**
@@ -348,7 +354,8 @@ public class DefaultModelBuilderFactory {
                 profileActivationFilePathInterpolator != null
                         ? profileActivationFilePathInterpolator
                         : newProfileActivationFilePathInterpolator(),
-                versionProcessor != null ? versionProcessor : newModelVersionPropertiesProcessor());
+                versionProcessor != null ? versionProcessor : newModelVersionPropertiesProcessor(),
+                transformer != null ? transformer : newModelSourceTransformer());
     }
 
     private static class StubLifecycleBindingsInjector implements LifecycleBindingsInjector {
diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelProcessor.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelProcessor.java
index 26eb70bb48..0b4935318a 100644
--- a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelProcessor.java
+++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelProcessor.java
@@ -81,7 +81,6 @@ public class DefaultModelProcessor implements ModelProcessor {
 
     public Path locatePom(Path projectDirectory) {
         // Note that the ModelProcessor#locatePom never returns null
-        // while the ModelParser#locatePom needs to return an existing path!
         Path pom = modelLocator.locatePom(projectDirectory.toFile()).toPath();
         if (!pom.equals(projectDirectory) && !pom.getParent().equals(projectDirectory)) {
             throw new IllegalArgumentException("The POM found does not belong to the given directory: " + pom);
diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelSourceTransformer.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelSourceTransformer.java
deleted file mode 100644
index cc52651c97..0000000000
--- a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelSourceTransformer.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * 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.
- */
-package org.apache.maven.model.building;
-
-import javax.xml.stream.XMLStreamReader;
-
-import java.io.IOException;
-import java.nio.file.Path;
-
-/**
- * Default ModelSourceTransformer, provides pomFile as inputStream and ignores the context
- *
- * @since 4.0.0
- */
-public class DefaultModelSourceTransformer implements ModelSourceTransformer {
-
-    @Override
-    public XMLStreamReader transform(XMLStreamReader parser, Path pomFile, TransformerContext context)
-            throws IOException, TransformerException {
-        return parser;
-    }
-}
diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultTransformerContext.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultTransformerContext.java
index 1497b128b3..c0ac5c38c3 100644
--- a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultTransformerContext.java
+++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultTransformerContext.java
@@ -47,6 +47,11 @@ class DefaultTransformerContext implements TransformerContext {
 
         Holder() {}
 
+        Holder(Model model) {
+            this.model = Objects.requireNonNull(model);
+            this.set = true;
+        }
+
         public static Model deref(Holder holder) {
             return holder != null ? holder.get() : null;
         }
diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelBuilder.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelBuilder.java
index f23d368d49..90e702065b 100644
--- a/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelBuilder.java
+++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelBuilder.java
@@ -55,5 +55,8 @@ public interface ModelBuilder {
      */
     Result<? extends Model> buildRawModel(File pomFile, int validationLevel, boolean locationTracking);
 
+    Result<? extends Model> buildRawModel(
+            File pomFile, int validationLevel, boolean locationTracking, TransformerContext context);
+
     TransformerContextBuilder newTransformerContextBuilder();
 }
diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelSourceTransformer.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelSourceTransformer.java
index 9e97096ec0..d279324ef8 100644
--- a/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelSourceTransformer.java
+++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelSourceTransformer.java
@@ -18,15 +18,14 @@
  */
 package org.apache.maven.model.building;
 
-import javax.xml.stream.XMLStreamReader;
-
-import java.io.IOException;
 import java.nio.file.Path;
 
+import org.apache.maven.model.Model;
+
 /**
  * The ModelSourceTransformer is a way to transform the local pom while streaming the input.
  *
- * The {@link #transform(XMLStreamReader, Path, TransformerContext)} method uses a Path on purpose, to ensure the
+ * The {@link #transform(Path, TransformerContext, Model)} method uses a Path on purpose, to ensure the
  * local pom is the original source.
  *
  * @since 4.0.0
@@ -36,10 +35,8 @@ public interface ModelSourceTransformer {
      *
      * @param pomFile the pom file, cannot be null
      * @param context the context, cannot be null
-     * @return the InputStream for the ModelReader
-     * @throws IOException if an I/O error occurs
+     * @param  model the model to transform
      * @throws TransformerException if the transformation fails
      */
-    XMLStreamReader transform(XMLStreamReader parser, Path pomFile, TransformerContext context)
-            throws IOException, TransformerException;
+    void transform(Path pomFile, TransformerContext context, Model model) throws TransformerException;
 }
diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/TransformerContext.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/TransformerContext.java
index 5df4d7ba18..2f9f26f2d0 100644
--- a/maven-model-builder/src/main/java/org/apache/maven/model/building/TransformerContext.java
+++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/TransformerContext.java
@@ -35,14 +35,15 @@ public interface TransformerContext {
     Object KEY = TransformerContext.class;
 
     /**
-     * Get the value of the commandline argument {@code -Dkey=value}
+     * Get the value of the Maven user property.
+     *
      * @param key
      * @return
      */
     String getUserProperty(String key);
 
     /**
-     * Get the model based on the path, will be used to resolve the parent based on relativePath
+     * Get the model based on the path when resolving the parent based on relativePath.
      *
      * @param pomFile the path to the pomFile
      * @return the model, otherwise {@code null}
@@ -50,7 +51,7 @@ public interface TransformerContext {
     Model getRawModel(Path pomFile);
 
     /**
-     * Get the model from the reactor based on the groupId and artifactId, will be used for reactor dependencies
+     * Get the model from the reactor based on the groupId and artifactId when resolving reactor dependencies.
      *
      * @param groupId the groupId
      * @param artifactId the artifactId
@@ -59,5 +60,11 @@ public interface TransformerContext {
      */
     Model getRawModel(String groupId, String artifactId);
 
+    /**
+     * Locate the POM file inside the given directory.
+     *
+     * @param path
+     * @return
+     */
     Path locate(Path path);
 }
diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/io/DefaultModelReader.java b/maven-model-builder/src/main/java/org/apache/maven/model/io/DefaultModelReader.java
index 60d172b7bd..86f140eb74 100644
--- a/maven-model-builder/src/main/java/org/apache/maven/model/io/DefaultModelReader.java
+++ b/maven-model-builder/src/main/java/org/apache/maven/model/io/DefaultModelReader.java
@@ -38,7 +38,6 @@ import java.util.Objects;
 import org.apache.maven.model.InputSource;
 import org.apache.maven.model.Model;
 import org.apache.maven.model.building.ModelSourceTransformer;
-import org.apache.maven.model.building.TransformerContext;
 import org.apache.maven.model.v4.MavenStaxReader;
 
 /**
@@ -96,28 +95,23 @@ public class DefaultModelReader implements ModelReader {
         return (InputSource) value;
     }
 
-    private TransformerContext getTransformerContext(Map<String, ?> options) {
-        Object value = (options != null) ? options.get(TRANSFORMER_CONTEXT) : null;
-        return (TransformerContext) value;
-    }
-
     private Model read(InputStream input, Path pomFile, Map<String, ?> options) throws IOException {
         try {
             XMLInputFactory factory = new com.ctc.wstx.stax.WstxInputFactory();
             factory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, false);
             XMLStreamReader parser = factory.createXMLStreamReader(input);
 
-            TransformerContext context = getTransformerContext(options);
-            XMLStreamReader transformingParser =
-                    context != null ? transformer.transform(parser, pomFile, context) : parser;
-
             InputSource source = getSource(options);
             boolean strict = isStrict(options);
-            if (source != null) {
-                return readModelEx(transformingParser, source, strict);
-            } else {
-                return readModel(transformingParser, strict);
-            }
+            MavenStaxReader mr = new MavenStaxReader();
+            mr.setAddLocationInformation(source != null);
+            Model model = new Model(mr.read(
+                    parser,
+                    strict,
+                    source != null
+                            ? new org.apache.maven.api.model.InputSource(source.getModelId(), source.getLocation())
+                            : null));
+            return model;
         } catch (XMLStreamException e) {
             Location location = e.getLocation();
             throw new ModelParseException(
@@ -125,8 +119,6 @@ public class DefaultModelReader implements ModelReader {
                     location != null ? location.getLineNumber() : -1,
                     location != null ? location.getColumnNumber() : -1,
                     e);
-        } catch (IOException e) {
-            throw e;
         } catch (Exception e) {
             throw new IOException("Unable to transform pom", e);
         }
@@ -138,17 +130,15 @@ public class DefaultModelReader implements ModelReader {
             factory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, false);
             XMLStreamReader parser = factory.createXMLStreamReader(reader);
 
-            TransformerContext context = getTransformerContext(options);
-            XMLStreamReader transformingParser =
-                    context != null ? transformer.transform(parser, pomFile, context) : parser;
-
             InputSource source = getSource(options);
             boolean strict = isStrict(options);
-            if (source != null) {
-                return readModelEx(transformingParser, source, strict);
-            } else {
-                return readModel(transformingParser, strict);
-            }
+            MavenStaxReader mr = new MavenStaxReader();
+            mr.setAddLocationInformation(source != null);
+            Model model = new Model(mr.read(
+                    parser,
+                    strict,
+                    new org.apache.maven.api.model.InputSource(source.getModelId(), source.getLocation())));
+            return model;
         } catch (XMLStreamException e) {
             Location location = e.getLocation();
             throw new ModelParseException(
@@ -156,23 +146,8 @@ public class DefaultModelReader implements ModelReader {
                     location != null ? location.getLineNumber() : -1,
                     location != null ? location.getColumnNumber() : -1,
                     e);
-        } catch (IOException e) {
-            throw e;
         } catch (Exception e) {
             throw new IOException("Unable to transform pom", e);
         }
     }
-
-    private Model readModel(XMLStreamReader parser, boolean strict) throws XMLStreamException, IOException {
-        MavenStaxReader mr = new MavenStaxReader();
-        mr.setAddLocationInformation(false);
-        return new Model(mr.read(parser, strict, null));
-    }
-
-    private Model readModelEx(XMLStreamReader parser, InputSource source, boolean strict)
-            throws XMLStreamException, IOException {
-        MavenStaxReader mr = new MavenStaxReader();
-        return new Model(mr.read(
-                parser, strict, new org.apache.maven.api.model.InputSource(source.getModelId(), source.getLocation())));
-    }
 }
diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/io/ModelReader.java b/maven-model-builder/src/main/java/org/apache/maven/model/io/ModelReader.java
index 277ebce80b..037a7b154f 100644
--- a/maven-model-builder/src/main/java/org/apache/maven/model/io/ModelReader.java
+++ b/maven-model-builder/src/main/java/org/apache/maven/model/io/ModelReader.java
@@ -45,12 +45,6 @@ public interface ModelReader {
      */
     String INPUT_SOURCE = "org.apache.maven.model.io.inputSource";
 
-    /**
-     * The key for the option to provide a transformer context, which can be used to transform the input while reading
-     * to get an advanced version of the model.
-     */
-    String TRANSFORMER_CONTEXT = "transformerContext";
-
     /**
      * Reads the model from the specified file.
      *
diff --git a/maven-model-builder/src/test/java/org/apache/maven/model/building/BuildModelSourceTransformerTest.java b/maven-model-builder/src/test/java/org/apache/maven/model/building/BuildModelSourceTransformerTest.java
new file mode 100644
index 0000000000..50d0534de2
--- /dev/null
+++ b/maven-model-builder/src/test/java/org/apache/maven/model/building/BuildModelSourceTransformerTest.java
@@ -0,0 +1,150 @@
+/*
+ * 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.
+ */
+package org.apache.maven.model.building;
+
+import java.beans.BeanInfo;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collections;
+import java.util.Objects;
+
+import org.apache.maven.model.Model;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class BuildModelSourceTransformerTest {
+
+    Path pomFile;
+    TransformerContext context = org.mockito.Mockito.mock(TransformerContext.class);
+
+    @Test
+    void testModelVersion() {
+        Model initial = new Model(org.apache.maven.api.model.Model.newBuilder()
+                .namespaceUri("http://maven.apache.org/POM/4.0.0")
+                .build());
+        Model expected = new Model(org.apache.maven.api.model.Model.newBuilder()
+                .namespaceUri("http://maven.apache.org/POM/4.0.0")
+                .modelVersion("4.0.0")
+                .build());
+        Model actual = transform(initial);
+        assertTrue(equalsDeep(expected, actual));
+    }
+
+    @Test
+    void testParent() {
+        Path root = Paths.get(".").toAbsolutePath().normalize();
+        pomFile = root.resolve("child/pom.xml");
+        Model parent = new Model(org.apache.maven.api.model.Model.newBuilder()
+                .groupId("GROUPID")
+                .artifactId("ARTIFACTID")
+                .version("1.0-SNAPSHOT")
+                .build());
+        Mockito.when(context.getRawModel(root.resolve("pom.xml"))).thenReturn(parent);
+        Mockito.when(context.locate(root)).thenReturn(root.resolve("pom.xml"));
+        Model initial = new Model(org.apache.maven.api.model.Model.newBuilder()
+                .parent(org.apache.maven.api.model.Parent.newBuilder()
+                        .groupId("GROUPID")
+                        .artifactId("ARTIFACTID")
+                        .build())
+                .build());
+        Model expected = new Model(org.apache.maven.api.model.Model.newBuilder()
+                .parent(org.apache.maven.api.model.Parent.newBuilder()
+                        .groupId("GROUPID")
+                        .artifactId("ARTIFACTID")
+                        .version("1.0-SNAPSHOT")
+                        .build())
+                .build());
+        Model actual = transform(initial);
+        assertTrue(equalsDeep(expected, actual));
+    }
+
+    @Test
+    void testReactorDependencies() {
+        Model dep = new Model(org.apache.maven.api.model.Model.newBuilder()
+                .groupId("GROUPID")
+                .artifactId("ARTIFACTID")
+                .version("1.0-SNAPSHOT")
+                .build());
+        Mockito.when(context.getRawModel("GROUPID", "ARTIFACTID")).thenReturn(dep);
+        Model initial = new Model(org.apache.maven.api.model.Model.newBuilder()
+                .dependencies(Collections.singleton(org.apache.maven.api.model.Dependency.newBuilder()
+                        .groupId("GROUPID")
+                        .artifactId("ARTIFACTID")
+                        .build()))
+                .build());
+        Model expected = new Model(org.apache.maven.api.model.Model.newBuilder()
+                .dependencies(Collections.singleton(org.apache.maven.api.model.Dependency.newBuilder()
+                        .groupId("GROUPID")
+                        .artifactId("ARTIFACTID")
+                        .version("1.0-SNAPSHOT")
+                        .build()))
+                .build());
+        Model actual = transform(initial);
+        assertTrue(equalsDeep(expected, actual));
+    }
+
+    @Test
+    void testCiFriendlyVersion() {
+        Model initial = new Model(org.apache.maven.api.model.Model.newBuilder()
+                .version("${revision}-${sha1}")
+                .build());
+        Mockito.when(context.getUserProperty("revision")).thenReturn("therev");
+        Mockito.when(context.getUserProperty("sha1")).thenReturn("thesha");
+        Model expected = new Model(org.apache.maven.api.model.Model.newBuilder()
+                .version("therev-thesha")
+                .build());
+        Model actual = transform(initial);
+        assertTrue(equalsDeep(expected, actual));
+    }
+
+    Model transform(Model model) {
+        Model transformed = model.clone();
+        new BuildModelSourceTransformer().transform(pomFile, context, transformed);
+        return transformed;
+    }
+
+    public static boolean equalsDeep(Object m1, Object m2) {
+        try {
+            if (m1 == m2) {
+                return true;
+            }
+            if (m1 == null || m2 == null) {
+                return false;
+            }
+            if (!Objects.equals(m1.getClass(), m2.getClass())) {
+                return false;
+            }
+            BeanInfo bean = Introspector.getBeanInfo(m1.getClass());
+            for (PropertyDescriptor prop : bean.getPropertyDescriptors()) {
+                Object p1 = prop.getReadMethod().invoke(m1);
+                Object p2 = prop.getReadMethod().invoke(m2);
+                if (!equalsDeep(p1, p2)) {
+                    return false;
+                }
+            }
+            return true;
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/maven-model-builder/src/test/java/org/apache/maven/model/inheritance/DefaultInheritanceAssemblerTest.java b/maven-model-builder/src/test/java/org/apache/maven/model/inheritance/DefaultInheritanceAssemblerTest.java
index 38e4880e2f..5f9dbde1c6 100644
--- a/maven-model-builder/src/test/java/org/apache/maven/model/inheritance/DefaultInheritanceAssemblerTest.java
+++ b/maven-model-builder/src/test/java/org/apache/maven/model/inheritance/DefaultInheritanceAssemblerTest.java
@@ -18,17 +18,11 @@
  */
 package org.apache.maven.model.inheritance;
 
-import javax.xml.stream.XMLStreamReader;
-
 import java.io.File;
 import java.io.IOException;
-import java.nio.file.Path;
 
 import org.apache.maven.api.model.Model;
-import org.apache.maven.model.building.AbstractModelSourceTransformer;
 import org.apache.maven.model.building.SimpleProblemCollector;
-import org.apache.maven.model.building.TransformerContext;
-import org.apache.maven.model.building.TransformerException;
 import org.apache.maven.model.io.DefaultModelReader;
 import org.apache.maven.model.io.DefaultModelWriter;
 import org.apache.maven.model.io.ModelWriter;
@@ -51,13 +45,7 @@ class DefaultInheritanceAssemblerTest {
 
     @BeforeEach
     void setUp() throws Exception {
-        reader = new DefaultModelReader(new AbstractModelSourceTransformer() {
-            @Override
-            public XMLStreamReader transform(XMLStreamReader parser, Path pomFile, TransformerContext context)
-                    throws IOException, TransformerException {
-                return null;
-            }
-        });
+        reader = new DefaultModelReader(null);
         writer = new DefaultModelWriter();
         assembler = new DefaultInheritanceAssembler();
     }
diff --git a/maven-model-transform/pom.xml b/maven-model-transform/pom.xml
deleted file mode 100644
index 26edb7a2e6..0000000000
--- a/maven-model-transform/pom.xml
+++ /dev/null
@@ -1,64 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-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.
--->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-  <modelVersion>4.0.0</modelVersion>
-  <parent>
-    <groupId>org.apache.maven</groupId>
-    <artifactId>maven</artifactId>
-    <version>4.0.0-alpha-8-SNAPSHOT</version>
-  </parent>
-  <artifactId>maven-model-transform</artifactId>
-  <name>Maven Model XML Transform</name>
-
-  <dependencies>
-    <dependency>
-      <groupId>org.apache.maven</groupId>
-      <artifactId>maven-model</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.codehaus.plexus</groupId>
-      <artifactId>plexus-xml</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.codehaus.woodstox</groupId>
-      <artifactId>stax2-api</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>com.fasterxml.woodstox</groupId>
-      <artifactId>woodstox-core</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.junit.jupiter</groupId>
-      <artifactId>junit-jupiter-params</artifactId>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.xmlunit</groupId>
-      <artifactId>xmlunit-assertj</artifactId>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.mockito</groupId>
-      <artifactId>mockito-core</artifactId>
-      <scope>test</scope>
-    </dependency>
-
-  </dependencies>
-</project>
diff --git a/maven-model-transform/src/main/java/org/apache/maven/model/transform/BuildToRawPomXMLFilterFactory.java b/maven-model-transform/src/main/java/org/apache/maven/model/transform/BuildToRawPomXMLFilterFactory.java
deleted file mode 100644
index d4b23d5f7a..0000000000
--- a/maven-model-transform/src/main/java/org/apache/maven/model/transform/BuildToRawPomXMLFilterFactory.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * 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.
- */
-package org.apache.maven.model.transform;
-
-import javax.xml.stream.XMLStreamReader;
-
-import java.nio.file.Path;
-import java.util.Optional;
-import java.util.function.BiFunction;
-import java.util.function.Function;
-
-/**
- * Base implementation for providing the BuildToRawPomXML.
- *
- * @since 4.0.0
- */
-public class BuildToRawPomXMLFilterFactory {
-    private final boolean consume;
-
-    public BuildToRawPomXMLFilterFactory() {
-        this(false);
-    }
-
-    public BuildToRawPomXMLFilterFactory(boolean consume) {
-        this.consume = consume;
-    }
-
-    /**
-     *
-     * @param projectFile will be used by ConsumerPomXMLFilter to get the right filter
-     */
-    public final XMLStreamReader get(XMLStreamReader orgParser, Path projectFile) {
-
-        // Ensure that xs:any elements aren't touched by next filters
-        XMLStreamReader parser = orgParser instanceof FastForwardFilter ? orgParser : new FastForwardFilter(orgParser);
-
-        if (getDependencyKeyToVersionMapper() != null) {
-            parser = new ReactorDependencyXMLFilter(parser, getDependencyKeyToVersionMapper());
-        }
-
-        if (getRelativePathMapper() != null) {
-            parser = new ParentXMLFilter(parser, getRelativePathMapper(), getModelLocator(), projectFile.getParent());
-        }
-
-        CiFriendlyXMLFilter ciFriendlyFilter = new CiFriendlyXMLFilter(parser, consume);
-        getChangelist().ifPresent(ciFriendlyFilter::setChangelist);
-        getRevision().ifPresent(ciFriendlyFilter::setRevision);
-        getSha1().ifPresent(ciFriendlyFilter::setSha1);
-        parser = ciFriendlyFilter;
-
-        parser = new ModelVersionXMLFilter(parser);
-
-        return parser;
-    }
-
-    /**
-     * @return the mapper or {@code null} if relativePaths don't need to be mapped
-     */
-    protected Function<Path, Optional<RelativeProject>> getRelativePathMapper() {
-        return null;
-    }
-
-    protected Function<Path, Path> getModelLocator() {
-        return null;
-    }
-
-    protected BiFunction<String, String, String> getDependencyKeyToVersionMapper() {
-        return null;
-    }
-
-    // getters for the 3 magic properties of CIFriendly versions ( https://maven.apache.org/maven-ci-friendly.html )
-
-    protected Optional<String> getChangelist() {
-        return Optional.empty();
-    }
-
-    protected Optional<String> getRevision() {
-        return Optional.empty();
-    }
-
-    protected Optional<String> getSha1() {
-        return Optional.empty();
-    }
-}
diff --git a/maven-model-transform/src/main/java/org/apache/maven/model/transform/CiFriendlyXMLFilter.java b/maven-model-transform/src/main/java/org/apache/maven/model/transform/CiFriendlyXMLFilter.java
deleted file mode 100644
index 8f53856a09..0000000000
--- a/maven-model-transform/src/main/java/org/apache/maven/model/transform/CiFriendlyXMLFilter.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * 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.
- */
-package org.apache.maven.model.transform;
-
-import javax.xml.stream.XMLStreamReader;
-
-import java.util.List;
-import java.util.function.Function;
-
-import org.apache.maven.model.transform.stax.NodeBufferingParser;
-
-/**
- * Resolves all ci-friendly properties occurrences between version-tags
- *
- * @since 4.0.0
- */
-class CiFriendlyXMLFilter extends NodeBufferingParser {
-    private final boolean replace;
-
-    private Function<String, String> replaceChain = Function.identity();
-
-    CiFriendlyXMLFilter(XMLStreamReader delegate, boolean replace) {
-        super(delegate, "version");
-        this.replace = replace;
-    }
-
-    public CiFriendlyXMLFilter setChangelist(String changelist) {
-        replaceChain = replaceChain.andThen(t -> t.replace("${changelist}", changelist));
-        return this;
-    }
-
-    public CiFriendlyXMLFilter setRevision(String revision) {
-        replaceChain = replaceChain.andThen(t -> t.replace("${revision}", revision));
-        return this;
-    }
-
-    public CiFriendlyXMLFilter setSha1(String sha1) {
-        replaceChain = replaceChain.andThen(t -> t.replace("${sha1}", sha1));
-        return this;
-    }
-
-    /**
-     * @return {@code true} is any of the ci properties is set, otherwise {@code false}
-     */
-    public boolean isSet() {
-        return !replaceChain.equals(Function.identity());
-    }
-
-    @Override
-    protected void process(List<Event> buffer) {
-        for (Event event : buffer) {
-            if (event.event == CHARACTERS && replace && event.text.contains("${")) {
-                event.text = replaceChain.apply(event.text);
-            }
-            pushEvent(event);
-        }
-    }
-}
diff --git a/maven-model-transform/src/main/java/org/apache/maven/model/transform/FastForwardFilter.java b/maven-model-transform/src/main/java/org/apache/maven/model/transform/FastForwardFilter.java
deleted file mode 100644
index 5d258fcdab..0000000000
--- a/maven-model-transform/src/main/java/org/apache/maven/model/transform/FastForwardFilter.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * 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.
- */
-package org.apache.maven.model.transform;
-
-import javax.xml.stream.XMLStreamException;
-import javax.xml.stream.XMLStreamReader;
-
-import java.util.ArrayDeque;
-import java.util.Deque;
-
-import org.apache.maven.model.transform.stax.BufferingParser;
-
-/**
- * This filter will bypass 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.
- *
- * @since 4.0.0
- */
-class FastForwardFilter extends BufferingParser {
-    /**
-     * 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;
-
-    FastForwardFilter(XMLStreamReader delegate) {
-        super(delegate);
-    }
-
-    @Override
-    public int next() throws XMLStreamException {
-        int event = super.next();
-        filter();
-        return event;
-    }
-
-    protected void filter() throws XMLStreamException {
-        if (delegate.getEventType() == START_ELEMENT) {
-            String localName = delegate.getLocalName();
-            if (domDepth > 0) {
-                domDepth++;
-            } else {
-                final String key = state.peekLast() + '/' + localName;
-                switch (key) {
-                    case "execution/configuration":
-                    case "plugin/configuration":
-                    case "plugin/goals":
-                    case "profile/reports":
-                    case "project/reports":
-                    case "reportSet/configuration":
-                        if (domDepth == 0) {
-                            bypass(true);
-                        }
-                        domDepth++;
-                        break;
-                    default:
-                        break;
-                }
-            }
-            state.add(localName);
-        } else if (delegate.getEventType() == END_ELEMENT) {
-            if (domDepth > 0) {
-                if (--domDepth == 0) {
-                    bypass(false);
-                }
-            }
-            state.removeLast();
-        }
-    }
-
-    @Override
-    public void bypass(boolean bypass) {
-        this.bypass = bypass;
-    }
-}
diff --git a/maven-model-transform/src/main/java/org/apache/maven/model/transform/ModelVersionDowngradeXMLFilter.java b/maven-model-transform/src/main/java/org/apache/maven/model/transform/ModelVersionDowngradeXMLFilter.java
deleted file mode 100644
index 7207fc446b..0000000000
--- a/maven-model-transform/src/main/java/org/apache/maven/model/transform/ModelVersionDowngradeXMLFilter.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * 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.
- */
-package org.apache.maven.model.transform;
-
-import javax.xml.stream.XMLStreamException;
-import javax.xml.stream.XMLStreamReader;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.maven.api.model.Model;
-import org.apache.maven.model.transform.stax.BufferingParser;
-import org.apache.maven.model.v4.MavenModelVersion;
-import org.apache.maven.model.v4.MavenStaxReader;
-
-public class ModelVersionDowngradeXMLFilter extends BufferingParser {
-
-    public static final String NAMESPACE_PREFIX = "http://maven.apache.org/POM/";
-
-    private final List<Event> buffer = new ArrayList<>();
-
-    public ModelVersionDowngradeXMLFilter(XMLStreamReader delegate) {
-        super(delegate);
-    }
-
-    @Override
-    protected boolean accept() throws XMLStreamException {
-        Event e = bufferEvent();
-        buffer.add(e);
-        if (e.event == XMLStreamReader.END_DOCUMENT) {
-            ReplayParser p = new ReplayParser(this);
-            buffer.forEach(p::pushEvent);
-            p.next();
-            String version;
-            Model model = new MavenStaxReader().read(p, false, null);
-            if (model.isPreserveModelVersion()) {
-                version = model.getModelVersion();
-            } else {
-                model = model.withPreserveModelVersion(false);
-                version = new MavenModelVersion().getModelVersion(model);
-            }
-            int depth = 0;
-            boolean isModelVersion = false;
-            for (Event event : buffer) {
-                event.namespace = NAMESPACE_PREFIX + version;
-                // rewrite namespace
-                if (event.namespaces != null) {
-                    for (int i = 0; i < event.namespaces.length; i++) {
-                        if (event.namespaces[i].uri.startsWith(NAMESPACE_PREFIX)) {
-                            event.namespaces[i].uri = event.namespace;
-                        }
-                    }
-                }
-                // rewrite xsi:schemaLocation attribute
-                if (event.attributes != null) {
-                    for (Attribute attribute : event.attributes) {
-                        if (attribute.namespace.equals("http://www.w3.org/2001/XMLSchema-instance")
-                                && attribute.name.equals("schemaLocation")) {
-                            attribute.value = attribute
-                                    .value
-                                    .replaceAll(
-                                            "\\Q" + NAMESPACE_PREFIX + "\\E[0-9]\\.[0-9]\\.[0-9]",
-                                            NAMESPACE_PREFIX + version)
-                                    .replaceAll(
-                                            "http(s?)://maven\\.apache\\.org/xsd/maven-[0-9]\\.[0-9]\\.[0-9]\\.xsd",
-                                            "https://maven.apache.org/xsd/maven-" + version + ".xsd");
-                        }
-                    }
-                }
-                // Rewrite modelVersion
-                if (event.event == XMLStreamReader.START_ELEMENT) {
-                    depth++;
-                    isModelVersion = depth == 2 && event.name.equals("modelVersion");
-                }
-                if (event.event == XMLStreamReader.CHARACTERS && isModelVersion) {
-                    event.text = version;
-                }
-                if (event.event == XMLStreamReader.END_ELEMENT) {
-                    depth--;
-                    isModelVersion = false;
-                }
-                pushEvent(event);
-            }
-        }
-        return false;
-    }
-
-    static class ReplayParser extends BufferingParser {
-        ReplayParser(XMLStreamReader delegate) {
-            super(delegate);
-        }
-
-        public void pushEvent(Event e) {
-            super.pushEvent(e);
-        }
-    }
-}
diff --git a/maven-model-transform/src/main/java/org/apache/maven/model/transform/ModelVersionXMLFilter.java b/maven-model-transform/src/main/java/org/apache/maven/model/transform/ModelVersionXMLFilter.java
deleted file mode 100644
index 1b721f7145..0000000000
--- a/maven-model-transform/src/main/java/org/apache/maven/model/transform/ModelVersionXMLFilter.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * 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.
- */
-package org.apache.maven.model.transform;
-
-import javax.xml.stream.XMLStreamReader;
-
-import java.util.List;
-import java.util.regex.Pattern;
-
-import org.apache.maven.model.transform.stax.NodeBufferingParser;
-
-public class ModelVersionXMLFilter extends NodeBufferingParser {
-
-    public static final String NAMESPACE_PREFIX = "http://maven.apache.org/POM/";
-
-    private static final Pattern S_FILTER = Pattern.compile("\\s+");
-
-    public ModelVersionXMLFilter(XMLStreamReader delegate) {
-        super(delegate, "project");
-    }
-
-    @Override
-    protected void process(List<Event> buffer) {
-        if (buffer.stream().noneMatch(e -> e.event == START_ELEMENT && "modelVersion".equals(e.name))) {
-            String namespace = null;
-            String prefix = null;
-            for (int pos = 0; pos < buffer.size(); pos++) {
-                Event e = buffer.get(pos);
-                if (namespace != null) {
-                    if (e.event == START_ELEMENT) {
-                        Event prev = buffer.get(pos - 1);
-                        if (prev.event != CHARACTERS
-                                || !S_FILTER.matcher(prev.text).matches()) {
-                            prev = null;
-                        }
-                        Event pmse = new Event();
-                        pmse.event = START_ELEMENT;
-                        pmse.name = "modelVersion";
-                        pmse.namespace = namespace;
-                        pmse.prefix = prefix;
-                        buffer.add(pos++, pmse);
-                        Event pmve = new Event();
-                        pmve.event = CHARACTERS;
-                        pmve.text = namespace.substring(NAMESPACE_PREFIX.length());
-                        buffer.add(pos++, pmve);
-                        Event pmee = new Event();
-                        pmee.event = END_ELEMENT;
-                        pmee.name = "modelVersion";
-                        pmee.namespace = namespace;
-                        pmee.prefix = prefix;
-                        buffer.add(pos++, pmee);
-                        if (prev != null) {
-                            buffer.add(pos++, prev);
-                        }
-                        break;
-                    }
-                } else if (e.event == START_ELEMENT
-                        && "project".equals(e.name)
-                        && e.namespace != null
-                        && e.namespace.startsWith(NAMESPACE_PREFIX)) {
-                    namespace = e.namespace;
-                    prefix = e.prefix;
-                }
-            }
-        }
-        buffer.forEach(this::pushEvent);
-    }
-}
diff --git a/maven-model-transform/src/main/java/org/apache/maven/model/transform/ModulesXMLFilter.java b/maven-model-transform/src/main/java/org/apache/maven/model/transform/ModulesXMLFilter.java
deleted file mode 100644
index 5f17788e7e..0000000000
--- a/maven-model-transform/src/main/java/org/apache/maven/model/transform/ModulesXMLFilter.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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.
- */
-package org.apache.maven.model.transform;
-
-import javax.xml.stream.XMLStreamReader;
-
-import java.util.List;
-
-import org.apache.maven.model.transform.stax.NodeBufferingParser;
-
-/**
- * Remove all modules, this is just buildtime information
- *
- * @since 4.0.0
- */
-class ModulesXMLFilter extends NodeBufferingParser {
-    ModulesXMLFilter(XMLStreamReader delegate) {
-        super(delegate, "modules");
-    }
-
-    protected void process(List<Event> buffer) {
-        // Do nothing, as we want to delete those nodes completely
-    }
-}
diff --git a/maven-model-transform/src/main/java/org/apache/maven/model/transform/ParentXMLFilter.java b/maven-model-transform/src/main/java/org/apache/maven/model/transform/ParentXMLFilter.java
deleted file mode 100644
index a9cea3d282..0000000000
--- a/maven-model-transform/src/main/java/org/apache/maven/model/transform/ParentXMLFilter.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * 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.
- */
-package org.apache.maven.model.transform;
-
-import javax.xml.stream.XMLStreamReader;
-
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.List;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.function.Function;
-import java.util.regex.Pattern;
-
-import org.apache.maven.model.transform.stax.NodeBufferingParser;
-
-/**
- * <p>
- * Transforms relativePath to version.
- * We could decide to simply allow {@code <parent/>}, but let's require the GA for now for checking
- * This filter does NOT remove the relativePath (which is done by {@link RelativePathXMLFilter}, it will only
- * optionally include the version based on the path
- * </p>
- *
- * @since 4.0.0
- */
-class ParentXMLFilter extends NodeBufferingParser {
-
-    private final Function<Path, Optional<RelativeProject>> relativePathMapper;
-
-    private final Function<Path, Path> modelLocator;
-
-    private final Path projectPath;
-
-    private static final Pattern S_FILTER = Pattern.compile("\\s+");
-
-    /**
-     * @param relativePathMapper
-     */
-    ParentXMLFilter(
-            XMLStreamReader delegate,
-            Function<Path, Optional<RelativeProject>> relativePathMapper,
-            Function<Path, Path> modelLocator,
-            Path projectPath) {
-        super(delegate, "parent");
-        this.relativePathMapper = relativePathMapper;
-        this.modelLocator = modelLocator;
-        this.projectPath = projectPath;
-    }
-
-    protected void process(List<Event> buffer) {
-        String tagName = null;
-        String groupId = null;
-        String artifactId = null;
-        String version = null;
-        String relativePath = null;
-        String whitespaceAfterParentStart = "";
-        boolean hasVersion = false;
-        boolean hasRelativePath = false;
-        for (int i = 0; i < buffer.size(); i++) {
-            Event event = buffer.get(i);
-            if (event.event == START_ELEMENT) {
-                tagName = event.name;
-                hasVersion |= "version".equals(tagName);
-                hasRelativePath |= "relativePath".equals(tagName);
-            } else if (event.event == CHARACTERS) {
-                if (S_FILTER.matcher(event.text).matches()) {
-                    if (whitespaceAfterParentStart.isEmpty()) {
-                        whitespaceAfterParentStart = event.text;
-                    }
-                } else if ("groupId".equals(tagName)) {
-                    groupId = nullSafeAppend(groupId, event.text);
-                } else if ("artifactId".equals(tagName)) {
-                    artifactId = nullSafeAppend(artifactId, event.text);
-                } else if ("relativePath".equals(tagName)) {
-                    relativePath = nullSafeAppend(relativePath, event.text);
-                } else if ("version".equals(tagName)) {
-                    version = nullSafeAppend(version, event.text);
-                }
-            } else if (event.event == END_ELEMENT && "parent".equals(event.name)) {
-                Optional<RelativeProject> resolvedParent;
-                if (!hasVersion && (!hasRelativePath || relativePath != null)) {
-                    Path relPath = Paths.get(Objects.toString(relativePath, ".."));
-                    resolvedParent = resolveRelativePath(relPath, groupId, artifactId);
-                } else {
-                    resolvedParent = Optional.empty();
-                }
-                if (!hasVersion && resolvedParent.isPresent()) {
-                    int pos = buffer.get(i - 1).event == CHARACTERS ? i - 1 : i;
-                    Event e = new Event();
-                    e.event = CHARACTERS;
-                    e.text = whitespaceAfterParentStart;
-                    buffer.add(pos++, e);
-                    e = new Event();
-                    e.event = START_ELEMENT;
-                    e.namespace = buffer.get(0).namespace;
-                    e.prefix = buffer.get(0).prefix;
-                    e.name = "version";
-                    buffer.add(pos++, e);
-                    e = new Event();
-                    e.event = CHARACTERS;
-                    e.text = resolvedParent.get().getVersion();
-                    buffer.add(pos++, e);
-                    e = new Event();
-                    e.event = END_ELEMENT;
-                    e.name = "version";
-                    e.namespace = buffer.get(0).namespace;
-                    e.prefix = buffer.get(0).prefix;
-                    buffer.add(pos++, e);
-                }
-                break;
-            }
-        }
-        buffer.forEach(this::pushEvent);
-    }
-
-    protected Optional<RelativeProject> resolveRelativePath(Path relativePath, String groupId, String artifactId) {
-        Path pomPath = modelLocator.apply(projectPath.resolve(relativePath).normalize());
-
-        if (pomPath == null) {
-            return Optional.empty();
-        }
-
-        Optional<RelativeProject> mappedProject = relativePathMapper.apply(pomPath.normalize());
-
-        if (mappedProject.isPresent()) {
-            RelativeProject project = mappedProject.get();
-
-            if (Objects.equals(groupId, project.getGroupId()) && Objects.equals(artifactId, project.getArtifactId())) {
-                return mappedProject;
-            }
-        }
-        return Optional.empty();
-    }
-}
diff --git a/maven-model-transform/src/main/java/org/apache/maven/model/transform/RawToConsumerPomXMLFilterFactory.java b/maven-model-transform/src/main/java/org/apache/maven/model/transform/RawToConsumerPomXMLFilterFactory.java
deleted file mode 100644
index b69dcda756..0000000000
--- a/maven-model-transform/src/main/java/org/apache/maven/model/transform/RawToConsumerPomXMLFilterFactory.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * 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.
- */
-package org.apache.maven.model.transform;
-
-import javax.xml.stream.XMLStreamReader;
-
-import java.nio.file.Path;
-
-/**
- * @since 4.0.0
- */
-public class RawToConsumerPomXMLFilterFactory {
-    private BuildToRawPomXMLFilterFactory buildPomXMLFilterFactory;
-
-    public RawToConsumerPomXMLFilterFactory(BuildToRawPomXMLFilterFactory buildPomXMLFilterFactory) {
-        this.buildPomXMLFilterFactory = buildPomXMLFilterFactory;
-    }
-
-    public final XMLStreamReader get(XMLStreamReader orgParser, Path projectPath) {
-        // Ensure that xs:any elements aren't touched by next filters
-        XMLStreamReader parser = orgParser instanceof FastForwardFilter ? orgParser : new FastForwardFilter(orgParser);
-
-        parser = buildPomXMLFilterFactory.get(parser, projectPath);
-
-        // Remove root model attribute
-        parser = new RootXMLFilter(parser);
-        // Strip modules
-        parser = new ModulesXMLFilter(parser);
-        // Adjust relativePath
-        parser = new RelativePathXMLFilter(parser);
-        // Downgrade modelVersion if needed
-        parser = new ModelVersionDowngradeXMLFilter(parser);
-
-        return parser;
-    }
-}
diff --git a/maven-model-transform/src/main/java/org/apache/maven/model/transform/ReactorDependencyXMLFilter.java b/maven-model-transform/src/main/java/org/apache/maven/model/transform/ReactorDependencyXMLFilter.java
deleted file mode 100644
index b1ab8cd068..0000000000
--- a/maven-model-transform/src/main/java/org/apache/maven/model/transform/ReactorDependencyXMLFilter.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * 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.
- */
-package org.apache.maven.model.transform;
-
-import javax.xml.stream.XMLStreamReader;
-
-import java.util.List;
-import java.util.function.BiFunction;
-import java.util.regex.Pattern;
-
-import org.apache.maven.model.transform.stax.NodeBufferingParser;
-
-/**
- * Will apply the version if the dependency is part of the reactor
- *
- * @since 4.0.0
- */
-public class ReactorDependencyXMLFilter extends NodeBufferingParser {
-    private final BiFunction<String, String, String> reactorVersionMapper;
-
-    private static final Pattern S_FILTER = Pattern.compile("\\s+");
-
-    public ReactorDependencyXMLFilter(
-            XMLStreamReader delegate, BiFunction<String, String, String> reactorVersionMapper) {
-        super(delegate, "dependency");
-        this.reactorVersionMapper = reactorVersionMapper;
-    }
-
-    protected void process(List<Event> buffer) {
-        // whiteSpace after <dependency>, to be used to position <version>
-        String dependencyWhitespace = "";
-        boolean hasVersion = false;
-        String groupId = null;
-        String artifactId = null;
-        String tagName = null;
-        for (int i = 0; i < buffer.size(); i++) {
-            Event event = buffer.get(i);
-            if (event.event == START_ELEMENT) {
-                tagName = event.name;
-                hasVersion |= "version".equals(tagName);
-            } else if (event.event == CHARACTERS) {
-                if (S_FILTER.matcher(event.text).matches()) {
-                    if (dependencyWhitespace.isEmpty()) {
-                        dependencyWhitespace = event.text;
-                    }
-                } else if ("groupId".equals(tagName)) {
-                    groupId = nullSafeAppend(groupId, event.text);
-                } else if ("artifactId".equals(tagName)) {
-                    artifactId = nullSafeAppend(artifactId, event.text);
-                }
-            } else if (event.event == END_ELEMENT && "dependency".equals(event.name)) {
-                String version = reactorVersionMapper.apply(groupId, artifactId);
-                if (!hasVersion && version != null) {
-                    int pos = buffer.get(i - 1).event == CHARACTERS ? i - 1 : i;
-                    Event e = new Event();
-                    e.event = CHARACTERS;
-                    e.text = dependencyWhitespace;
-                    buffer.add(pos++, e);
-                    e = new Event();
-                    e.event = START_ELEMENT;
-                    e.namespace = buffer.get(0).namespace;
-                    e.prefix = buffer.get(0).prefix;
-                    e.name = "version";
-                    buffer.add(pos++, e);
-                    e = new Event();
-                    e.event = CHARACTERS;
-                    e.text = version;
-                    buffer.add(pos++, e);
-                    e = new Event();
-                    e.event = END_ELEMENT;
-                    e.name = "version";
-                    e.namespace = buffer.get(0).namespace;
-                    e.prefix = buffer.get(0).prefix;
-                    buffer.add(pos++, e);
-                }
-                break;
-            }
-        }
-        buffer.forEach(this::pushEvent);
-    }
-}
diff --git a/maven-model-transform/src/main/java/org/apache/maven/model/transform/RelativePathXMLFilter.java b/maven-model-transform/src/main/java/org/apache/maven/model/transform/RelativePathXMLFilter.java
deleted file mode 100644
index 774b830a1c..0000000000
--- a/maven-model-transform/src/main/java/org/apache/maven/model/transform/RelativePathXMLFilter.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * 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.
- */
-package org.apache.maven.model.transform;
-
-import javax.xml.stream.XMLStreamReader;
-
-import java.util.List;
-import java.util.regex.Pattern;
-
-import org.apache.maven.model.transform.stax.NodeBufferingParser;
-
-/**
- * Remove relativePath element, has no value for consumer pom
- *
- * @since 4.0.0
- */
-public class RelativePathXMLFilter extends NodeBufferingParser {
-
-    private static final Pattern S_FILTER = Pattern.compile("\\s+");
-
-    public RelativePathXMLFilter(XMLStreamReader delegate) {
-        super(delegate, "parent");
-    }
-
-    protected void process(List<Event> buffer) {
-        boolean skip = false;
-        Event prev = null;
-        for (Event event : buffer) {
-            if (event.event == START_ELEMENT && "relativePath".equals(event.name)) {
-                skip = true;
-                if (prev != null
-                        && prev.event == CHARACTERS
-                        && S_FILTER.matcher(prev.text).matches()) {
-                    prev = null;
-                }
-                event = null;
-            } else if (event.event == END_ELEMENT && "relativePath".equals(event.name)) {
-                skip = false;
-                event = null;
-            } else if (skip) {
-                event = null;
-            }
-            if (prev != null) {
-                pushEvent(prev);
-            }
-            prev = event;
-        }
-        pushEvent(prev);
-    }
-}
diff --git a/maven-model-transform/src/main/java/org/apache/maven/model/transform/RelativeProject.java b/maven-model-transform/src/main/java/org/apache/maven/model/transform/RelativeProject.java
deleted file mode 100644
index 3a4d565f0c..0000000000
--- a/maven-model-transform/src/main/java/org/apache/maven/model/transform/RelativeProject.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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.
- */
-package org.apache.maven.model.transform;
-
-/**
- *
- * @since 4.0.0
- */
-public class RelativeProject {
-    private final String groupId;
-
-    private final String artifactId;
-
-    private final String version;
-
-    public RelativeProject(String groupId, String artifactId, String version) {
-        this.groupId = groupId;
-        this.artifactId = artifactId;
-        this.version = version;
-    }
-
-    public String getGroupId() {
-        return groupId;
-    }
-
-    public String getArtifactId() {
-        return artifactId;
-    }
-
-    public String getVersion() {
-        return version;
-    }
-}
diff --git a/maven-model-transform/src/main/java/org/apache/maven/model/transform/RootXMLFilter.java b/maven-model-transform/src/main/java/org/apache/maven/model/transform/RootXMLFilter.java
deleted file mode 100644
index d2794a5e81..0000000000
--- a/maven-model-transform/src/main/java/org/apache/maven/model/transform/RootXMLFilter.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * 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.
- */
-package org.apache.maven.model.transform;
-
-import javax.xml.stream.XMLStreamException;
-import javax.xml.stream.XMLStreamReader;
-
-import java.util.ArrayDeque;
-import java.util.Deque;
-import java.util.stream.Stream;
-
-import org.apache.maven.model.transform.stax.BufferingParser;
-
-/**
- * Remove the root attribute on the model
- *
- * @since 4.0.0
- */
-class RootXMLFilter extends BufferingParser {
-
-    final Deque<String> elements = new ArrayDeque<>();
-
-    RootXMLFilter(XMLStreamReader delegate) {
-        super(delegate);
-    }
-
-    @Override
-    protected boolean accept() throws XMLStreamException {
-        if (delegate.getEventType() == START_ELEMENT) {
-            elements.push(delegate.getLocalName());
-            if (elements.size() == 1 && "project".equals(delegate.getLocalName())) {
-                Event event = bufferEvent();
-                event.attributes = Stream.of(event.attributes)
-                        .filter(a -> !"root".equals(a.name))
-                        .toArray(Attribute[]::new);
-                pushEvent(event);
-                return false;
-            }
-        } else if (delegate.getEventType() == END_ELEMENT) {
-            elements.pop();
-        }
-        return true;
-    }
-}
diff --git a/maven-model-transform/src/main/java/org/apache/maven/model/transform/stax/BufferingParser.java b/maven-model-transform/src/main/java/org/apache/maven/model/transform/stax/BufferingParser.java
deleted file mode 100644
index a83458f66d..0000000000
--- a/maven-model-transform/src/main/java/org/apache/maven/model/transform/stax/BufferingParser.java
+++ /dev/null
@@ -1,523 +0,0 @@
-/*
- * 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.
- */
-package org.apache.maven.model.transform.stax;
-
-import javax.xml.namespace.NamespaceContext;
-import javax.xml.namespace.QName;
-import javax.xml.stream.Location;
-import javax.xml.stream.XMLStreamException;
-import javax.xml.stream.XMLStreamReader;
-
-import java.util.ArrayDeque;
-import java.util.Arrays;
-import java.util.Deque;
-import java.util.Objects;
-import java.util.regex.Pattern;
-
-import org.codehaus.stax2.XMLStreamReader2;
-
-public class BufferingParser implements XMLStreamReader {
-
-    private static final Pattern WHITESPACE_REGEX = Pattern.compile("[ \r\t\n]+");
-
-    private static final String[] TYPES = new String[] {
-        "",
-        "START_ELEMENT",
-        "END_ELEMENT",
-        "PROCESSING_INSTRUCTION",
-        "CHARACTERS",
-        "COMMENT",
-        "SPACE",
-        "START_DOCUMENT",
-        "END_DOCUMENT",
-        "ENTITY_REFERENCE",
-        "ATTRIBUTE",
-        "DTD",
-        "CDATA",
-        "NAMESPACE",
-        "NOTATION_DECLARATION",
-        "ENTITY_DECLARATION"
-    };
-
-    protected XMLStreamReader delegate;
-    protected Deque<Event> events;
-    protected Event current;
-    protected boolean bypass;
-
-    @SuppressWarnings("checkstyle:VisibilityModifier")
-    public static class Event {
-        public int event;
-        public String name;
-        public String prefix;
-        public String namespace;
-        public boolean empty;
-        public String text;
-        public Attribute[] attributes;
-        public Namespace[] namespaces;
-        public int columnNumber;
-        public int lineNumber;
-
-        public String positionDescription() {
-            return " " + TYPES[event] + " @" + lineNumber + ":" + columnNumber;
-        }
-
-        @Override
-        public String toString() {
-            switch (event) {
-                case START_DOCUMENT:
-                case END_DOCUMENT:
-                    return "Event{event=" + TYPES[event] + "'}";
-                case PROCESSING_INSTRUCTION:
-                case CHARACTERS:
-                case CDATA:
-                case ENTITY_REFERENCE:
-                case COMMENT:
-                case SPACE:
-                    return "Event{event=" + TYPES[event] + ", text='" + text + "'}";
-                case START_ELEMENT:
-                    return "Event{" + "event=START_TAG"
-                            + ", name='"
-                            + name + '\'' + ", prefix='"
-                            + prefix + '\'' + ", namespace='"
-                            + namespace + '\'' + ", empty="
-                            + empty + ", attributes="
-                            + Arrays.toString(attributes) + ", namespaces="
-                            + Arrays.toString(namespaces) + '}';
-                case END_ELEMENT:
-                    return "Event{" + "event=END_TAG"
-                            + ", name='"
-                            + name + '\'' + ", prefix='"
-                            + prefix + '\'' + ", namespace='"
-                            + namespace + '\'' + ", empty="
-                            + empty + ", namespaces="
-                            + Arrays.toString(namespaces) + '}';
-                default:
-                    return "Event{" + "event="
-                            + TYPES[event] + ", name='"
-                            + name + '\'' + ", prefix='"
-                            + prefix + '\'' + ", namespace='"
-                            + namespace + '\'' + ", empty="
-                            + empty + ", text='"
-                            + text + '\'' + ", attributes="
-                            + Arrays.toString(attributes) + ", namespaces="
-                            + Arrays.toString(namespaces) + '}';
-            }
-        }
-    }
-
-    @SuppressWarnings("checkstyle:VisibilityModifier")
-    public static class Namespace {
-        public String prefix;
-        public String uri;
-    }
-
-    @SuppressWarnings("checkstyle:VisibilityModifier")
-    public static class Attribute {
-        public String name;
-        public String prefix;
-        public String namespace;
-        public String type;
-        public String value;
-    }
-
-    public BufferingParser(XMLStreamReader delegate) {
-        this.delegate = delegate;
-    }
-
-    @Override
-    public Object getProperty(String name) throws IllegalArgumentException {
-        return delegate.getProperty(name);
-    }
-
-    @Override
-    public String getElementText() throws XMLStreamException {
-        return delegate.getElementText();
-    }
-
-    @Override
-    public boolean hasNext() throws XMLStreamException {
-        return delegate.hasNext();
-    }
-
-    @Override
-    public void close() throws XMLStreamException {
-        delegate.close();
-    }
-
-    @Override
-    public String getNamespaceURI(String prefix) {
-        return delegate.getNamespaceURI(prefix);
-    }
-
-    @Override
-    public boolean isStartElement() {
-        return delegate.isStartElement();
-    }
-
-    @Override
-    public boolean isEndElement() {
-        return delegate.isEndElement();
-    }
-
-    @Override
-    public boolean isCharacters() {
-        return delegate.isCharacters();
-    }
-
-    @Override
-    public boolean isWhiteSpace() {
-        return delegate.isWhiteSpace();
-    }
-
-    @Override
-    public QName getAttributeName(int index) {
-        return delegate.getAttributeName(index);
-    }
-
-    @Override
-    public boolean isAttributeSpecified(int index) {
-        return delegate.isAttributeSpecified(index);
-    }
-
-    @Override
-    public int getNamespaceCount() {
-        return current != null
-                ? current.namespaces != null ? current.namespaces.length : 0
-                : delegate.getNamespaceCount();
-    }
-
-    @Override
-    public String getNamespacePrefix(int index) {
-        return current != null ? current.namespaces[index].prefix : delegate.getNamespacePrefix(index);
-    }
-
-    @Override
-    public String getNamespaceURI(int index) {
-        return current != null ? current.namespaces[index].uri : delegate.getNamespaceURI(index);
-    }
-
-    @Override
-    public NamespaceContext getNamespaceContext() {
-        return delegate.getNamespaceContext();
-    }
-
-    @Override
-    public char[] getTextCharacters() {
-        return delegate.getTextCharacters();
-    }
-
-    @Override
-    public int getTextCharacters(int sourceStart, char[] target, int targetStart, int length)
-            throws XMLStreamException {
-        return delegate.getTextCharacters(sourceStart, target, targetStart, length);
-    }
-
-    @Override
-    public int getTextStart() {
-        return delegate.getTextStart();
-    }
-
-    @Override
-    public int getTextLength() {
-        return delegate.getTextLength();
-    }
-
-    @Override
-    public String getEncoding() {
-        return delegate.getEncoding();
-    }
-
-    @Override
-    public boolean hasText() {
-        return delegate.hasText();
-    }
-
-    @Override
-    public Location getLocation() {
-        return delegate.getLocation();
-    }
-
-    @Override
-    public QName getName() {
-        return delegate.getName();
-    }
-
-    @Override
-    public boolean hasName() {
-        return delegate.hasName();
-    }
-
-    @Override
-    public String getVersion() {
-        return delegate.getVersion();
-    }
-
-    @Override
-    public boolean isStandalone() {
-        return delegate.isStandalone();
-    }
-
-    @Override
-    public boolean standaloneSet() {
-        return delegate.standaloneSet();
-    }
-
-    @Override
-    public String getCharacterEncodingScheme() {
-        return delegate.getCharacterEncodingScheme();
-    }
-
-    @Override
-    public String getPITarget() {
-        return delegate.getPITarget();
-    }
-
-    @Override
-    public String getPIData() {
-        return delegate.getPIData();
-    }
-
-    @Override
-    public String getText() {
-        return current != null ? current.text : delegate.getText();
-    }
-
-    @Override
-    public String getNamespaceURI() {
-        return current != null ? current.namespace : delegate.getNamespaceURI();
-    }
-
-    @Override
-    public String getLocalName() {
-        return current != null ? current.name : delegate.getLocalName();
-    }
-
-    @Override
-    public String getPrefix() {
-        return current != null ? current.prefix : delegate.getPrefix();
-    }
-
-    @Override
-    public int getAttributeCount() {
-        if (current != null) {
-            return current.attributes != null ? current.attributes.length : 0;
-        } else {
-            return delegate.getAttributeCount();
-        }
-    }
-
-    @Override
-    public String getAttributeNamespace(int index) {
-        if (current != null) {
-            return current.attributes[index].namespace;
-        } else {
-            return delegate.getAttributeNamespace(index);
-        }
-    }
-
-    @Override
-    public String getAttributeLocalName(int index) {
-        if (current != null) {
-            return current.attributes[index].name;
-        } else {
-            return delegate.getAttributeLocalName(index);
-        }
-    }
-
-    @Override
-    public String getAttributePrefix(int index) {
-        if (current != null) {
-            return current.attributes[index].prefix;
-        } else {
-            return delegate.getAttributePrefix(index);
-        }
-    }
-
-    @Override
-    public String getAttributeType(int index) {
-        if (current != null) {
-            return current.attributes[index].type;
-        } else {
-            return delegate.getAttributeType(index);
-        }
-    }
-
-    @Override
-    public String getAttributeValue(int index) {
-        if (current != null) {
-            return current.attributes[index].value;
-        } else {
-            return delegate.getAttributeValue(index);
-        }
-    }
-
-    @Override
-    public String getAttributeValue(String namespace, String name) {
-        if (current != null) {
-            if (current.attributes != null) {
-                for (Attribute attr : current.attributes) {
-                    if (Objects.equals(namespace, attr.namespace) && Objects.equals(name, attr.name)) {
-                        return attr.value;
-                    }
-                }
-            }
-            return null;
-        } else {
-            return delegate.getAttributeValue(namespace, name);
-        }
-    }
-
-    @Override
-    public void require(int type, String namespace, String name) throws XMLStreamException {
-        if (current != null) {
-            throw new IllegalStateException("Not supported during events replay");
-        }
-        delegate.require(type, namespace, name);
-    }
-
-    @Override
-    public int getEventType() {
-        return current != null ? current.event : delegate.getEventType();
-    }
-
-    @Override
-    public int next() throws XMLStreamException {
-        while (true) {
-            if (events != null && !events.isEmpty()) {
-                current = events.removeFirst();
-                return current.event;
-            } else {
-                current = null;
-            }
-            if (getEventType() == END_DOCUMENT) {
-                throw new XMLStreamException("already reached end of XML input", getLocation(), null);
-            }
-            int currentEvent = delegate.next();
-            if (bypass() || accept()) {
-                return currentEvent;
-            }
-        }
-    }
-
-    @Override
-    public int nextTag() throws XMLStreamException {
-        int eventType = next();
-        while (eventType == CHARACTERS && isWhitespace() // skip whitespace
-                || eventType == COMMENT) { // skip comments
-            eventType = next();
-        }
-        if (eventType != START_ELEMENT && eventType != END_ELEMENT) {
-            throw new XMLStreamException(
-                    "expected START_TAG or END_TAG not " + TYPES[getEventType()], getLocation(), null);
-        }
-        return eventType;
-    }
-
-    public boolean isWhitespace() throws XMLStreamException {
-        if (getEventType() == CHARACTERS || getEventType() == CDATA) {
-            return WHITESPACE_REGEX.matcher(getText()).matches();
-        } else if (getEventType() == SPACE) {
-            return true;
-        } else {
-            throw new XMLStreamException("no content available to check for whitespaces");
-        }
-    }
-
-    protected Event bufferEvent() throws XMLStreamException {
-        Event event = new Event();
-        XMLStreamReader pp = delegate;
-        event.event = delegate.getEventType();
-        event.columnNumber = delegate.getLocation().getColumnNumber();
-        event.lineNumber = delegate.getLocation().getLineNumber();
-        switch (event.event) {
-            case START_DOCUMENT:
-            case END_DOCUMENT:
-                break;
-            case START_ELEMENT:
-                event.name = pp.getLocalName();
-                event.namespace = pp.getNamespaceURI();
-                event.prefix = pp.getPrefix();
-                event.empty = (pp instanceof XMLStreamReader2) && ((XMLStreamReader2) pp).isEmptyElement();
-                // event.text = pp.getText();
-                event.attributes = new Attribute[pp.getAttributeCount()];
-                for (int i = 0; i < pp.getAttributeCount(); i++) {
-                    Attribute attr = new Attribute();
-                    attr.name = pp.getAttributeLocalName(i);
-                    attr.namespace = pp.getAttributeNamespace(i);
-                    attr.value = pp.getAttributeValue(i);
-                    attr.type = pp.getAttributeType(i);
-                    event.attributes[i] = attr;
-                }
-                event.namespaces = new Namespace[pp.getNamespaceCount()];
-                for (int i = 0; i < pp.getNamespaceCount(); i++) {
-                    Namespace ns = new Namespace();
-                    ns.uri = pp.getNamespaceURI(i);
-                    ns.prefix = pp.getNamespacePrefix(i);
-                    event.namespaces[i] = ns;
-                }
-                break;
-            case END_ELEMENT:
-                event.name = pp.getLocalName();
-                event.namespace = pp.getNamespaceURI();
-                event.prefix = pp.getPrefix();
-                // event.text = pp.getText();
-                break;
-            case CHARACTERS:
-            case COMMENT:
-            case SPACE:
-            case CDATA:
-            case ENTITY_REFERENCE:
-                event.text = pp.getText();
-                break;
-            default:
-                break;
-        }
-        return event;
-    }
-
-    protected void pushEvent(Event event) {
-        if (events == null) {
-            events = new ArrayDeque<>();
-        }
-        events.add(event);
-    }
-
-    protected boolean accept() throws XMLStreamException {
-        return true;
-    }
-
-    public void bypass(boolean bypass) {
-        if (bypass && events != null && !events.isEmpty()) {
-            throw new IllegalStateException("Can not disable filter while processing");
-        }
-        this.bypass = bypass;
-    }
-
-    public boolean bypass() {
-        return bypass || (delegate instanceof BufferingParser && ((BufferingParser) delegate).bypass());
-    }
-
-    protected static String nullSafeAppend(String originalValue, String charSegment) {
-        if (originalValue == null) {
-            return charSegment;
-        } else {
-            return originalValue + charSegment;
-        }
-    }
-}
diff --git a/maven-model-transform/src/main/java/org/apache/maven/model/transform/stax/NodeBufferingParser.java b/maven-model-transform/src/main/java/org/apache/maven/model/transform/stax/NodeBufferingParser.java
deleted file mode 100644
index c31435512a..0000000000
--- a/maven-model-transform/src/main/java/org/apache/maven/model/transform/stax/NodeBufferingParser.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * 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.
- */
-package org.apache.maven.model.transform.stax;
-
-import javax.xml.stream.XMLStreamException;
-import javax.xml.stream.XMLStreamReader;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * Buffer events while parsing a given element to allow some post-processing.
- *
- * @since 4.0.0
- */
-public abstract class NodeBufferingParser extends BufferingParser {
-
-    private final List<Event> buffer = new ArrayList<>();
-
-    private final String nodeName;
-
-    private boolean buffering;
-
-    public NodeBufferingParser(XMLStreamReader delegate, String nodeName) {
-        super(delegate);
-        this.nodeName = Objects.requireNonNull(nodeName);
-    }
-
-    @Override
-    protected boolean accept() throws XMLStreamException {
-        if (!buffering && delegate.getEventType() == START_ELEMENT && nodeName.equals(delegate.getLocalName())) {
-            buffer.add(bufferEvent());
-            buffering = true;
-            return false;
-        } else if (buffering && delegate.getEventType() == END_ELEMENT && nodeName.equals(delegate.getLocalName())) {
-            buffer.add(bufferEvent());
-            process(buffer);
-            buffering = false;
-            buffer.clear();
-            return false;
-        } else if (buffering) {
-            buffer.add(bufferEvent());
-            return false;
-        } else {
-            return true;
-        }
-    }
-
-    @Override
-    public boolean bypass() {
-        return !buffering && super.bypass();
-    }
-
-    protected abstract void process(List<Event> buffer);
-}
diff --git a/maven-model-transform/src/main/java/org/apache/maven/model/transform/stax/XmlUtils.java b/maven-model-transform/src/main/java/org/apache/maven/model/transform/stax/XmlUtils.java
deleted file mode 100644
index 48e7a9b14d..0000000000
--- a/maven-model-transform/src/main/java/org/apache/maven/model/transform/stax/XmlUtils.java
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * 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.
- */
-package org.apache.maven.model.transform.stax;
-
-import javax.xml.stream.XMLOutputFactory;
-import javax.xml.stream.XMLStreamConstants;
-import javax.xml.stream.XMLStreamException;
-import javax.xml.stream.XMLStreamReader;
-import javax.xml.stream.XMLStreamWriter;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.ArrayDeque;
-import java.util.Deque;
-
-import com.ctc.wstx.api.WstxOutputProperties;
-
-public class XmlUtils {
-
-    public static InputStream writeDocument(XMLStreamReader parser) throws XMLStreamException {
-        ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        writeDocument(parser, baos);
-        return new ByteArrayInputStream(baos.toByteArray());
-    }
-
-    public static void writeDocument(XMLStreamReader parser, OutputStream output) throws XMLStreamException {
-        XMLOutputFactory factory = new com.ctc.wstx.stax.WstxOutputFactory();
-        factory.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, false);
-        factory.setProperty(WstxOutputProperties.P_USE_DOUBLE_QUOTES_IN_XML_DECL, true);
-        factory.setProperty(WstxOutputProperties.P_ADD_SPACE_AFTER_EMPTY_ELEM, true);
-        XMLStreamWriter serializer = factory.createXMLStreamWriter(output, parser.getCharacterEncodingScheme());
-        copy(parser, serializer);
-    }
-
-    private static String normalize(String input) {
-        if (input != null) {
-            return input.replace("\r\n", "\n");
-        }
-        return input;
-    }
-
-    /**
-     * Copies the reader to the writer. The start and end document methods must
-     * be handled on the writer manually.
-     */
-    public static void copy(XMLStreamReader reader, XMLStreamWriter writer) throws XMLStreamException {
-        copy(reader, writer, false, false);
-    }
-
-    public static void copy(XMLStreamReader reader, XMLStreamWriter writer, boolean fragment)
-            throws XMLStreamException {
-        copy(reader, writer, fragment, false);
-    }
-
-    public static void copy(XMLStreamReader reader, XMLStreamWriter writer, boolean fragment, boolean isThreshold)
-            throws XMLStreamException {
-        // number of elements read in
-        int read = 0;
-        int elementCount = 0;
-        final Deque<Integer> countStack = new ArrayDeque<>();
-        int event = reader.getEventType();
-
-        while (true) {
-            switch (event) {
-                case XMLStreamConstants.START_ELEMENT:
-                    read++;
-                    if (isThreshold) {
-                        elementCount++;
-                        countStack.push(elementCount);
-                        elementCount = 0;
-                    }
-                    writeStartElement(reader, writer);
-                    break;
-                case XMLStreamConstants.END_ELEMENT:
-                    if (read > 0) {
-                        writer.writeEndElement();
-                    }
-                    read--;
-                    if (read < 0 && fragment) {
-                        return;
-                    }
-                    if (isThreshold && !countStack.isEmpty()) {
-                        elementCount = countStack.pop();
-                    }
-                    break;
-                case XMLStreamConstants.CHARACTERS:
-                    writer.writeCharacters(normalize(reader.getText()));
-                    break;
-                case XMLStreamConstants.SPACE:
-                    writer.writeCharacters(normalize(reader.getText()));
-                    break;
-                case XMLStreamConstants.ENTITY_REFERENCE:
-                    writer.writeEntityRef(reader.getLocalName());
-                    break;
-                case XMLStreamConstants.COMMENT:
-                    writer.writeComment(normalize(reader.getText()));
-                    break;
-                case XMLStreamConstants.CDATA:
-                    writer.writeCData(normalize(reader.getText()));
-                    break;
-                case XMLStreamConstants.START_DOCUMENT:
-                    if (reader.getVersion() != null) {
-                        writer.writeStartDocument(reader.getCharacterEncodingScheme(), reader.getVersion());
-                    }
-                    break;
-                case XMLStreamConstants.END_DOCUMENT:
-                    writer.writeEndDocument();
-                    return;
-                default:
-                    break;
-            }
-            event = reader.next();
-        }
-    }
-
-    private static void writeStartElement(XMLStreamReader reader, XMLStreamWriter writer) throws XMLStreamException {
-        String uri = reader.getNamespaceURI();
-        String prefix = reader.getPrefix();
-        String local = reader.getLocalName();
-
-        if (prefix == null) {
-            prefix = "";
-        }
-
-        // Write out the element name
-        if (uri != null) {
-            if (prefix.isEmpty() && isEmpty(uri)) {
-                writer.writeStartElement(local);
-            } else {
-                writer.writeStartElement(prefix, local, uri);
-            }
-        } else {
-            writer.writeStartElement(local);
-        }
-
-        // Write out the namespaces
-        for (int i = 0; i < reader.getNamespaceCount(); i++) {
-            String nsURI = reader.getNamespaceURI(i);
-            String nsPrefix = reader.getNamespacePrefix(i);
-            if (nsURI == null) {
-                nsURI = "";
-            }
-            writer.writeNamespace(nsPrefix, nsURI);
-            writer.setPrefix(nsPrefix, nsURI);
-        }
-
-        // Write out attributes
-        for (int i = 0; i < reader.getAttributeCount(); i++) {
-            String ns = reader.getAttributeNamespace(i);
-            String nsPrefix = reader.getAttributePrefix(i);
-            if (ns == null || ns.isEmpty()) {
-                writer.writeAttribute(reader.getAttributeLocalName(i), reader.getAttributeValue(i));
-            } else if (nsPrefix == null || nsPrefix.isEmpty()) {
-                writer.writeAttribute(
-                        reader.getAttributeNamespace(i), reader.getAttributeLocalName(i), reader.getAttributeValue(i));
-            } else {
-                writer.writeAttribute(
-                        reader.getAttributePrefix(i),
-                        reader.getAttributeNamespace(i),
-                        reader.getAttributeLocalName(i),
-                        reader.getAttributeValue(i));
-            }
-        }
-    }
-
-    private static boolean isEmpty(String s) {
-        return s == null || s.isEmpty();
-    }
-}
diff --git a/maven-model-transform/src/site/apt/index.apt b/maven-model-transform/src/site/apt/index.apt
deleted file mode 100644
index fa329c62dc..0000000000
--- a/maven-model-transform/src/site/apt/index.apt
+++ /dev/null
@@ -1,73 +0,0 @@
-~~ 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.
-
- -----
- Introduction
- -----
- Hervé Boutemy
- -----
- 2021-04-04
- -----
-
-Maven Model XML Transformer
-
- Maven Model XML Transformer provides build/consumer <<<pom.xml>>> transformers.
-
- In order to keep formatting, comments and every detail when transforming a build <<<pom.xml>>> to a consumer <<<pom.xml>>>, transformation happens directly on the stream of SAX events.
-
- There are 3 states of a <<<pom.xml>>> content:
-
- [[1]] the <<build>> <<<pom.xml>>>, as it is stored on disk during development and in source control, which can be simplified to ease source code maintenance,
-
- [[2]] the <<raw>> content (usually not saved to a file), which is enriched from initial build content, to match Maven Model validation rules,
-
- [[3]] the <<consumer>> <<<pom.xml>>>, as it is saved to local repository or remote repository, to be used as dependencies descriptor when consumed by a project.
-
- []
-
- Transformation is implemented as two filters:
-
- * <<build to raw>> in <<<BuildToRawPomXMLFilter>>> ({{{./apidocs/org/apache/maven/model/transform/BuildPomToRawXMLFilter.html}javadoc}}),
-   with its <<<BuildToRawPomXMLFilterFactory>>> ({{{./xref/org/apache/maven/model/transform/BuildToRawPomXMLFilterFactory.html}source}}) assembling transformation steps,
-
-   * in a multi-module build, <<<parent>>>'s <<<version>>> is automatically added,
-
-   * in a multi-module build, dependencies <<<version>>> is automatically added for reactor modules,
-
-   * CI-friendly <<<$\{sha1}>>>, <<<$\{revision}>>> and <<<$\{changelist}>>> are properties are replaced with their value,
-
-   []
-
- * <<raw to consumer>> in <<<RawToConsumerPomXMLFilter>>>  ({{{./apidocs/org/apache/maven/model/transform/RawToConsumerPomXMLFilter.html}javadoc}}),
-   with its <<<RawToConsumerPomXMLFilterFactory>>> ({{{./xref/org/apache/maven/model/transform/RawToConsumerPomXMLFilterFactory.html}source}}) assembling transformation steps.
-
-   * <<<modules>>> is stripped because it only has a meaning at build time on disk, but not once mapped to repository format,
-
-   * <<<parent>>>'s <<<relativePath>>> is stripped because it only has a meaning at build time on disk, but not once mapped to repository format.
-
-   []
-
- []
-
- For Maven 4, every state of Maven Model remains with the same <<<maven-4.0.0.xsd>>> schema, but it the future Maven 5+:
-
- * build model should evolve to add new features configuration in new <v5> model fields, or remove some old unused fields,
-
- * consumer model should at least continue to produce a <<<maven-4.0.0.xsd>>>-compliant <<<pom.xml>>> for compatibility with the vast and diverse dependency consumers ecosystem,
-   but may also produce in parallel new consumption formats (yet to be defined).
-
- []
diff --git a/maven-model-transform/src/site/site.xml b/maven-model-transform/src/site/site.xml
deleted file mode 100644
index 8ffe43d07c..0000000000
--- a/maven-model-transform/src/site/site.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!--
-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.
--->
-
-<project xmlns="http://maven.apache.org/DECORATION/1.8.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-  xsi:schemaLocation="http://maven.apache.org/DECORATION/1.8.0 http://maven.apache.org/xsd/decoration-1.8.0.xsd">
-
-  <edit>${project.scm.url}</edit>
-
-  <body>
-    <menu name="Overview">
-      <item name="Introduction" href="index.html"/>
-      <item name="Javadocs" href="apidocs/index.html"/>
-      <item name="Source Xref" href="xref/index.html"/>
-      <!--item name="FAQ" href="faq.html"/-->
-    </menu>
-
-    <menu ref="parent"/>
-    <menu ref="reports"/>
-  </body>
-</project>
\ No newline at end of file
diff --git a/maven-model-transform/src/test/java/org/apache/maven/model/transform/AbstractXMLFilterTests.java b/maven-model-transform/src/test/java/org/apache/maven/model/transform/AbstractXMLFilterTests.java
deleted file mode 100644
index 9ed0e731d2..0000000000
--- a/maven-model-transform/src/test/java/org/apache/maven/model/transform/AbstractXMLFilterTests.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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.
- */
-package org.apache.maven.model.transform;
-
-import javax.xml.stream.XMLInputFactory;
-import javax.xml.stream.XMLStreamException;
-import javax.xml.stream.XMLStreamReader;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.Reader;
-import java.io.StringReader;
-
-import org.apache.maven.model.transform.stax.XmlUtils;
-
-public abstract class AbstractXMLFilterTests {
-    protected XMLStreamReader getFilter(XMLStreamReader parser) {
-        throw new UnsupportedOperationException("Override one of the getFilter() methods");
-    }
-
-    protected String transform(String input) throws XMLStreamException, IOException {
-        return transform(new StringReader(input));
-    }
-
-    protected String transform(Reader input) throws XMLStreamException, IOException {
-        XMLStreamReader parser = XMLInputFactory.newFactory().createXMLStreamReader(input);
-        XMLStreamReader filter = getFilter(parser);
-
-        ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        XmlUtils.writeDocument(filter, baos);
-        return baos.toString();
-    }
-}
diff --git a/maven-model-transform/src/test/java/org/apache/maven/model/transform/CiFriendlyXMLFilterTest.java b/maven-model-transform/src/test/java/org/apache/maven/model/transform/CiFriendlyXMLFilterTest.java
deleted file mode 100644
index e33b35e72e..0000000000
--- a/maven-model-transform/src/test/java/org/apache/maven/model/transform/CiFriendlyXMLFilterTest.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * 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.
- */
-package org.apache.maven.model.transform;
-
-import javax.xml.stream.XMLStreamReader;
-
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-class CiFriendlyXMLFilterTest extends AbstractXMLFilterTests {
-    @Override
-    protected CiFriendlyXMLFilter getFilter(XMLStreamReader parser) {
-
-        CiFriendlyXMLFilter filter = new CiFriendlyXMLFilter(parser, true);
-        filter.setChangelist("CHANGELIST");
-        return filter;
-    }
-
-    @Test
-    void changelist() throws Exception {
-        String input = "<project>"
-                + "  <groupId>GROUPID</groupId>"
-                + "  <artifactId>ARTIFACTID</artifactId>"
-                + "<version>${changelist}</version>"
-                + "</project>";
-        String expected = "<project>"
-                + "  <groupId>GROUPID</groupId>"
-                + "  <artifactId>ARTIFACTID</artifactId>"
-                + "<version>CHANGELIST</version>"
-                + "</project>";
-
-        String actual = transform(input);
-
-        assertEquals(expected, actual);
-    }
-}
diff --git a/maven-model-transform/src/test/java/org/apache/maven/model/transform/ConsumerPomXMLFilterTest.java b/maven-model-transform/src/test/java/org/apache/maven/model/transform/ConsumerPomXMLFilterTest.java
deleted file mode 100644
index 37e69d8665..0000000000
--- a/maven-model-transform/src/test/java/org/apache/maven/model/transform/ConsumerPomXMLFilterTest.java
+++ /dev/null
@@ -1,307 +0,0 @@
-/*
- * 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.
- */
-package org.apache.maven.model.transform;
-
-import javax.xml.stream.XMLStreamReader;
-
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.Optional;
-import java.util.function.BiFunction;
-import java.util.function.Function;
-
-import org.junit.jupiter.api.Test;
-
-import static org.xmlunit.assertj.XmlAssert.assertThat;
-
-class ConsumerPomXMLFilterTest extends AbstractXMLFilterTests {
-    @Override
-    protected XMLStreamReader getFilter(XMLStreamReader orgParser) {
-        final BuildToRawPomXMLFilterFactory buildPomXMLFilterFactory = new BuildToRawPomXMLFilterFactory(true) {
-            @Override
-            protected Function<Path, Optional<RelativeProject>> getRelativePathMapper() {
-                return null;
-            }
-
-            @Override
-            protected BiFunction<String, String, String> getDependencyKeyToVersionMapper() {
-                return null;
-            }
-
-            @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");
-            }
-        };
-
-        XMLStreamReader parser =
-                new RawToConsumerPomXMLFilterFactory(buildPomXMLFilterFactory).get(orgParser, Paths.get("pom.xml"));
-        return parser;
-    }
-
-    @Test
-    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
-    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"
-                + "                           https://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
-    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 https://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>4.0.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();
-    }
-
-    @Test
-    void lexicalHandler() 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();
-    }
-
-    @Test
-    void downgradeModelVersion() throws Exception {
-        String input = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
-                + "<project xmlns=\"http://maven.apache.org/POM/4.1.0\"\n"
-                + "       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
-                + "       xsi:schemaLocation=\"http://maven.apache.org/POM/4.1.0\n"
-                + "                           http://maven.apache.org/xsd/maven-4.1.0.xsd\">\n"
-                + "  <modelVersion>4.1.0</modelVersion>\n"
-                + "  <groupId>org.sonatype.mavenbook.multispring</groupId>\n"
-                + "  <artifactId>parent</artifactId>\n"
-                + "  <version>0.9-SNAPSHOT</version>\n"
-                + "  <packaging>pom</packaging>\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"
-                + "                           https://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-SNAPSHOT</version>\n"
-                + "  <packaging>pom</packaging>\n"
-                + "</project>";
-        String actual = transform(input);
-        assertThat(actual).and(expected).ignoreWhitespace().areIdentical();
-    }
-
-    @Test
-    void downgradeNotModelVersion() throws Exception {
-        String input = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
-                + "<project xmlns=\"http://maven.apache.org/POM/4.1.0\"\n"
-                + "       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
-                + "       xsi:schemaLocation=\"http://maven.apache.org/POM/4.1.0 http://maven.apache.org/xsd/maven-4.1.0.xsd\""
-                + "       preserve.model.version=\"true\">\n"
-                + "  <modelVersion>4.1.0</modelVersion>\n"
-                + "  <groupId>org.sonatype.mavenbook.multispring</groupId>\n"
-                + "  <artifactId>parent</artifactId>\n"
-                + "  <version>0.9-SNAPSHOT</version>\n"
-                + "  <packaging>pom</packaging>\n"
-                + "  <build>"
-                + "    <plugins>\n"
-                + "      <plugin>\n"
-                + "        <executions>\n"
-                + "          <execution>\n"
-                + "            <priority>1</priority>\n"
-                + "          </execution>\n"
-                + "        </executions>\n"
-                + "      </plugin>\n"
-                + "    </plugins>\n"
-                + "  </build>\n"
-                + "</project>";
-        String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
-                + "<project xmlns=\"http://maven.apache.org/POM/4.1.0\"\n"
-                + "       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
-                + "       preserve.model.version=\"true\""
-                + "       xsi:schemaLocation=\"http://maven.apache.org/POM/4.1.0 https://maven.apache.org/xsd/maven-4.1.0.xsd\">\n"
-                + "  <modelVersion>4.1.0</modelVersion>\n"
-                + "  <groupId>org.sonatype.mavenbook.multispring</groupId>\n"
-                + "  <artifactId>parent</artifactId>\n"
-                + "  <version>0.9-SNAPSHOT</version>\n"
-                + "  <packaging>pom</packaging>\n"
-                + "  <build>"
-                + "    <plugins>\n"
-                + "      <plugin>\n"
-                + "        <executions>\n"
-                + "          <execution>\n"
-                + "            <priority>1</priority>\n"
-                + "          </execution>\n"
-                + "        </executions>\n"
-                + "      </plugin>\n"
-                + "    </plugins>\n"
-                + "  </build>\n"
-                + "</project>";
-        String actual = transform(input);
-        assertThat(actual).and(expected).ignoreWhitespace().areIdentical();
-    }
-}
diff --git a/maven-model-transform/src/test/java/org/apache/maven/model/transform/ModelVersionXMLFilterTest.java b/maven-model-transform/src/test/java/org/apache/maven/model/transform/ModelVersionXMLFilterTest.java
deleted file mode 100644
index c1b7120702..0000000000
--- a/maven-model-transform/src/test/java/org/apache/maven/model/transform/ModelVersionXMLFilterTest.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * 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.
- */
-package org.apache.maven.model.transform;
-
-import javax.xml.stream.XMLStreamReader;
-
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-class ModelVersionXMLFilterTest extends AbstractXMLFilterTests {
-    @Override
-    protected XMLStreamReader getFilter(XMLStreamReader parser) {
-        return new ModelVersionXMLFilter(parser);
-    }
-
-    @Test
-    void modelVersionWithDefaultPrefix() throws Exception {
-        String input = "<project xmlns='http://maven.apache.org/POM/4.0.0'>"
-                + "  <groupId>GROUPID</groupId>"
-                + "  <artifactId>ARTIFACTID</artifactId>"
-                + "  <version>VERSION</version>"
-                + "</project>";
-        String expected = "<project xmlns=\"http://maven.apache.org/POM/4.0.0\">"
-                + "  <modelVersion>4.0.0</modelVersion>"
-                + "  <groupId>GROUPID</groupId>"
-                + "  <artifactId>ARTIFACTID</artifactId>"
-                + "  <version>VERSION</version>"
-                + "</project>";
-
-        // Check that the modelVersion is added
-        assertEquals(expected, transform(input));
-        // Check that the transformed POM is stable (modelVersion not added twice)
-        assertEquals(expected, transform(expected));
-    }
-
-    @Test
-    void modelVersionWithPrefix() throws Exception {
-        String input = "<maven:project xmlns:maven='http://maven.apache.org/POM/4.0.0'>"
-                + "  <maven:groupId>GROUPID</maven:groupId>"
-                + "  <maven:artifactId>ARTIFACTID</maven:artifactId>"
-                + "  <maven:version>VERSION</maven:version>"
-                + "</maven:project>";
-        String expected = "<maven:project xmlns:maven=\"http://maven.apache.org/POM/4.0.0\">"
-                + "  <maven:modelVersion>4.0.0</maven:modelVersion>"
-                + "  <maven:groupId>GROUPID</maven:groupId>"
-                + "  <maven:artifactId>ARTIFACTID</maven:artifactId>"
-                + "  <maven:version>VERSION</maven:version>"
-                + "</maven:project>";
-
-        // Check that the modelVersion is added
-        assertEquals(expected, transform(input));
-        // Check that the transformed POM is stable (modelVersion not added twice)
-        assertEquals(expected, transform(expected));
-    }
-}
diff --git a/maven-model-transform/src/test/java/org/apache/maven/model/transform/ModulesXMLFilterTest.java b/maven-model-transform/src/test/java/org/apache/maven/model/transform/ModulesXMLFilterTest.java
deleted file mode 100644
index a1e3601e3d..0000000000
--- a/maven-model-transform/src/test/java/org/apache/maven/model/transform/ModulesXMLFilterTest.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * 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.
- */
-package org.apache.maven.model.transform;
-
-import javax.xml.stream.XMLStreamReader;
-
-import org.junit.jupiter.api.Test;
-
-import static org.xmlunit.assertj.XmlAssert.assertThat;
-
-class ModulesXMLFilterTest extends AbstractXMLFilterTests {
-
-    @Override
-    protected ModulesXMLFilter getFilter(XMLStreamReader parser) {
-        return new ModulesXMLFilter(parser);
-    }
-
-    @Test
-    void emptyModules() throws Exception {
-        String input = "<project><modules/></project>";
-        String expected = "<project/>";
-        String actual = transform(input);
-        assertThat(actual).and(expected).areIdentical();
-    }
-
-    @Test
-    void setOfModules() throws Exception {
-        String input = "<project><modules>" + "<module>ab</module>" + "<module>../cd</module>" + "</modules></project>";
-        String expected = "<project/>";
-        String actual = transform(input);
-        assertThat(actual).and(expected).areIdentical();
-    }
-
-    @Test
-    void noModules() throws Exception {
-        String input = "<project><name>NAME</name></project>";
-        String expected = input;
-        String actual = transform(input);
-        assertThat(actual).and(expected).areIdentical();
-    }
-
-    @Test
-    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();
-    }
-
-    @Test
-    void setOfModulesLF() throws Exception {
-        String input = "<project>\n"
-                + "\n"
-                + "  <modules>\n"
-                + "    <module>ab</module>\n"
-                + "    <module>../cd</module>\n"
-                + "  </modules>\n"
-                + "\n"
-                + "</project>\n";
-        String expected = "<project>\n" + "\n" + "  \n" + "\n" + "</project>\n";
-        String actual = transform(input);
-        assertThat(actual).and(expected).areIdentical();
-    }
-}
diff --git a/maven-model-transform/src/test/java/org/apache/maven/model/transform/ParentXMLFilterTest.java b/maven-model-transform/src/test/java/org/apache/maven/model/transform/ParentXMLFilterTest.java
deleted file mode 100644
index 73071eb059..0000000000
--- a/maven-model-transform/src/test/java/org/apache/maven/model/transform/ParentXMLFilterTest.java
+++ /dev/null
@@ -1,331 +0,0 @@
-/*
- * 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.
- */
-package org.apache.maven.model.transform;
-
-import javax.xml.stream.XMLStreamReader;
-
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.Optional;
-import java.util.function.Function;
-
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-class ParentXMLFilterTest extends AbstractXMLFilterTests {
-    private Function<XMLStreamReader, XMLStreamReader> filterCreator;
-    private Path projectPath;
-
-    @BeforeEach
-    void reset() throws IOException {
-        filterCreator = null;
-        projectPath = Paths.get("target/test-classes/" + getClass().getSimpleName() + "/child");
-        Files.createDirectories(projectPath);
-        if (!Files.isRegularFile(projectPath.resolve("../pom.xml"))) {
-            Files.createFile(projectPath.resolve("../pom.xml"));
-        }
-        if (!Files.isRegularFile(projectPath.resolve("pom.xml"))) {
-            Files.createFile(projectPath.resolve("pom.xml"));
-        }
-        Path relPath = projectPath.resolve("RELATIVEPATH");
-        Files.createDirectories(relPath);
-        if (!Files.isRegularFile(relPath.resolve("pom.xml"))) {
-            Files.createFile(relPath.resolve("pom.xml"));
-        }
-    }
-
-    @Override
-    protected XMLStreamReader getFilter(XMLStreamReader parser) {
-        Function<XMLStreamReader, XMLStreamReader> filterCreator =
-                (this.filterCreator != null ? this.filterCreator : this::createFilter);
-        return filterCreator.apply(parser);
-    }
-
-    protected XMLStreamReader createFilter(XMLStreamReader parser) {
-        return createFilter(
-                parser, x -> Optional.of(new RelativeProject("GROUPID", "ARTIFACTID", "1.0.0")), projectPath);
-    }
-
-    protected XMLStreamReader createFilter(
-            XMLStreamReader parser, Function<Path, Optional<RelativeProject>> pathMapper, Path projectPath) {
-        Function<Path, Path> locator = p -> p.resolve("pom.xml");
-        return new ParentXMLFilter(new FastForwardFilter(parser), pathMapper, locator, projectPath);
-    }
-
-    @Test
-    void testWithFastForward() throws Exception {
-        String input = "<project>"
-                + "<build>"
-                + "<plugins>"
-                + "<plugin>"
-                + "<configuration>"
-                + "<parent>"
-                + "<groupId>GROUPID</groupId>"
-                + "<artifactId>ARTIFACTID</artifactId>"
-                + "</parent>"
-                + "</configuration>"
-                + "</plugin>"
-                + "</plugins>"
-                + "</build>"
-                + "</project>";
-        String expected = input;
-
-        String actual = transform(input);
-
-        assertEquals(expected, actual);
-    }
-
-    @Test
-    void testWithFastForwardAfterByPass() throws Exception {
-        String input = "<project>"
-                + "<build>"
-                + "<plugins>"
-                + "<plugin>"
-                + "<configuration>"
-                + "<parent>"
-                + "<groupId>GROUPID</groupId>"
-                + "<artifactId>ARTIFACTID</artifactId>"
-                + "</parent>"
-                + "</configuration>"
-                + "</plugin>"
-                + "</plugins>"
-                + "</build>"
-                + "<parent>"
-                + "<groupId>GROUPID</groupId>"
-                + "<artifactId>ARTIFACTID</artifactId>"
-                + "</parent>"
-                + "</project>";
-        String expected = "<project>"
-                + "<build>"
-                + "<plugins>"
-                + "<plugin>"
-                + "<configuration>"
-                + "<parent>"
-                + "<groupId>GROUPID</groupId>"
-                + "<artifactId>ARTIFACTID</artifactId>"
-                + "</parent>"
-                + "</configuration>"
-                + "</plugin>"
-                + "</plugins>"
-                + "</build>"
-                + "<parent>"
-                + "<groupId>GROUPID</groupId>"
-                + "<artifactId>ARTIFACTID</artifactId>"
-                + "<version>1.0.0</version>"
-                + "</parent>"
-                + "</project>";
-
-        String actual = transform(input);
-
-        assertEquals(expected, actual);
-    }
-
-    @Test
-    void testMinimum() throws Exception {
-        String input = "<project><parent /></project>";
-        String expected = input;
-        String actual = transform(input);
-        assertEquals(expected, actual);
-    }
-
-    @Test
-    void testNoRelativePath() throws Exception {
-        String input = "<project><parent>"
-                + "<groupId>GROUPID</groupId>"
-                + "<artifactId>ARTIFACTID</artifactId>"
-                + "<version>VERSION</version>"
-                + "</parent></project>";
-        String expected = input;
-
-        String actual = transform(input);
-
-        assertEquals(expected, actual);
-    }
-
-    @Test
-    void testDefaultRelativePath() throws Exception {
-        String input = "<project>\n"
-                + "  <parent>\n"
-                + "    <groupId>GROUPID</groupId>\n"
-                + "    <artifactId>ARTIFACTID</artifactId>\n"
-                + "  </parent>\n"
-                + "</project>";
-        String expected = "<project>\n"
-                + "  <parent>\n"
-                + "    <groupId>GROUPID</groupId>\n"
-                + "    <artifactId>ARTIFACTID</artifactId>\n"
-                + "    <version>1.0.0</version>\n"
-                + "  </parent>\n"
-                + "</project>";
-
-        String actual = transform(input);
-
-        assertEquals(expected, actual);
-    }
-
-    /**
-     * An empty relative path means it must be downloaded from a repository.
-     * That implies that the version cannot be solved (if missing, Maven should complain)
-     *
-     * @throws Exception
-     */
-    @Test
-    void testEmptyRelativePathNoVersion() throws Exception {
-        String input = "<project><parent>"
-                + "<groupId>GROUPID</groupId>"
-                + "<artifactId>ARTIFACTID</artifactId>"
-                + "<relativePath></relativePath>"
-                + "</parent></project>";
-        String expected = "<project><parent>"
-                + "<groupId>GROUPID</groupId>"
-                + "<artifactId>ARTIFACTID</artifactId>"
-                + "<relativePath />" // SAX optimization, however "" != null ...
-                + "</parent></project>";
-
-        String actual = transform(input);
-
-        assertEquals(expected, actual);
-    }
-
-    @Test
-    void testNoVersion() throws Exception {
-        String input = "<project><parent>"
-                + "<groupId>GROUPID</groupId>"
-                + "<artifactId>ARTIFACTID</artifactId>"
-                + "<relativePath>RELATIVEPATH</relativePath>"
-                + "</parent></project>";
-        String expected = "<project><parent>"
-                + "<groupId>GROUPID</groupId>"
-                + "<artifactId>ARTIFACTID</artifactId>"
-                + "<relativePath>RELATIVEPATH</relativePath>"
-                + "<version>1.0.0</version>"
-                + "</parent></project>";
-
-        String actual = transform(input);
-
-        assertEquals(expected, actual);
-    }
-
-    @Test
-    void testInvalidRelativePath() throws Exception {
-        filterCreator = parser -> createFilter(
-                parser, x -> Optional.ofNullable(null), Paths.get("pom.xml").toAbsolutePath());
-
-        String input = "<project><parent>"
-                + "<groupId>GROUPID</groupId>"
-                + "<artifactId>ARTIFACTID</artifactId>"
-                + "<relativePath>RELATIVEPATH</relativePath>"
-                + "</parent></project>";
-        String expected = input;
-
-        String actual = transform(input);
-
-        assertEquals(expected, actual);
-    }
-
-    @Test
-    void testRelativePathAndVersion() throws Exception {
-        String input = "<project><parent>"
-                + "<groupId>GROUPID</groupId>"
-                + "<artifactId>ARTIFACTID</artifactId>"
-                + "<relativePath>RELATIVEPATH</relativePath>"
-                + "<version>1.0.0</version>"
-                + "</parent></project>";
-        String expected = "<project><parent>"
-                + "<groupId>GROUPID</groupId>"
-                + "<artifactId>ARTIFACTID</artifactId>"
-                + "<relativePath>RELATIVEPATH</relativePath>"
-                + "<version>1.0.0</version>"
-                + "</parent></project>";
-
-        String actual = transform(input);
-
-        assertEquals(expected, actual);
-    }
-
-    @Test
-    void testWithWeirdNamespace() throws Exception {
-        String input = "<relativePath:project xmlns:relativePath=\"relativePath\">"
-                + "<relativePath:parent>"
-                + "<relativePath:groupId>GROUPID</relativePath:groupId>"
-                + "<relativePath:artifactId>ARTIFACTID</relativePath:artifactId>"
-                + "<relativePath:relativePath>RELATIVEPATH</relativePath:relativePath>"
-                + "</relativePath:parent></relativePath:project>";
-        String expected = "<relativePath:project xmlns:relativePath=\"relativePath\">"
-                + "<relativePath:parent>"
-                + "<relativePath:groupId>GROUPID</relativePath:groupId>"
-                + "<relativePath:artifactId>ARTIFACTID</relativePath:artifactId>"
-                + "<relativePath:relativePath>RELATIVEPATH</relativePath:relativePath>"
-                + "<relativePath:version>1.0.0</relativePath:version>"
-                + "</relativePath:parent>"
-                + "</relativePath:project>";
-
-        String actual = transform(input);
-
-        assertEquals(expected, actual);
-    }
-
-    @Test
-    void comment() throws Exception {
-        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);
-    }
-
-    @Test
-    void testIndent() throws Exception {
-        String input = "<project>\n"
-                + "  <parent>\n"
-                + "    <groupId>GROUPID</groupId>\n"
-                + "    <artifactId>ARTIFACTID</artifactId>\n"
-                + "    <!--version here-->\n"
-                + "  </parent>\n"
-                + "</project>";
-        String expected = "<project>\n"
-                + "  <parent>\n"
-                + "    <groupId>GROUPID</groupId>\n"
-                + "    <artifactId>ARTIFACTID</artifactId>\n"
-                + "    <!--version here-->\n"
-                + "    <version>1.0.0</version>\n"
-                + "  </parent>\n"
-                + "</project>";
-
-        String actual = transform(input);
-
-        assertEquals(expected, actual);
-    }
-}
diff --git a/maven-model-transform/src/test/java/org/apache/maven/model/transform/ReactorDependencyXMLFilterTest.java b/maven-model-transform/src/test/java/org/apache/maven/model/transform/ReactorDependencyXMLFilterTest.java
deleted file mode 100644
index 0621d8b844..0000000000
--- a/maven-model-transform/src/test/java/org/apache/maven/model/transform/ReactorDependencyXMLFilterTest.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * 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.
- */
-package org.apache.maven.model.transform;
-
-import javax.xml.stream.XMLStreamReader;
-
-import java.util.function.BiFunction;
-
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-import static org.xmlunit.assertj.XmlAssert.assertThat;
-
-class ReactorDependencyXMLFilterTest extends AbstractXMLFilterTests {
-    private BiFunction<String, String, String> reactorVersionMapper;
-
-    @BeforeEach
-    protected void reset() {
-        reactorVersionMapper = null;
-    }
-
-    @Override
-    protected ReactorDependencyXMLFilter getFilter(XMLStreamReader parser) {
-        return new ReactorDependencyXMLFilter(
-                parser, reactorVersionMapper != null ? reactorVersionMapper : (g, a) -> "1.0.0");
-    }
-
-    @Test
-    void testDefaultDependency() throws Exception {
-        String input = "<dependency>"
-                + "<groupId>GROUPID</groupId>"
-                + "<artifactId>ARTIFACTID</artifactId>"
-                + "<version>VERSION</version>"
-                + "</dependency>";
-        String expected = input;
-
-        String actual = transform(input);
-
-        assertThat(actual).isEqualTo(expected);
-    }
-
-    @Test
-    void testManagedDependency() throws Exception {
-        reactorVersionMapper = (g, a) -> null;
-
-        String input =
-                "<dependency>" + "<groupId>GROUPID</groupId>" + "<artifactId>ARTIFACTID</artifactId>" + "</dependency>";
-        String expected = input;
-
-        String actual = transform(input);
-
-        assertThat(actual).isEqualTo(expected);
-    }
-
-    @Test
-    void testReactorDependency() throws Exception {
-        String input =
-                "<dependency>" + "<groupId>GROUPID</groupId>" + "<artifactId>ARTIFACTID</artifactId>" + "</dependency>";
-        String expected = "<dependency>"
-                + "<groupId>GROUPID</groupId>"
-                + "<artifactId>ARTIFACTID</artifactId>"
-                + "<version>1.0.0</version>"
-                + "</dependency>";
-
-        String actual = transform(input);
-
-        assertThat(actual).isEqualTo(expected);
-    }
-
-    @Test
-    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();
-    }
-
-    @Test
-    void multipleDependencies() throws Exception {
-        String input = "<project>\n" + "  <modelVersion>4.0.0</modelVersion>\n"
-                + "    <groupId>tests.project</groupId>\n"
-                + "    <artifactId>duplicate-plugin-defs-merged</artifactId>\n"
-                + "    <version>1</version>\n"
-                + "    <build>\n"
-                + "      <plugins>\n"
-                + "        <plugin>\n"
-                + "          <artifactId>maven-compiler-plugin</artifactId>\n"
-                + "          <dependencies>\n"
-                + "            <dependency>\n"
-                + "              <groupId>group</groupId>\n"
-                + "              <artifactId>first</artifactId>\n"
-                + "              <version>1</version>\n"
-                + "            </dependency>\n"
-                + "          </dependencies>\n"
-                + "        </plugin>\n"
-                + "        <plugin>\n"
-                + "          <artifactId>maven-compiler-plugin</artifactId>\n"
-                + "          <dependencies>\n"
-                + "            <dependency>\n"
-                + "              <groupId>group</groupId>\n"
-                + "              <artifactId>second</artifactId>\n"
-                + "              <version>1</version>\n"
-                + "            </dependency>\n"
-                + "          </dependencies>\n"
-                + "        </plugin>\n"
-                + "      </plugins>\n"
-                + "    </build>\n"
-                + "</project>";
-        String expected = input;
-
-        String actual = transform(input);
-
-        assertThat(actual).and(expected).areIdentical();
-    }
-}
diff --git a/maven-model-transform/src/test/java/org/apache/maven/model/transform/RelativePathXMLFilterTest.java b/maven-model-transform/src/test/java/org/apache/maven/model/transform/RelativePathXMLFilterTest.java
deleted file mode 100644
index 2388521870..0000000000
--- a/maven-model-transform/src/test/java/org/apache/maven/model/transform/RelativePathXMLFilterTest.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * 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.
- */
-package org.apache.maven.model.transform;
-
-import javax.xml.stream.XMLStreamReader;
-
-import org.junit.jupiter.api.Test;
-
-import static org.xmlunit.assertj.XmlAssert.assertThat;
-
-class RelativePathXMLFilterTest extends AbstractXMLFilterTests {
-    @Override
-    protected RelativePathXMLFilter getFilter(XMLStreamReader parser) {
-        return new RelativePathXMLFilter(parser);
-    }
-
-    @Test
-    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"
-                + "  </parent>\n"
-                + "  <artifactId>PROJECT</artifactId>\n"
-                + "</project>";
-        String actual = transform(input);
-        assertThat(actual).and(expected).areIdentical();
-    }
-
-    @Test
-    void testRelativePathNS() throws Exception {
-        String input = "<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"
-                + "  <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 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"
-                + "  <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).areIdentical();
-    }
-
-    @Test
-    void testRelativePathPasNS() throws Exception {
-        String input = "<p:project xmlns:p=\"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"
-                + "  <p:parent>\n"
-                + "    <p:groupId>GROUPID</p:groupId>\n"
-                + "    <p:artifactId>PARENT</p:artifactId>\n"
-                + "    <p:version>VERSION</p:version>\n"
-                + "    <p:relativePath>../pom.xml</p:relativePath>\n"
-                + "  </p:parent>\n"
-                + "  <p:artifactId>PROJECT</p:artifactId>\n"
-                + "</p:project>";
-        String expected = "<p:project xmlns:p=\"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"
-                + "  <p:parent>\n"
-                + "    <p:groupId>GROUPID</p:groupId>\n"
-                + "    <p:artifactId>PARENT</p:artifactId>\n"
-                + "    <p:version>VERSION</p:version>\n"
-                + "  </p:parent>\n"
-                + "  <p:artifactId>PROJECT</p:artifactId>\n"
-                + "</p:project>";
-        String actual = transform(input);
-        assertThat(actual).and(expected).areIdentical();
-    }
-}
diff --git a/pom.xml b/pom.xml
index 7d16f9fb5e..5c93937fc0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -105,7 +105,6 @@ under the License.
     <module>maven-builder-support</module>
     <module>maven-model</module>
     <module>maven-model-builder</module>
-    <module>maven-model-transform</module>
     <module>api</module>
     <module>maven-xml-impl</module>
     <module>maven-core</module>
diff --git a/src/mdo/model.vm b/src/mdo/model.vm
index 912545e580..9f790bd583 100644
--- a/src/mdo/model.vm
+++ b/src/mdo/model.vm
@@ -114,6 +114,7 @@ public class ${class.name}
     #end
 {
     #if ( $class == $root )
+    final String namespaceUri;
     final String modelEncoding;
     #end
     #foreach ( $field in $class.getFields($version) )
@@ -138,6 +139,7 @@ public class ${class.name}
       */
     ${class.name}(
     #if ( $class == $root )
+        String namespaceUri,
         String modelEncoding,
     #end
     #foreach ( $field in $allFields )
@@ -164,6 +166,7 @@ public class ${class.name}
         );
     #end
     #if ( $class == $root )
+        this.namespaceUri = namespaceUri;
         this.modelEncoding = modelEncoding;
     #end
     #foreach ( $field in $class.getFields($version) )
@@ -200,6 +203,10 @@ public class ${class.name}
 
     #end
     #if ( $class == $root )
+    public String getNamespaceUri() {
+        return namespaceUri;
+    }
+
     public String getModelEncoding() {
         return modelEncoding;
     }
@@ -349,6 +356,7 @@ public class ${class.name}
     {
         ${class.name} base;
     #if ( $class == $root )
+        String namespaceUri;
         String modelEncoding;
     #end
     #foreach ( $field in $class.getFields($version) )
@@ -389,6 +397,10 @@ public class ${class.name}
         Builder(${class.name} base, boolean forceCopy) {
     #if ( $class.superClass )
             super(base, forceCopy);
+    #end
+    #if ( $class == $root )
+            this.namespaceUri = base.namespaceUri;
+            this.modelEncoding = base.modelEncoding;
     #end
             if (forceCopy) {
     #foreach ( $field in $class.getFields($version) )
@@ -403,6 +415,12 @@ public class ${class.name}
         }
 
     #if ( $class == $root )
+        @Nonnull
+        public Builder namespaceUri(String namespaceUri) {
+            this.namespaceUri = namespaceUri;
+            return this;
+        }
+
         @Nonnull
         public Builder modelEncoding(String modelEncoding) {
             this.modelEncoding = modelEncoding;
@@ -456,6 +474,7 @@ public class ${class.name}
     #end
             return new ${class.name}(
     #if ( $class == $root )
+                namespaceUri != null ? namespaceUri : (base != null ? base.namespaceUri : ""),
                 modelEncoding != null ? modelEncoding : (base != null ? base.modelEncoding : "UTF-8"),
     #end
     #foreach ( $field in $allFields )
diff --git a/src/mdo/reader-stax.vm b/src/mdo/reader-stax.vm
index 789f398287..61bd8428fe 100644
--- a/src/mdo/reader-stax.vm
+++ b/src/mdo/reader-stax.vm
@@ -676,6 +676,7 @@ public class ${className} {
     #end
   #end
   #if ( $class == $root )
+        ${classLcapName}.namespaceUri(parser.getNamespaceURI());
         ${classLcapName}.modelEncoding(parser.getEncoding());
   #end
         return ${classLcapName}.build();
diff --git a/src/site/xdoc/index.xml b/src/site/xdoc/index.xml
index 1664a45f2c..21521eb5fe 100644
--- a/src/site/xdoc/index.xml
+++ b/src/site/xdoc/index.xml
@@ -57,7 +57,6 @@ under the License.
           <area shape="rect" coords="244,381,406,421"  alt="maven-settings-builder" href="maven-settings-builder/" />
           <area shape="rect" coords="278,447,371,486"  alt="maven-settings" href="maven-settings/" />
           <area shape="rect" coords="387,512,540,553"  alt="maven-model-builder" href="maven-model-builder/" />
-          <area shape="rect" coords="304,578,485,619"  alt="maven-model-transform" href="maven-model-transform/" />
           <area shape="rect" coords="492,577,594,619"  alt="maven-model" href="maven-model/" />
           <area shape="rect" coords="71,0,222,41"      alt="maven-slf4j-provider" href="maven-slf4j-provider/" />
           <area shape="rect" coords="73,66,220,108"    alt="maven-slf4j-wrapper" href="maven-slf4j-wrapper/" />