[MNG-7877] Attach the build POM and simplify build/consumer implementation (#1240)

.. at the cost of loosing xml formatting in the consumer POMs
This commit is contained in:
Guillaume Nodet 2023-09-13 13:39:50 +02:00 committed by GitHub
parent 25488f9231
commit c6380108b1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
63 changed files with 734 additions and 3730 deletions

View File

@ -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.
*

View File

@ -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>

View File

@ -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>

View File

@ -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;
}
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) {
@Override
public void transform(Path src, Path dest) {
Model model = project.getModel().getDelegate();
transform(src, dest, model);
}
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);
}
}
}
}

View File

@ -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;

View File

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

View File

@ -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);
}
}

View File

@ -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();
}

View File

@ -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());
}
}

View File

@ -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

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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 {}

View File

@ -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 {
@Override
public XMLStreamReader transform(XMLStreamReader parser, Path pomFile, TransformerContext context)
throws IOException, TransformerException {
BuildToRawPomXMLFilterFactory buildPomXMLFilterFactory = new DefaultBuildPomXMLFilterFactory(context, false);
return buildPomXMLFilterFactory.get(parser, pomFile);
public static final String NAMESPACE_PREFIX = "http://maven.apache.org/POM/";
@Override
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;
}
public String getVersion() {
return version;
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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 {

View File

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

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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();
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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())));
}
}

View File

@ -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.
*

View File

@ -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);
}
}
}

View File

@ -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();
}

View File

@ -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>

View File

@ -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();
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}

View File

@ -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
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}

View File

@ -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();
}
}

View File

@ -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).
[]

View File

@ -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>

View File

@ -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();
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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));
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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>

View File

@ -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 )

View File

@ -676,6 +676,7 @@ public class ${className} {
#end
#end
#if ( $class == $root )
${classLcapName}.namespaceUri(parser.getNamespaceURI());
${classLcapName}.modelEncoding(parser.getEncoding());
#end
return ${classLcapName}.build();

View File

@ -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/" />