Fix classpath calculation
* Refactor MavanProjectAnalyzer and related classes * Refactor MavenProjectParser parse methods to reuse code between source sets * ParsingResults are provided to subsequent modules in a multi-module project. The result can be used to build the classpath when parsing subsequent modules.
This commit is contained in:
@@ -1,198 +0,0 @@
|
||||
/*
|
||||
* Copyright 2021 - 2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://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.springframework.rewrite;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.junit.jupiter.api.*;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.openrewrite.ExecutionContext;
|
||||
import org.openrewrite.RecipeRun;
|
||||
import org.openrewrite.SourceFile;
|
||||
import org.openrewrite.internal.InMemoryLargeSourceSet;
|
||||
import org.openrewrite.java.JavaIsoVisitor;
|
||||
import org.openrewrite.java.JavaParser;
|
||||
import org.openrewrite.java.JavaTemplate;
|
||||
import org.openrewrite.java.marker.JavaSourceSet;
|
||||
import org.openrewrite.java.tree.J;
|
||||
import org.openrewrite.java.tree.JavaType;
|
||||
import org.springframework.rewrite.parsers.RewriteExecutionContext;
|
||||
import org.springframework.rewrite.parsers.RewriteProjectParsingResult;
|
||||
import org.springframework.rewrite.parsers.SpringRewriteProperties;
|
||||
import org.springframework.rewrite.parsers.maven.ClasspathDependencies;
|
||||
import org.springframework.rewrite.support.openrewrite.GenericOpenRewriteRecipe;
|
||||
import org.springframework.rewrite.test.util.ParserExecutionHelper;
|
||||
import org.springframework.rewrite.test.util.TestProjectHelper;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class MavenTypeResolutionTest {
|
||||
|
||||
@Nested
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
class WithSingleModuleMavenProject {
|
||||
|
||||
private static RewriteProjectParsingResult parsingResult;
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
@DisplayName("parsed project should have correct classpath")
|
||||
void parsedProjectShouldHaveCorrectClasspath(@TempDir Path baseDir) {
|
||||
// given
|
||||
pepareProject(baseDir);
|
||||
// when
|
||||
parsingResult = new ParserExecutionHelper().parseWithRewriteProjectParser(baseDir,
|
||||
new SpringRewriteProperties());
|
||||
// then
|
||||
verifyTypesOnClasspathAndNoTypesInUse(parsingResult);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(2)
|
||||
@DisplayName("annotating the class adds the annotation to typesInUse")
|
||||
void annotatingTheClassAddsTheAnnotationToTypesInUse() {
|
||||
// Given: parsed project
|
||||
List<SourceFile> sourceFiles = parsingResult.sourceFiles();
|
||||
assertThat(sourceFiles).isNotEmpty();
|
||||
// When: Add @SpringBootApplication annotation to class
|
||||
RecipeRun recipeRun = annotateClass(sourceFiles);
|
||||
// Then: annotation is in typesInUse
|
||||
verifyAnnotationIsNowInUsedTypes(recipeRun);
|
||||
}
|
||||
|
||||
private static void verifyAnnotationIsNowInUsedTypes(RecipeRun recipeRun) {
|
||||
SourceFile after = recipeRun.getChangeset().getAllResults().get(0).getAfter();
|
||||
assertThat(after).isInstanceOf(J.CompilationUnit.class);
|
||||
J.CompilationUnit cu = (J.CompilationUnit) after;
|
||||
List<String> classpath = cu.getMarkers()
|
||||
.findFirst(JavaSourceSet.class)
|
||||
.get()
|
||||
.getClasspath()
|
||||
.stream()
|
||||
.map(JavaType.FullyQualified::getFullyQualifiedName)
|
||||
.toList();
|
||||
assertThat(classpath).contains("org.springframework.boot.autoconfigure.SpringBootApplication", "SomeClass");
|
||||
|
||||
List<String> typesInUse = cu.getTypesInUse()
|
||||
.getTypesInUse()
|
||||
.stream()
|
||||
.map(JavaType.FullyQualified.class::cast)
|
||||
.map(JavaType.FullyQualified::getFullyQualifiedName)
|
||||
.toList();
|
||||
assertThat(typesInUse)
|
||||
.containsExactlyInAnyOrder("org.springframework.boot.autoconfigure.SpringBootApplication");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static RecipeRun annotateClass(List<SourceFile> sourceFiles) {
|
||||
RecipeRun recipeRun = new GenericOpenRewriteRecipe<>(() -> new JavaIsoVisitor<>() {
|
||||
@Override
|
||||
public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl,
|
||||
ExecutionContext executionContext) {
|
||||
J.ClassDeclaration cd = super.visitClassDeclaration(classDecl, executionContext);
|
||||
if (cd.getSimpleName().equals("SomeClass")) {
|
||||
ClasspathDependencies classpathDependencies = ((J.CompilationUnit) getCursor()
|
||||
.dropParentUntil(J.CompilationUnit.class::isInstance)
|
||||
.getValue()).getMarkers().findFirst(ClasspathDependencies.class).get();
|
||||
String annotationFqName = "org.springframework.boot.autoconfigure.SpringBootApplication";
|
||||
cd = JavaTemplate.builder("@SpringBootApplication")
|
||||
.imports(annotationFqName)
|
||||
.javaParser(JavaParser.fromJavaVersion()
|
||||
.classpath(classpathDependencies.getDependencies())
|
||||
.logCompilationWarningsAndErrors(true))
|
||||
.build()
|
||||
.apply(getCursor(), cd.getCoordinates()
|
||||
.addAnnotation(Comparator.comparing(J.Annotation::getSimpleName)));
|
||||
maybeAddImport(annotationFqName);
|
||||
}
|
||||
return cd;
|
||||
}
|
||||
}).run(new InMemoryLargeSourceSet(sourceFiles), new RewriteExecutionContext());
|
||||
return recipeRun;
|
||||
}
|
||||
|
||||
private static void verifyTypesOnClasspathAndNoTypesInUse(RewriteProjectParsingResult parallelParsingResult) {
|
||||
J.CompilationUnit cuBefore = parallelParsingResult.sourceFiles()
|
||||
.stream()
|
||||
.filter(J.CompilationUnit.class::isInstance)
|
||||
.map(J.CompilationUnit.class::cast)
|
||||
.findFirst()
|
||||
.get();
|
||||
List<String> typesInUseBefore = cuBefore.getTypesInUse()
|
||||
.getTypesInUse()
|
||||
.stream()
|
||||
.map(JavaType.FullyQualified.class::cast)
|
||||
.map(JavaType.FullyQualified::getFullyQualifiedName)
|
||||
.toList();
|
||||
assertThat(typesInUseBefore).isEmpty();
|
||||
List<String> classpathBefore = cuBefore.getMarkers()
|
||||
.findFirst(JavaSourceSet.class)
|
||||
.get()
|
||||
.getClasspath()
|
||||
.stream()
|
||||
.map(JavaType.FullyQualified::getFullyQualifiedName)
|
||||
.toList();
|
||||
assertThat(classpathBefore).contains("org.springframework.boot.autoconfigure.SpringBootApplication",
|
||||
"SomeClass");
|
||||
}
|
||||
|
||||
private static void pepareProject(Path baseDir) {
|
||||
TestProjectHelper.createTestProject(baseDir)
|
||||
.addResource("pom.xml",
|
||||
"""
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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>
|
||||
<groupId>org.example</groupId>
|
||||
<artifactId>artifact</artifactId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure</artifactId>
|
||||
<version>3.1.3</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
""")
|
||||
|
||||
.addResource("src/main/java/SomeClass.java",
|
||||
// @formatter:off
|
||||
"""
|
||||
public class SomeClass {}
|
||||
"""
|
||||
// @formatter:on
|
||||
)
|
||||
.writeToFilesystem();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
class WithMultiModuleMavenProject {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,514 @@
|
||||
/*
|
||||
* Copyright 2021 - 2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://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.springframework.rewrite;
|
||||
|
||||
import org.intellij.lang.annotations.Language;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.junit.jupiter.api.*;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.openrewrite.ExecutionContext;
|
||||
import org.openrewrite.RecipeRun;
|
||||
import org.openrewrite.SourceFile;
|
||||
import org.openrewrite.internal.InMemoryLargeSourceSet;
|
||||
import org.openrewrite.java.JavaIsoVisitor;
|
||||
import org.openrewrite.java.JavaParser;
|
||||
import org.openrewrite.java.JavaTemplate;
|
||||
import org.openrewrite.java.marker.JavaSourceSet;
|
||||
import org.openrewrite.java.tree.J;
|
||||
import org.openrewrite.java.tree.JavaType;
|
||||
import org.openrewrite.java.tree.Statement;
|
||||
import org.springframework.rewrite.parsers.RewriteExecutionContext;
|
||||
import org.springframework.rewrite.parsers.RewriteProjectParsingResult;
|
||||
import org.springframework.rewrite.parsers.SpringRewriteProperties;
|
||||
import org.springframework.rewrite.parsers.maven.ClasspathDependencies;
|
||||
import org.springframework.rewrite.support.openrewrite.GenericOpenRewriteRecipe;
|
||||
import org.springframework.rewrite.test.util.ParserExecutionHelper;
|
||||
import org.springframework.rewrite.test.util.TestProjectHelper;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class MavenTypeResolutionTests {
|
||||
|
||||
@Nested
|
||||
class ClassesFromMainCanBeResolvedInTest {
|
||||
|
||||
@Test
|
||||
@DisplayName("TestClass should have MainClass in used types")
|
||||
void testClassShouldHaveMainClassInUsedTypes(@TempDir Path baseDir) {
|
||||
|
||||
TestProjectHelper.createTestProject(baseDir)
|
||||
.addResource("pom.xml",
|
||||
"""
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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>
|
||||
<groupId>org.example</groupId>
|
||||
<artifactId>artifact</artifactId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure</artifactId>
|
||||
<version>3.1.3</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
""")
|
||||
|
||||
.addResource("src/main/java/main/SomeClass.java", """
|
||||
package main;
|
||||
public class SomeClass {}
|
||||
""")
|
||||
.addResource("src/test/java/test/SomeTest.java", """
|
||||
package test;
|
||||
import main.SomeClass;
|
||||
public class SomeTest {
|
||||
private SomeClass someClass;
|
||||
}
|
||||
""")
|
||||
.writeToFilesystem();
|
||||
|
||||
RewriteProjectParsingResult parsingResult = new ParserExecutionHelper()
|
||||
.parseWithRewriteProjectParser(baseDir, new SpringRewriteProperties());
|
||||
|
||||
SourceFile sourceFile = parsingResult.sourceFiles()
|
||||
.stream()
|
||||
.filter(f -> f.getSourcePath().toString().endsWith("SomeTest.java"))
|
||||
.findFirst()
|
||||
.get();
|
||||
assertThat(sourceFile).isNotNull();
|
||||
J.CompilationUnit cu = (J.CompilationUnit) sourceFile;
|
||||
cu.getClasses().get(0).getBody().getStatements().get(0);
|
||||
assertThat(
|
||||
((JavaType.Class) ((J.VariableDeclarations) cu.getClasses().get(0).getBody().getStatements().get(0))
|
||||
.getTypeExpression()
|
||||
.getType()).getFullyQualifiedName())
|
||||
.isEqualTo("main.SomeClass");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
class WithSingleModuleMavenProject {
|
||||
|
||||
private static RewriteProjectParsingResult parsingResult;
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
@DisplayName("parsed project should have correct classpath")
|
||||
void parsedProjectShouldHaveCorrectClasspath(@TempDir Path baseDir) {
|
||||
// given
|
||||
pepareProject(baseDir);
|
||||
// when
|
||||
parsingResult = new ParserExecutionHelper().parseWithRewriteProjectParser(baseDir,
|
||||
new SpringRewriteProperties());
|
||||
// then
|
||||
verifyTypesOnClasspathAndNoTypesInUse(parsingResult);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(2)
|
||||
@DisplayName("annotating the class adds the annotation to typesInUse")
|
||||
void annotatingTheClassAddsTheAnnotationToTypesInUse() {
|
||||
// Given: parsed project
|
||||
List<SourceFile> sourceFiles = parsingResult.sourceFiles();
|
||||
assertThat(sourceFiles).isNotEmpty();
|
||||
// When: Add @SpringBootApplication annotation to class
|
||||
RecipeRun recipeRun = annotateClass(sourceFiles);
|
||||
// Then: annotation is in typesInUse
|
||||
verifyAnnotationIsNowInUsedTypes(recipeRun);
|
||||
}
|
||||
|
||||
private static void verifyAnnotationIsNowInUsedTypes(RecipeRun recipeRun) {
|
||||
SourceFile after = recipeRun.getChangeset().getAllResults().get(0).getAfter();
|
||||
assertThat(after).isInstanceOf(J.CompilationUnit.class);
|
||||
J.CompilationUnit cu = (J.CompilationUnit) after;
|
||||
List<String> classpath = cu.getMarkers()
|
||||
.findFirst(JavaSourceSet.class)
|
||||
.get()
|
||||
.getClasspath()
|
||||
.stream()
|
||||
.map(JavaType.FullyQualified::getFullyQualifiedName)
|
||||
.toList();
|
||||
assertThat(classpath).contains("org.springframework.boot.autoconfigure.SpringBootApplication", "SomeClass");
|
||||
|
||||
List<String> typesInUse = cu.getTypesInUse()
|
||||
.getTypesInUse()
|
||||
.stream()
|
||||
.map(JavaType.FullyQualified.class::cast)
|
||||
.map(JavaType.FullyQualified::getFullyQualifiedName)
|
||||
.toList();
|
||||
assertThat(typesInUse)
|
||||
.containsExactlyInAnyOrder("org.springframework.boot.autoconfigure.SpringBootApplication");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static RecipeRun annotateClass(List<SourceFile> sourceFiles) {
|
||||
RecipeRun recipeRun = new GenericOpenRewriteRecipe<>(() -> new JavaIsoVisitor<>() {
|
||||
@Override
|
||||
public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl,
|
||||
ExecutionContext executionContext) {
|
||||
J.ClassDeclaration cd = super.visitClassDeclaration(classDecl, executionContext);
|
||||
if (cd.getSimpleName().equals("SomeClass")) {
|
||||
ClasspathDependencies classpathDependencies = ((J.CompilationUnit) getCursor()
|
||||
.dropParentUntil(J.CompilationUnit.class::isInstance)
|
||||
.getValue()).getMarkers().findFirst(ClasspathDependencies.class).get();
|
||||
String annotationFqName = "org.springframework.boot.autoconfigure.SpringBootApplication";
|
||||
cd = JavaTemplate.builder("@SpringBootApplication")
|
||||
.imports(annotationFqName)
|
||||
.javaParser(JavaParser.fromJavaVersion()
|
||||
.classpath(classpathDependencies.getDependencies())
|
||||
.logCompilationWarningsAndErrors(true))
|
||||
.build()
|
||||
.apply(getCursor(), cd.getCoordinates()
|
||||
.addAnnotation(Comparator.comparing(J.Annotation::getSimpleName)));
|
||||
maybeAddImport(annotationFqName);
|
||||
}
|
||||
return cd;
|
||||
}
|
||||
}).run(new InMemoryLargeSourceSet(sourceFiles), new RewriteExecutionContext());
|
||||
return recipeRun;
|
||||
}
|
||||
|
||||
private static void verifyTypesOnClasspathAndNoTypesInUse(RewriteProjectParsingResult parallelParsingResult) {
|
||||
J.CompilationUnit cuBefore = parallelParsingResult.sourceFiles()
|
||||
.stream()
|
||||
.filter(J.CompilationUnit.class::isInstance)
|
||||
.map(J.CompilationUnit.class::cast)
|
||||
.findFirst()
|
||||
.get();
|
||||
List<String> typesInUseBefore = cuBefore.getTypesInUse()
|
||||
.getTypesInUse()
|
||||
.stream()
|
||||
.map(JavaType.FullyQualified.class::cast)
|
||||
.map(JavaType.FullyQualified::getFullyQualifiedName)
|
||||
.toList();
|
||||
assertThat(typesInUseBefore).isEmpty();
|
||||
List<String> classpathBefore = cuBefore.getMarkers()
|
||||
.findFirst(JavaSourceSet.class)
|
||||
.get()
|
||||
.getClasspath()
|
||||
.stream()
|
||||
.map(JavaType.FullyQualified::getFullyQualifiedName)
|
||||
.toList();
|
||||
assertThat(classpathBefore).contains("org.springframework.boot.autoconfigure.SpringBootApplication",
|
||||
"SomeClass");
|
||||
}
|
||||
|
||||
private static void pepareProject(Path baseDir) {
|
||||
// formatter:off
|
||||
TestProjectHelper.createTestProject(baseDir)
|
||||
.addResource("pom.xml",
|
||||
"""
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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>
|
||||
<groupId>org.example</groupId>
|
||||
<artifactId>artifact</artifactId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure</artifactId>
|
||||
<version>3.1.3</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
""")
|
||||
|
||||
.addResource("src/main/java/SomeClass.java", """
|
||||
public class SomeClass {}
|
||||
""")
|
||||
.writeToFilesystem();
|
||||
// formatter:on
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
@Nested
|
||||
class WithMultiModuleMavenProject {
|
||||
|
||||
/**
|
||||
* formatter:off Multi-module project with two modules module-1 and module-2.
|
||||
* module-2 has a dependency to spring and a class in main Module2Class and one in
|
||||
* test Module2Test module-1 has a dependency to module-2 and a class in main
|
||||
* Module1Class and one in test Module1Test, Module1Class depends on Module2Class
|
||||
* and MOdule1Test depends on Module2Class and Module2Test. formatter:on
|
||||
*/
|
||||
@Test
|
||||
@DisplayName("Module-1 should have classpath from module-2")
|
||||
void module1ShouldHaveClasspathFromModule2(@TempDir Path baseDir) {
|
||||
TestProjectHelper.createTestProject(baseDir)
|
||||
.addResource("pom.xml", PARENT_POM)
|
||||
.addResource("module-1/pom.xml", MODULE_1_POM)
|
||||
.addResource("module-2/pom.xml", MODULE_2_POM)
|
||||
.addResource("module-1/src/main/java/com/example/module1/main/Module1Class.java", MODULE_1_CLASS)
|
||||
.addResource("module-1/src/test/java/com/example/module1/test/Module1Test.java", MODULE_1_TEST)
|
||||
.addResource("module-2/src/main/java/com/example/module2/main/Module2Class.java", MODULE_2_CLASS)
|
||||
.addResource("module-2/src/test/java/com/example/module2/test/Module2Test.java", MODULE_2_TEST)
|
||||
.writeToFilesystem();
|
||||
|
||||
RewriteProjectParsingResult parsingResult = new ParserExecutionHelper()
|
||||
.parseWithRewriteProjectParser(baseDir, new SpringRewriteProperties());
|
||||
|
||||
// Module2Class
|
||||
J.CompilationUnit module2class = getCompilationContainginTypeEndingWith("Module2Class.java", parsingResult);
|
||||
// classpath
|
||||
List<String> cpModule2 = getClasspath(module2class);
|
||||
// typesInUse
|
||||
List<String> module2classTypesInUse = getTypesInUse(module2class);
|
||||
|
||||
assertThat(module2classTypesInUse).contains("org.springframework.boot.autoconfigure.SpringBootApplication");
|
||||
assertThat(cpModule2).contains("org.springframework.boot.autoconfigure.SpringBootApplication");
|
||||
// Module2Test
|
||||
J.CompilationUnit module2test = getCompilationContainginTypeEndingWith("Module2Test.java", parsingResult);
|
||||
// typesInUse
|
||||
List<String> module2testTypesInUse = getTypesInUse(module2test);
|
||||
// classpath
|
||||
List<String> classpathModule2test = getClasspath(module2test);
|
||||
|
||||
Statement actual = module2test.getClasses().get(0).getBody().getStatements().get(0);
|
||||
assertThat(classpathModule2test).contains("com.example.module2.main.Module2Class",
|
||||
"com.example.module2.test.Module2Test",
|
||||
"org.springframework.boot.autoconfigure.SpringBootApplication",
|
||||
"org.springframework.boot.test.context.SpringBootTest");
|
||||
|
||||
// Module1Class
|
||||
J.CompilationUnit module1class = getCompilationContainginTypeEndingWith("Module1Class.java", parsingResult);
|
||||
// typesInUse
|
||||
List<String> module1classTypesInUse = getTypesInUse(module1class);
|
||||
// classpath
|
||||
List<String> module1classClasspath = getClasspath(module1class);
|
||||
|
||||
assertThat(module1classTypesInUse).contains("com.example.module2.main.Module2Class");
|
||||
assertThat(module1classClasspath).contains("com.example.module1.main.Module1Class",
|
||||
"com.example.module2.main.Module2Class",
|
||||
"org.springframework.boot.autoconfigure.SpringBootApplication");
|
||||
|
||||
assertThat(module1classClasspath).doesNotContain("com.example.module2.test.Module2Test",
|
||||
"org.springframework.boot.test.context.SpringBootTest");
|
||||
|
||||
// Module1Test
|
||||
J.CompilationUnit module1test = getCompilationContainginTypeEndingWith("Module1Test.java", parsingResult);
|
||||
// typesInUse
|
||||
List<String> module1testTypesInUse = getTypesInUse(module1test);
|
||||
// classpath
|
||||
List<String> classpathModule1test = getClasspath(module1test);
|
||||
|
||||
assertThat(module1testTypesInUse).contains("com.example.module2.main.Module2Class",
|
||||
"com.example.module2.test.Module2Test", "org.springframework.boot.test.context.SpringBootTest");
|
||||
assertThat(classpathModule1test).contains("com.example.module1.main.Module1Class",
|
||||
"com.example.module1.test.Module1Test",
|
||||
"org.springframework.boot.autoconfigure.SpringBootApplication",
|
||||
"org.springframework.boot.test.context.SpringBootTest", "com.example.module2.main.Module2Class",
|
||||
"com.example.module2.test.Module2Test");
|
||||
|
||||
}
|
||||
|
||||
// @formatter:off
|
||||
|
||||
@Language("xml")
|
||||
private static final String PARENT_POM =
|
||||
"""
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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>
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>parent</artifactId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
</properties>
|
||||
<modules>
|
||||
<module>module-1</module>
|
||||
<module>module-2</module>
|
||||
</modules>
|
||||
</project>
|
||||
""";
|
||||
|
||||
@Language("xml")
|
||||
private static final String MODULE_1_POM =
|
||||
"""
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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>com.example</groupId>
|
||||
<artifactId>parent</artifactId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<artifactId>module-1</artifactId>
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>module-2</artifactId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-test</artifactId>
|
||||
<version>3.1.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
""";
|
||||
|
||||
private static final String MODULE_1_CLASS =
|
||||
"""
|
||||
package com.example.module1.main;
|
||||
import com.example.module2.main.Module2Class;
|
||||
|
||||
public class Module1Class {
|
||||
private Module2Class module2Class;
|
||||
}
|
||||
""";
|
||||
|
||||
private static final String MODULE_1_TEST =
|
||||
"""
|
||||
package com.example.module1.test;
|
||||
import com.example.module2.main.Module2Class;
|
||||
import com.example.module2.test.Module2Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@SpringBootTest
|
||||
public class Module1Test extends Module2Test {
|
||||
private Module1Class module1Class;
|
||||
private Module2Class module2Class;
|
||||
}
|
||||
""";
|
||||
|
||||
@Language("xml")
|
||||
private static final String MODULE_2_POM =
|
||||
"""
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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>com.example</groupId>
|
||||
<artifactId>parent</artifactId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<artifactId>module-2</artifactId>
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure</artifactId>
|
||||
<version>3.1.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-test</artifactId>
|
||||
<version>3.1.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
""";
|
||||
private static final String MODULE_2_CLASS =
|
||||
"""
|
||||
package com.example.module2.main;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class Module2Class {
|
||||
}
|
||||
""";
|
||||
|
||||
private static final String MODULE_2_TEST =
|
||||
"""
|
||||
package com.example.module2.test;
|
||||
import com.example.module2.main.Module2Class;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@SpringBootTest
|
||||
public class Module2Test {
|
||||
Module2Class module2Class;
|
||||
}
|
||||
""";
|
||||
|
||||
// @formatter:on
|
||||
|
||||
@NotNull
|
||||
private static List<String> getClasspath(J.CompilationUnit module2class) {
|
||||
return module2class.getMarkers()
|
||||
.findFirst(JavaSourceSet.class)
|
||||
.get()
|
||||
.getClasspath()
|
||||
.stream()
|
||||
.map(JavaType.FullyQualified::getFullyQualifiedName)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<String> getTypesInUse(J.CompilationUnit module2class) {
|
||||
return module2class.getTypesInUse()
|
||||
.getTypesInUse()
|
||||
.stream()
|
||||
.map(JavaType.FullyQualified.class::cast)
|
||||
.map(JavaType.FullyQualified::getFullyQualifiedName)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static J.CompilationUnit getCompilationContainginTypeEndingWith(String endsWith,
|
||||
RewriteProjectParsingResult parsingResult) {
|
||||
return (J.CompilationUnit) parsingResult.sourceFiles()
|
||||
.stream()
|
||||
.filter(sf -> sf.getSourcePath().toString().endsWith(endsWith))
|
||||
.findFirst()
|
||||
.get();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -15,8 +15,9 @@
|
||||
*/
|
||||
package org.springframework.rewrite.parsers;
|
||||
|
||||
import org.apache.maven.model.Model;
|
||||
import org.apache.maven.model.Plugin;
|
||||
import org.apache.maven.model.*;
|
||||
import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
|
||||
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.openrewrite.maven.tree.MavenResolutionResult;
|
||||
import org.openrewrite.maven.tree.ResolvedDependency;
|
||||
@@ -29,11 +30,9 @@ import org.springframework.rewrite.utils.LinuxWindowsPathUnifier;
|
||||
import org.springframework.rewrite.utils.ResourceUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Properties;
|
||||
import java.util.*;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
@@ -43,14 +42,24 @@ public class MavenProject {
|
||||
|
||||
private final Path projectRoot;
|
||||
|
||||
private final Resource pomFile;
|
||||
private final MavenBuildFile buildFile;
|
||||
|
||||
// FIXME: 945 temporary method, model should nopt come from Maven
|
||||
private final Model pomModel;
|
||||
/**
|
||||
* All {@link MavenProject}s of this build.
|
||||
*/
|
||||
private List<MavenProject> reactorProjects = new ArrayList<>();
|
||||
|
||||
private List<MavenProject> collectedProjects = new ArrayList<>();
|
||||
/**
|
||||
* List of {@link MavenProject}s that depend on this project.
|
||||
*/
|
||||
private final List<MavenProject> dependentProjects = new ArrayList<>();
|
||||
|
||||
private Xml.Document sourceFile;
|
||||
/**
|
||||
* List of {@link MavenProject}s this project depends on.
|
||||
*/
|
||||
private final List<MavenProject> dependencyProjects = new ArrayList<>();
|
||||
|
||||
private final List<MavenProject> moduleProjects = new ArrayList<>();
|
||||
|
||||
private final MavenArtifactDownloader rewriteMavenArtifactDownloader;
|
||||
|
||||
@@ -58,35 +67,58 @@ public class MavenProject {
|
||||
|
||||
private ProjectId projectId;
|
||||
|
||||
public MavenProject(Path projectRoot, Resource pomFile, Model pomModel,
|
||||
public MavenProject(Path baseDir, Resource rootPom, MavenArtifactDownloader rewriteMavenArtifactDownloader,
|
||||
List<Resource> resources) {
|
||||
this(baseDir, rootPom, List.of(), rewriteMavenArtifactDownloader, resources);
|
||||
}
|
||||
|
||||
public MavenProject(Path baseDir, Resource pomFile, List<MavenProject> dependsOnModels,
|
||||
MavenArtifactDownloader rewriteMavenArtifactDownloader, List<Resource> resources) {
|
||||
this.projectRoot = projectRoot;
|
||||
this.pomFile = pomFile;
|
||||
this.pomModel = pomModel;
|
||||
this.projectRoot = baseDir;
|
||||
this.buildFile = new MavenBuildFile(pomFile);
|
||||
if (dependsOnModels != null) {
|
||||
this.dependentProjects.addAll(dependsOnModels);
|
||||
}
|
||||
this.rewriteMavenArtifactDownloader = rewriteMavenArtifactDownloader;
|
||||
this.resources = resources;
|
||||
projectId = new ProjectId(getGroupId(), getArtifactId());
|
||||
}
|
||||
|
||||
public List<MavenProject> getDependentProjects() {
|
||||
return dependentProjects;
|
||||
}
|
||||
|
||||
public void setDependencyProjects(List<MavenProject> dependencyProjects) {
|
||||
this.dependencyProjects.clear();
|
||||
this.dependencyProjects.addAll(dependencyProjects);
|
||||
}
|
||||
|
||||
public List<MavenProject> getDependencyProjects() {
|
||||
return dependencyProjects;
|
||||
}
|
||||
|
||||
public File getFile() {
|
||||
return ResourceUtil.getPath(pomFile).toFile();
|
||||
return buildFile.getPath().toFile();
|
||||
}
|
||||
|
||||
public MavenBuildFile getBuildFile() {
|
||||
return buildFile;
|
||||
}
|
||||
|
||||
public Path getBasedir() {
|
||||
// TODO: 945 Check if this is correct
|
||||
return pomFile == null ? null : ResourceUtil.getPath(pomFile).getParent();
|
||||
return buildFile == null ? null : buildFile.getPath().getParent();
|
||||
}
|
||||
|
||||
public void setCollectedProjects(List<MavenProject> collected) {
|
||||
this.collectedProjects = collected;
|
||||
public void setReactorProjects(List<MavenProject> collected) {
|
||||
this.reactorProjects = collected;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return all {@link MavenProject}s belonging to the same reactor build.
|
||||
*/
|
||||
public List<MavenProject> getCollectedProjects() {
|
||||
return collectedProjects;
|
||||
}
|
||||
|
||||
public Resource getResource() {
|
||||
return pomFile;
|
||||
return reactorProjects;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -103,29 +135,28 @@ public class MavenProject {
|
||||
if (getBasedir() == null) {
|
||||
return null;
|
||||
}
|
||||
else if ("pom.xml"
|
||||
.equals(LinuxWindowsPathUnifier.relativize(projectRoot, ResourceUtil.getPath(pomFile)).toString())) {
|
||||
else if ("pom.xml".equals(LinuxWindowsPathUnifier.relativize(projectRoot, buildFile.getPath()).toString())) {
|
||||
return Path.of("");
|
||||
}
|
||||
else {
|
||||
return LinuxWindowsPathUnifier.relativize(projectRoot, ResourceUtil.getPath(pomFile)).getParent();
|
||||
return LinuxWindowsPathUnifier.relativize(projectRoot, buildFile.getPath()).getParent();
|
||||
}
|
||||
}
|
||||
|
||||
public String getGroupIdAndArtifactId() {
|
||||
return this.pomModel.getGroupId() + ":" + pomModel.getArtifactId();
|
||||
return this.buildFile.getGroupIdAndArtifactId();
|
||||
}
|
||||
|
||||
public Path getPomFilePath() {
|
||||
return ResourceUtil.getPath(pomFile);
|
||||
return buildFile.getPath();
|
||||
}
|
||||
|
||||
public Plugin getPlugin(String s) {
|
||||
return pomModel.getBuild() == null ? null : pomModel.getBuild().getPluginsAsMap().get(s);
|
||||
return buildFile.getBuild() == null ? null : buildFile.getBuild().getPluginsAsMap().get(s);
|
||||
}
|
||||
|
||||
public Properties getProperties() {
|
||||
return pomModel.getProperties();
|
||||
return buildFile.getProperties();
|
||||
}
|
||||
|
||||
public MavenRuntimeInformation getMavenRuntimeInformation() {
|
||||
@@ -134,15 +165,15 @@ public class MavenProject {
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return pomModel.getName();
|
||||
return buildFile.getName();
|
||||
}
|
||||
|
||||
public String getGroupId() {
|
||||
return pomModel.getGroupId() == null ? pomModel.getParent().getGroupId() : pomModel.getGroupId();
|
||||
return buildFile.getGroupId() == null ? buildFile.getParent().getGroupId() : buildFile.getGroupId();
|
||||
}
|
||||
|
||||
public String getArtifactId() {
|
||||
return pomModel.getArtifactId();
|
||||
return buildFile.getArtifactId();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -150,30 +181,25 @@ public class MavenProject {
|
||||
* version will be null.
|
||||
*/
|
||||
public String getVersion() {
|
||||
return pomModel.getVersion() == null ? pomModel.getParent().getVersion() : pomModel.getVersion();
|
||||
return buildFile.getVersion() == null ? buildFile.getParent().getVersion() : buildFile.getVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String groupId = pomModel.getGroupId() == null ? pomModel.getParent().getGroupId() : pomModel.getGroupId();
|
||||
return groupId + ":" + pomModel.getArtifactId();
|
||||
String groupId = buildFile.getGroupId() == null ? buildFile.getParent().getGroupId() : buildFile.getGroupId();
|
||||
return groupId + ":" + buildFile.getArtifactId();
|
||||
}
|
||||
|
||||
public String getBuildDirectory() {
|
||||
String s = pomModel.getBuild() != null ? pomModel.getBuild().getDirectory() : null;
|
||||
return s == null
|
||||
? ResourceUtil.getPath(pomFile).getParent().resolve("target").toAbsolutePath().normalize().toString()
|
||||
String s = buildFile.getBuild() != null ? buildFile.getBuild().getDirectory() : null;
|
||||
return s == null ? buildFile.getPath().getParent().resolve("target").toAbsolutePath().normalize().toString()
|
||||
: s;
|
||||
}
|
||||
|
||||
public String getSourceDirectory() {
|
||||
String s = pomModel.getBuild() != null ? pomModel.getBuild().getSourceDirectory() : null;
|
||||
return s == null ? ResourceUtil.getPath(pomFile)
|
||||
.getParent()
|
||||
.resolve("src/main/java")
|
||||
.toAbsolutePath()
|
||||
.normalize()
|
||||
.toString() : s;
|
||||
String s = buildFile.getBuild() != null ? buildFile.getBuild().getSourceDirectory() : null;
|
||||
return s == null
|
||||
? buildFile.getPath().getParent().resolve("src/main/java").toAbsolutePath().normalize().toString() : s;
|
||||
}
|
||||
|
||||
public List<Path> getCompileClasspathElements() {
|
||||
@@ -187,7 +213,20 @@ public class MavenProject {
|
||||
|
||||
@NotNull
|
||||
private List<Path> getClasspathElements(Scope scope) {
|
||||
MavenResolutionResult pom = getSourceFile().getMarkers().findFirst(MavenResolutionResult.class).get();
|
||||
Xml.Document pomSourceFile = getSourceFile();
|
||||
return getClasspathJars(scope, pomSourceFile);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private List<Path> getClasspathJars(Scope scope, Xml.Document pomSourceFile) {
|
||||
MavenArtifactDownloader downloader = rewriteMavenArtifactDownloader;
|
||||
return getClasspathJars(scope, pomSourceFile, downloader);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static List<Path> getClasspathJars(Scope scope, Xml.Document pomSourceFile,
|
||||
MavenArtifactDownloader downloader) {
|
||||
MavenResolutionResult pom = pomSourceFile.getMarkers().findFirst(MavenResolutionResult.class).get();
|
||||
List<ResolvedDependency> resolvedDependencies = pom.getDependencies().get(scope);
|
||||
if (resolvedDependencies != null) {
|
||||
return resolvedDependencies
|
||||
@@ -195,7 +234,7 @@ public class MavenProject {
|
||||
//
|
||||
.stream()
|
||||
.filter(rd -> rd.getRepository() != null)
|
||||
.map(rd -> rewriteMavenArtifactDownloader.downloadArtifact(rd))
|
||||
.map(rd -> downloader.downloadArtifact(rd))
|
||||
.filter(Objects::nonNull)
|
||||
.distinct()
|
||||
.toList();
|
||||
@@ -206,17 +245,13 @@ public class MavenProject {
|
||||
}
|
||||
|
||||
public String getTestSourceDirectory() {
|
||||
String s = pomModel.getBuild() != null ? pomModel.getBuild().getSourceDirectory() : null;
|
||||
return s == null ? ResourceUtil.getPath(pomFile)
|
||||
.getParent()
|
||||
.resolve("src/test/java")
|
||||
.toAbsolutePath()
|
||||
.normalize()
|
||||
.toString() : s;
|
||||
String s = buildFile.getBuild() != null ? buildFile.getBuild().getSourceDirectory() : null;
|
||||
return s == null
|
||||
? buildFile.getPath().getParent().resolve("src/test/java").toAbsolutePath().normalize().toString() : s;
|
||||
}
|
||||
|
||||
public void setSourceFile(Xml.Document sourceFile) {
|
||||
this.sourceFile = sourceFile;
|
||||
this.buildFile.setSourceFile(sourceFile);
|
||||
}
|
||||
|
||||
private static List<Resource> listJavaSources(List<Resource> resources, Path sourceDirectory) {
|
||||
@@ -245,6 +280,9 @@ public class MavenProject {
|
||||
return this.resources;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return All {@link Resource}s found under {@code src/main/java} of this project.
|
||||
*/
|
||||
public List<Resource> getMainJavaSources() {
|
||||
Path sourceDir = getProjectRoot().resolve(getModuleDir()).resolve("src/main/java");
|
||||
return listJavaSources(resources, sourceDir);
|
||||
@@ -259,31 +297,316 @@ public class MavenProject {
|
||||
}
|
||||
|
||||
public Object getProjectEncoding() {
|
||||
return getPomModel().getProperties().get("project.build.sourceEncoding");
|
||||
return buildFile.getProperties().get("project.build.sourceEncoding");
|
||||
}
|
||||
|
||||
public Path getProjectRoot() {
|
||||
return projectRoot;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public Resource getPomFile() {
|
||||
return pomFile;
|
||||
}
|
||||
|
||||
public Model getPomModel() {
|
||||
return pomModel;
|
||||
return buildFile.getPomFileResource();
|
||||
}
|
||||
|
||||
public Xml.Document getSourceFile() {
|
||||
return sourceFile;
|
||||
return buildFile.getSourceFile();
|
||||
}
|
||||
|
||||
public MavenArtifactDownloader getRewriteMavenArtifactDownloader() {
|
||||
return rewriteMavenArtifactDownloader;
|
||||
public boolean dependsOn(MavenProject model) {
|
||||
return dependentProjects.stream()
|
||||
.anyMatch(
|
||||
m -> m.getGroupId().equals(model.getGroupId()) && m.getArtifactId().equals(model.getArtifactId()));
|
||||
}
|
||||
|
||||
public void setProjectId(ProjectId projectId) {
|
||||
this.projectId = projectId;
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o)
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
MavenProject that = (MavenProject) o;
|
||||
return Objects.equals(buildFile, that.buildFile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(buildFile);
|
||||
}
|
||||
|
||||
public class MavenBuildFile extends org.apache.maven.model.Model {
|
||||
|
||||
private final Resource pomFileResource;
|
||||
|
||||
// FIXME: 945 temporary method, model should nopt come from Maven
|
||||
@Deprecated
|
||||
private final Resource resource;
|
||||
|
||||
private Xml.Document sourceFile;
|
||||
|
||||
private final org.apache.maven.model.Model delegate;
|
||||
|
||||
private static final MavenXpp3Reader XPP_3_READER = new MavenXpp3Reader();
|
||||
|
||||
public MavenBuildFile(Resource pomFileResource) {
|
||||
this.pomFileResource = pomFileResource;
|
||||
assertPomFile(pomFileResource);
|
||||
this.resource = pomFileResource;
|
||||
try {
|
||||
this.delegate = XPP_3_READER.read(ResourceUtil.getInputStream(resource));
|
||||
this.delegate.setPomFile(resource.getFile());
|
||||
List<Dependency> dependencies = this.delegate.getDependencies();
|
||||
dependencies.forEach(d -> {
|
||||
|
||||
});
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
catch (XmlPullParserException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertPomFile(Resource resource) {
|
||||
if (!LinuxWindowsPathUnifier.unifiedPathString(resource).endsWith("pom.xml")) {
|
||||
throw new IllegalArgumentException(
|
||||
"Provided resource '%s' is not a pom.xml file.".formatted(ResourceUtil.getPath(resource)));
|
||||
}
|
||||
}
|
||||
|
||||
public void setSourceFile(Xml.Document sourceFile) {
|
||||
this.sourceFile = sourceFile;
|
||||
}
|
||||
|
||||
public Xml.Document getSourceFile() {
|
||||
return sourceFile;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return ResourceUtil.getContent(pomFileResource);
|
||||
}
|
||||
|
||||
public Path getPath() {
|
||||
return ResourceUtil.getPath(pomFileResource);
|
||||
}
|
||||
|
||||
public Resource getPomFileResource() {
|
||||
return pomFileResource;
|
||||
}
|
||||
|
||||
// Model methods
|
||||
@Override
|
||||
public String toString() {
|
||||
return (delegate.getGroupId() == null ? delegate.getParent().getGroupId() : delegate.getGroupId()) + ":"
|
||||
+ delegate.getArtifactId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getArtifactId() {
|
||||
return delegate.getArtifactId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Build getBuild() {
|
||||
return delegate.getBuild();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getChildProjectUrlInheritAppendPath() {
|
||||
return delegate.getChildProjectUrlInheritAppendPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CiManagement getCiManagement() {
|
||||
return delegate.getCiManagement();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Contributor> getContributors() {
|
||||
return delegate.getContributors();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return delegate.getDescription();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Developer> getDevelopers() {
|
||||
return delegate.getDevelopers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getGroupId() {
|
||||
return delegate.getGroupId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getInceptionYear() {
|
||||
return delegate.getInceptionYear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IssueManagement getIssueManagement() {
|
||||
return delegate.getIssueManagement();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<License> getLicenses() {
|
||||
return delegate.getLicenses();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MailingList> getMailingLists() {
|
||||
return delegate.getMailingLists();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getModelEncoding() {
|
||||
return delegate.getModelEncoding();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getModelVersion() {
|
||||
return delegate.getModelVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
String name = delegate.getName();
|
||||
if (name == null) {
|
||||
name = delegate.getArtifactId();
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Organization getOrganization() {
|
||||
return delegate.getOrganization();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPackaging() {
|
||||
return delegate.getPackaging();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Parent getParent() {
|
||||
return delegate.getParent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Prerequisites getPrerequisites() {
|
||||
return delegate.getPrerequisites();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Profile> getProfiles() {
|
||||
return delegate.getProfiles();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Scm getScm() {
|
||||
return delegate.getScm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUrl() {
|
||||
return delegate.getUrl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return delegate.getVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getPomFile() {
|
||||
return delegate.getPomFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getProjectDirectory() {
|
||||
return delegate.getPomFile().toPath().getParent().toFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return delegate.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Dependency> getDependencies() {
|
||||
return delegate.getDependencies();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DependencyManagement getDependencyManagement() {
|
||||
return delegate.getDependencyManagement();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DistributionManagement getDistributionManagement() {
|
||||
return delegate.getDistributionManagement();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputLocation getLocation(Object key) {
|
||||
return delegate.getLocation(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getModules() {
|
||||
return delegate.getModules();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Repository> getPluginRepositories() {
|
||||
return delegate.getPluginRepositories();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Properties getProperties() {
|
||||
return delegate.getProperties();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Reporting getReporting() {
|
||||
return delegate.getReporting();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getReports() {
|
||||
return delegate.getReports();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Repository> getRepositories() {
|
||||
return delegate.getRepositories();
|
||||
}
|
||||
|
||||
public String getGroupIdAndArtifactId() {
|
||||
return getGroupId() + ":" + getArtifactId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o)
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
MavenBuildFile that = (MavenBuildFile) o;
|
||||
Path thisPath = ResourceUtil.getPath(this.pomFileResource);
|
||||
Path thatPath = ResourceUtil.getPath(that.pomFileResource);
|
||||
return Objects.equals(thisPath, thatPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(pomFileResource);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2021 - 2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://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.springframework.rewrite.parsers;
|
||||
|
||||
import org.openrewrite.SourceFile;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Fabian Krüger
|
||||
*/
|
||||
public record ModuleParsingResult(MavenProject currentProject, SourceSetParsingResult mainSourcesParsingResult,
|
||||
SourceSetParsingResult testSourcesParsingResult, List<SourceFile> resourceFilesList) {
|
||||
public List<? extends SourceFile> sourceFiles() {
|
||||
List<SourceFile> allSourceFiles = new ArrayList<>();
|
||||
allSourceFiles.addAll(mainSourcesParsingResult.sourceFiles());
|
||||
allSourceFiles.addAll(testSourcesParsingResult.sourceFiles());
|
||||
allSourceFiles.addAll(resourceFilesList);
|
||||
return allSourceFiles;
|
||||
}
|
||||
}
|
||||
@@ -102,7 +102,10 @@ public class RewriteParserConfiguration {
|
||||
|
||||
@Bean
|
||||
MavenProjectAnalyzer mavenProjectAnalyzer(MavenArtifactDownloader artifactDownloader) {
|
||||
return new MavenProjectAnalyzer(artifactDownloader);
|
||||
MavenProjectGraph mavenProjectGraph = new MavenProjectGraph();
|
||||
MavenProjectSorter mavenProjectSorter = new MavenProjectSorter(mavenProjectGraph);
|
||||
MavenProjectFactory mavenProjectFactory = new MavenProjectFactory(artifactDownloader);
|
||||
return new MavenProjectAnalyzer(mavenProjectSorter, mavenProjectFactory);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -35,7 +35,6 @@ import org.springframework.rewrite.parsers.maven.MavenProjectAnalyzer;
|
||||
import org.springframework.rewrite.parsers.maven.ProvenanceMarkerFactory;
|
||||
import org.springframework.rewrite.recipes.RewriteRecipeDiscovery;
|
||||
import org.springframework.rewrite.scopes.ScanScope;
|
||||
import org.springframework.rewrite.utils.LinuxWindowsPathUnifier;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.nio.file.Path;
|
||||
@@ -140,7 +139,8 @@ public class RewriteProjectParser {
|
||||
List<NamedStyles> styles = List.of();
|
||||
|
||||
// Get the ordered otherSourceFiles of projects
|
||||
ParserContext parserContext = mavenProjectAnalyzer.createParserContext(baseDir, resources);
|
||||
List<MavenProject> sortedProjects = mavenProjectAnalyzer.getBuildProjects(baseDir, resources);
|
||||
ParserContext parserContext = new ParserContext(baseDir, resources, sortedProjects);
|
||||
|
||||
// generate provenance
|
||||
Map<Path, List<Marker>> provenanceMarkers = provenanceMarkerFactory.generateProvenanceMarkers(baseDir,
|
||||
|
||||
@@ -46,6 +46,9 @@ public class SourceFileParser {
|
||||
|
||||
Set<SourceFile> parsedSourceFiles = new LinkedHashSet<>();
|
||||
|
||||
// we use the map to look up previous parsing results when building the classpath
|
||||
// of a module
|
||||
Map<MavenProject, ModuleParsingResult> parsingResultsMap = new HashMap<>();
|
||||
parserContext.getSortedProjects().forEach(currentMavenProject -> {
|
||||
Xml.Document moduleBuildFile = currentMavenProject.getSourceFile();
|
||||
List<Marker> markers = provenanceMarkers.get(currentMavenProject.getPomFilePath());
|
||||
@@ -53,9 +56,30 @@ public class SourceFileParser {
|
||||
LOGGER.warn("Could not find provenance markers for resource '%s'"
|
||||
.formatted(parserContext.getMatchingBuildFileResource(currentMavenProject)));
|
||||
}
|
||||
List<SourceFile> sourceFiles = moduleParser.parseModuleSourceFiles(resources, currentMavenProject,
|
||||
moduleBuildFile, markers, styles, executionContext, baseDir);
|
||||
parsedSourceFiles.addAll(sourceFiles);
|
||||
ModuleParsingResult result = moduleParser.parseModule(baseDir, resources, currentMavenProject,
|
||||
moduleBuildFile, markers, styles, executionContext, parsingResultsMap);
|
||||
|
||||
parsingResultsMap.put(currentMavenProject, result);
|
||||
|
||||
// Maybe..
|
||||
// Return ModuleParsingResult
|
||||
// mpr.getMainClasspath()
|
||||
// mpr.getTestClasspath()
|
||||
// if(currentMavenProject.dependsOn(mpr.getModule())
|
||||
// requirements:
|
||||
// - provide jars that define the classpath
|
||||
// - provide classes from (transitive) module(s)
|
||||
|
||||
// Retrieve and append the shared classpath from previously parsed modules
|
||||
Set<Path> classpath = new HashSet<>();
|
||||
Map<MavenProject, Set<Path>> modelClasspathMap = new HashMap<>();
|
||||
currentMavenProject.getDependentProjects().forEach(m -> {
|
||||
Set<Path> dependencyPaths = modelClasspathMap.get(m);
|
||||
classpath.addAll(dependencyPaths);
|
||||
});
|
||||
// TODO: provide the classpath to ModuleParser
|
||||
|
||||
parsedSourceFiles.addAll(result.sourceFiles());
|
||||
});
|
||||
|
||||
return new ArrayList<>(parsedSourceFiles);
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package org.springframework.rewrite.parsers;
|
||||
|
||||
import org.openrewrite.SourceFile;
|
||||
import org.openrewrite.java.internal.JavaTypeCache;
|
||||
import org.openrewrite.java.tree.JavaType;
|
||||
|
||||
import java.util.List;
|
||||
@@ -23,5 +24,6 @@ import java.util.List;
|
||||
/**
|
||||
* @author Fabian Krüger
|
||||
*/
|
||||
public record SourceSetParsingResult(List<SourceFile> sourceFiles, List<JavaType.FullyQualified> classpath) {
|
||||
public record SourceSetParsingResult(List<SourceFile> sourceFiles, List<JavaType.FullyQualified> classpath,
|
||||
JavaTypeCache typeCache) {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2021 - 2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://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.springframework.rewrite.parsers.maven;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.rewrite.utils.ResourceUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Fabian Krüger
|
||||
*/
|
||||
class MavenBuildFileFilter {
|
||||
|
||||
private static final String POM_XML = "pom.xml";
|
||||
|
||||
@NotNull
|
||||
static List<Resource> filterBuildFiles(List<Resource> resources) {
|
||||
return resources.stream()
|
||||
.filter(r -> ResourceUtil.getPath(r).getFileName().toString().equals(POM_XML))
|
||||
.toList();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -67,9 +67,9 @@ public class MavenModuleParser {
|
||||
this.springRewriteProperties = springRewriteProperties;
|
||||
}
|
||||
|
||||
public List<SourceFile> parseModuleSourceFiles(List<Resource> resources, MavenProject currentProject,
|
||||
public ModuleParsingResult parseModule(Path baseDir, List<Resource> resources, MavenProject currentProject,
|
||||
Xml.Document moduleBuildFile, List<Marker> provenanceMarkers, List<NamedStyles> styles,
|
||||
ExecutionContext executionContext, Path baseDir) {
|
||||
ExecutionContext executionContext, Map<MavenProject, ModuleParsingResult> parsingResultsMap) {
|
||||
|
||||
List<SourceFile> sourceFiles = new ArrayList<>();
|
||||
// 146:149: get source encoding from maven
|
||||
@@ -85,9 +85,10 @@ public class MavenModuleParser {
|
||||
.setCharset(Charset.forName(mavenSourceEncoding.toString()));
|
||||
}
|
||||
|
||||
boolean logCompilationWarningsAndErrors = springRewriteProperties.isLogCompilationWarningsAndErrors();
|
||||
JavaParser.Builder<? extends JavaParser, ?> javaParserBuilder = JavaParser.fromJavaVersion()
|
||||
.styles(styles)
|
||||
.logCompilationWarningsAndErrors(springRewriteProperties.isLogCompilationWarningsAndErrors());
|
||||
.logCompilationWarningsAndErrors(logCompilationWarningsAndErrors);
|
||||
|
||||
Path buildFilePath = currentProject.getBasedir().resolve(moduleBuildFile.getSourcePath());
|
||||
LOGGER.info("Parsing module " + buildFilePath);
|
||||
@@ -102,12 +103,12 @@ public class MavenModuleParser {
|
||||
alreadyParsed.add(moduleBuildFilePath);
|
||||
alreadyParsed.addAll(skipResourceScanDirs);
|
||||
|
||||
SourceSetParsingResult mainSourcesParsingResult = processMainSources(baseDir, resources, moduleBuildFile,
|
||||
javaParserBuilder, rp, provenanceMarkers, alreadyParsed, executionContext, currentProject);
|
||||
SourceSetParsingResult mainSourcesParsingResult = parseMainSourceSet(baseDir, currentProject, javaParserBuilder,
|
||||
parsingResultsMap, executionContext, alreadyParsed, provenanceMarkers, resources, rp);
|
||||
|
||||
SourceSetParsingResult testSourcesParsingResult = processTestSources(baseDir, moduleBuildFile,
|
||||
javaParserBuilder, rp, provenanceMarkers, alreadyParsed, executionContext, currentProject, resources,
|
||||
mainSourcesParsingResult.classpath());
|
||||
SourceSetParsingResult testSourcesParsingResult = parseTestSourceSet(baseDir, currentProject, javaParserBuilder,
|
||||
parsingResultsMap, executionContext, alreadyParsed, provenanceMarkers, resources, rp,
|
||||
mainSourcesParsingResult);
|
||||
// Collect the dirs of modules parsed in previous steps
|
||||
|
||||
// parse other project resources
|
||||
@@ -122,7 +123,199 @@ public class MavenModuleParser {
|
||||
sourceFiles.addAll(mainAndTestSources);
|
||||
sourceFiles.addAll(resourceFilesList);
|
||||
|
||||
return sourceFiles;
|
||||
ModuleParsingResult moduleParsingResult = new ModuleParsingResult(currentProject, mainSourcesParsingResult,
|
||||
testSourcesParsingResult, resourceFilesList);
|
||||
return moduleParsingResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse main source set {@code src/main} from current module. The classpath for Java
|
||||
* sources is created from jars and compilation units of dependency project previously
|
||||
* parsed. The parsed java sources are collected and provided with the result and can
|
||||
* be used by subsequent parse to build tha classpath.
|
||||
*/
|
||||
SourceSetParsingResult parseMainSourceSet(@Nullable Path baseDir, MavenProject currentProject,
|
||||
JavaParser.Builder<? extends JavaParser, ?> javaParserBuilder,
|
||||
Map<MavenProject, ModuleParsingResult> parsingResultsMap, ExecutionContext executionContext,
|
||||
Set<Path> alreadyParsed, List<Marker> provenanceMarkers, List<Resource> resources,
|
||||
RewriteResourceParser rp) {
|
||||
// collect and prepare all types for classpath and TypeCache
|
||||
// java sources in current source set
|
||||
List<Resource> javaSourcesInSrc = currentProject.getMainJavaSources();
|
||||
// jars from dependencies
|
||||
List<Path> classpathJars = currentProject.getCompileClasspathElements();
|
||||
|
||||
LOGGER.debug("Dependencies on main classpath: %s".formatted(classpathJars));
|
||||
javaParserBuilder.classpath(classpathJars);
|
||||
|
||||
// sources from other dependency modules
|
||||
List<SourceFile> sourceFilesFromOtherModules = currentProject.getDependencyProjects()
|
||||
.stream()
|
||||
// get their parsing result
|
||||
.map(project -> parsingResultsMap.get(project))
|
||||
.flatMap(result -> result.mainSourcesParsingResult().sourceFiles().stream())
|
||||
.toList();
|
||||
|
||||
String[] dependsOnSources = sourceFilesFromOtherModules.stream()
|
||||
.map(SourceFile::printAll)
|
||||
.toArray(String[]::new);
|
||||
javaParserBuilder.dependsOn(dependsOnSources);
|
||||
|
||||
JavaTypeCache typeCache = getJavaTypeCache(currentProject, parsingResultsMap, sourceFilesFromOtherModules);
|
||||
javaParserBuilder.typeCache(typeCache);
|
||||
|
||||
Set<JavaType.FullyQualified> sourceSetClassesCp = new HashSet<>();
|
||||
|
||||
// Add test sources from dependency projects to classpath
|
||||
sourceFilesFromOtherModules.stream()
|
||||
.filter(J.CompilationUnit.class::isInstance)
|
||||
.map(J.CompilationUnit.class::cast)
|
||||
.flatMap(s -> s.getClasses().stream())
|
||||
.map(J.ClassDeclaration::getType)
|
||||
.forEach(sourceSetClassesCp::add);
|
||||
|
||||
return parseSourceSet(baseDir, currentProject, javaSourcesInSrc, javaParserBuilder, sourceSetClassesCp,
|
||||
executionContext, alreadyParsed, classpathJars, typeCache, provenanceMarkers, "main", resources, rp,
|
||||
"src/main");
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse test source set {@code src/test} from current module. The classpath for Java
|
||||
* sources is created from jars and compilation units of dependency project previously
|
||||
* parsed. The parsed java sources are collected and provided with the result and can
|
||||
* be used by subsequent parse to build tha classpath.
|
||||
*/
|
||||
SourceSetParsingResult parseTestSourceSet(@Nullable Path baseDir, MavenProject currentProject,
|
||||
JavaParser.Builder<? extends JavaParser, ?> javaParserBuilder,
|
||||
Map<MavenProject, ModuleParsingResult> parsingResultsMap, ExecutionContext executionContext,
|
||||
Set<Path> alreadyParsed, List<Marker> provenanceMarkers, List<Resource> resources, RewriteResourceParser rp,
|
||||
SourceSetParsingResult mainSourcesParsingResult) {
|
||||
// collect and prepare all types for classpath and TypeCache
|
||||
// java sources in current source set
|
||||
List<Resource> javaSourcesInSrc = currentProject.getTestJavaSources();
|
||||
// jars from dependencies
|
||||
List<Path> classpathJars = currentProject.getTestClasspathElements();
|
||||
|
||||
LOGGER.debug("Dependencies on main classpath: %s".formatted(classpathJars));
|
||||
javaParserBuilder.classpath(classpathJars);
|
||||
|
||||
// sources from other dependency modules
|
||||
List<SourceFile> sourceFilesFromOtherModules = currentProject.getDependencyProjects()
|
||||
.stream()
|
||||
// get their parsing result
|
||||
.map(project -> parsingResultsMap.get(project))
|
||||
.flatMap(result -> {
|
||||
return Stream.concat(result.mainSourcesParsingResult().sourceFiles().stream(),
|
||||
result.testSourcesParsingResult().sourceFiles().stream());
|
||||
})
|
||||
.toList();
|
||||
|
||||
List<SourceFile> sourceFilesFromMain = mainSourcesParsingResult.sourceFiles();
|
||||
String[] dependsOnSources = Stream.concat(sourceFilesFromMain.stream(), sourceFilesFromOtherModules.stream())
|
||||
.map(SourceFile::printAll)
|
||||
.toArray(String[]::new);
|
||||
javaParserBuilder.dependsOn(dependsOnSources);
|
||||
|
||||
JavaTypeCache typeCache = getJavaTypeCache(currentProject, parsingResultsMap, sourceFilesFromOtherModules);
|
||||
javaParserBuilder.typeCache(typeCache);
|
||||
|
||||
Set<JavaType.FullyQualified> sourceSetClassesCp = new HashSet<>();
|
||||
|
||||
// Add test sources from dependency projects to classpath
|
||||
Stream.concat(sourceFilesFromMain.stream(), sourceFilesFromOtherModules.stream())
|
||||
.filter(J.CompilationUnit.class::isInstance)
|
||||
.map(J.CompilationUnit.class::cast)
|
||||
.flatMap(s -> s.getClasses().stream())
|
||||
.map(J.ClassDeclaration::getType)
|
||||
.forEach(sourceSetClassesCp::add);
|
||||
|
||||
return parseSourceSet(baseDir, currentProject, javaSourcesInSrc, javaParserBuilder, sourceSetClassesCp,
|
||||
executionContext, alreadyParsed, classpathJars, typeCache, provenanceMarkers, "test", resources, rp,
|
||||
"src/test");
|
||||
}
|
||||
|
||||
SourceSetParsingResult parseSourceSet(@Nullable Path baseDir, MavenProject currentProject,
|
||||
List<Resource> javaSourcesInSrc, JavaParser.Builder<? extends JavaParser, ?> javaParserBuilder,
|
||||
Set<JavaType.FullyQualified> localClassesCp, ExecutionContext executionContext, Set<Path> alreadyParsed,
|
||||
List<Path> classpathJars, JavaTypeCache typeCache, List<Marker> provenanceMarkers, String sourceSetName,
|
||||
List<Resource> resources, RewriteResourceParser rp, String sourceDir) {
|
||||
// collect source files from module src dir
|
||||
List<Resource> javaSources = new ArrayList<>();
|
||||
List<Resource> javaSourcesInTarget = currentProject.getJavaSourcesInTarget();
|
||||
javaSources.addAll(javaSourcesInTarget);
|
||||
javaSources.addAll(javaSourcesInSrc);
|
||||
|
||||
Iterable<Parser.Input> inputs = javaSources.stream().map(r -> {
|
||||
FileAttributes fileAttributes = null;
|
||||
Path path = ResourceUtil.getPath(r);
|
||||
boolean isSynthetic = Files.exists(path);
|
||||
Supplier<InputStream> inputStreamSupplier = () -> ResourceUtil.getInputStream(r);
|
||||
Parser.Input input = new Parser.Input(path, fileAttributes, inputStreamSupplier, isSynthetic);
|
||||
return input;
|
||||
}).toList();
|
||||
|
||||
// collecting parsed compilation units to the classpath (localClassesCp).
|
||||
List<? extends SourceFile> cus = javaParserBuilder.build()
|
||||
.parseInputs(inputs, baseDir, executionContext)
|
||||
.peek(s -> {
|
||||
((J.CompilationUnit) s).getClasses()
|
||||
.stream()
|
||||
.map(J.ClassDeclaration::getType)
|
||||
.forEach(localClassesCp::add);
|
||||
|
||||
alreadyParsed.add(baseDir.resolve(s.getSourcePath()));
|
||||
})
|
||||
.toList();
|
||||
|
||||
JavaSourceSet javaSourceSet = sourceSet(sourceSetName, classpathJars, typeCache);
|
||||
List<Marker> markers = new ArrayList<>(provenanceMarkers);
|
||||
|
||||
javaSourceSet = appendToClasspath(localClassesCp, javaSourceSet);
|
||||
ClasspathDependencies classpathDependencies = new ClasspathDependencies(classpathJars);
|
||||
|
||||
markers.add(javaSourceSet);
|
||||
markers.add(classpathDependencies);
|
||||
|
||||
List<Path> parsedJavaPaths = javaSourcesInTarget.stream().map(ResourceUtil::getPath).toList();
|
||||
Stream<SourceFile> parsedJavaSources = cus.stream().map(addProvenance(baseDir, markers, parsedJavaPaths));
|
||||
LOGGER.debug("[%s] Scanned %d java source files in main scope.".formatted(currentProject, javaSources.size()));
|
||||
|
||||
// Filter out any generated source files from the returned list, as we do not want
|
||||
// to apply the recipe to the
|
||||
// generated files.
|
||||
Path buildDirectory = LinuxWindowsPathUnifier.unifiedPath(Paths.get(currentProject.getBuildDirectory()));
|
||||
List<SourceFile> filteredJavaSources = filterOutResourcesInDir(parsedJavaSources, buildDirectory);
|
||||
|
||||
int sourcesParsedBefore = alreadyParsed.size();
|
||||
alreadyParsed.addAll(parsedJavaPaths);
|
||||
|
||||
List<Resource> resourcesLeft = resources.stream()
|
||||
.filter(r -> alreadyParsed.stream().noneMatch(path -> LinuxWindowsPathUnifier.pathStartsWith(r, path)))
|
||||
.toList();
|
||||
|
||||
LOGGER.info("Parsing test resources");
|
||||
Path searchDir = currentProject.getModulePath().resolve(sourceDir).resolve("resources");
|
||||
List<SourceFile> parsedResourceFiles = rp
|
||||
.parseSourceFiles(searchDir, resourcesLeft, alreadyParsed, executionContext)
|
||||
.map(addProvenance(baseDir, markers, null))
|
||||
.toList();
|
||||
|
||||
LOGGER.info("Parsed %d main resources".formatted(parsedResourceFiles.size()));
|
||||
|
||||
// TODO: Remove
|
||||
// List<SourceFile> parsedResourceFiles = rp
|
||||
// .parse(currentProject.getModulePath().resolve("src/main/resources"), resources,
|
||||
// alreadyParsed)
|
||||
// .map(addProvenance(baseDir, mainProjectProvenance, null))
|
||||
// .toList();
|
||||
|
||||
LOGGER.debug("[%s] Scanned %d resource files in main scope.".formatted(currentProject,
|
||||
(alreadyParsed.size() - sourcesParsedBefore)));
|
||||
// Any resources parsed from "main/resources" should also have the main source set
|
||||
// added to them.
|
||||
filteredJavaSources.addAll(parsedResourceFiles);
|
||||
return new SourceSetParsingResult(filteredJavaSources, javaSourceSet.getClasspath(), typeCache);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -163,134 +356,26 @@ public class MavenModuleParser {
|
||||
private Set<Path> pathsToOtherMavenProjects(MavenProject mavenProject, Path moduleBuildFile) {
|
||||
return mavenProject.getCollectedProjects()
|
||||
.stream()
|
||||
.filter(p -> !p.getFile().toPath().toString().equals(moduleBuildFile.toString()))
|
||||
.filter(p -> !LinuxWindowsPathUnifier.pathEquals(p.getBuildFile().getPath(), moduleBuildFile))
|
||||
.map(p -> p.getFile().toPath().getParent())
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse Java sources and resources under {@code src/main} of current module.
|
||||
*/
|
||||
public SourceSetParsingResult processMainSources(Path baseDir, List<Resource> resources,
|
||||
Xml.Document moduleBuildFile, JavaParser.Builder<? extends JavaParser, ?> javaParserBuilder,
|
||||
RewriteResourceParser rp, List<Marker> provenanceMarkers, Set<Path> alreadyParsed,
|
||||
ExecutionContext executionContext, MavenProject currentProject) {
|
||||
LOGGER.info("Processing main sources in module '%s'".formatted(currentProject.getProjectId()));
|
||||
// FIXME: 945
|
||||
// Some annotation processors output generated sources to the /target directory.
|
||||
// These are added for parsing but
|
||||
// should be filtered out of the final SourceFile list.
|
||||
|
||||
List<Resource> mainJavaSources = new ArrayList<>();
|
||||
List<Resource> javaSourcesInTarget = currentProject.getJavaSourcesInTarget(); // listJavaSources(resources,
|
||||
// currentProject.getBasedir().resolve(currentProject.getBuildDirectory()));
|
||||
List<Resource> javaSourcesInMain = currentProject.getMainJavaSources(); // listJavaSources(resources,
|
||||
// currentProject.getBasedir().resolve(currentProject.getSourceDirectory()));
|
||||
mainJavaSources.addAll(javaSourcesInTarget);
|
||||
mainJavaSources.addAll(javaSourcesInMain);
|
||||
|
||||
LOGGER.info("[%s] Parsing main source files".formatted(currentProject));
|
||||
|
||||
// FIXME 945 classpath
|
||||
// - Resolve dependencies to non-reactor projects from Maven repository
|
||||
// - Resolve dependencies to reactor projects by providing the sources
|
||||
// javaParserBuilder.classpath(byte[])
|
||||
|
||||
// we're processing a module here. The classpath of the module consists of all
|
||||
// declared dependencies and their transitive dependencies too.
|
||||
// For dependencies to projects that belong to the current rector...
|
||||
// They'd either need to be built with Maven before to guarantee that the jars are
|
||||
// installed to local Maven repo.
|
||||
// Or, the classpath must be created from the sources of the project.
|
||||
|
||||
List<Path> dependencies = currentProject.getCompileClasspathElements();
|
||||
|
||||
javaParserBuilder.classpath(dependencies);
|
||||
|
||||
LOGGER.info("Dependencies on main classpath: %s".formatted(dependencies));
|
||||
|
||||
JavaTypeCache typeCache = new JavaTypeCache();
|
||||
javaParserBuilder.typeCache(typeCache);
|
||||
|
||||
Iterable<Parser.Input> inputs = mainJavaSources.stream().map(r -> {
|
||||
FileAttributes fileAttributes = null;
|
||||
Path path = ResourceUtil.getPath(r);
|
||||
boolean isSynthetic = Files.exists(path);
|
||||
Supplier<InputStream> inputStreamSupplier = () -> ResourceUtil.getInputStream(r);
|
||||
Parser.Input input = new Parser.Input(path, fileAttributes, inputStreamSupplier, isSynthetic);
|
||||
return input;
|
||||
}).toList();
|
||||
|
||||
LOGGER.info("Parsing main Java sources.");
|
||||
|
||||
Set<JavaType.FullyQualified> localClassesCp = new HashSet<>();
|
||||
List<? extends SourceFile> cus = javaParserBuilder.build()
|
||||
.parseInputs(inputs, baseDir, executionContext)
|
||||
.peek(s -> {
|
||||
((J.CompilationUnit) s).getClasses()
|
||||
.stream()
|
||||
.map(J.ClassDeclaration::getType)
|
||||
.forEach(localClassesCp::add);
|
||||
|
||||
alreadyParsed.add(baseDir.resolve(s.getSourcePath()));
|
||||
})
|
||||
.toList();
|
||||
|
||||
LOGGER.info("Parsed %d main Java source files.".formatted(cus.size()));
|
||||
|
||||
// TODO: This is a hack:
|
||||
// Parsed java sources are not themselves on the classpath (here).
|
||||
// The actual parsing happens when the stream is terminated (toList),
|
||||
// therefore the toList() must be called before the parsed compilation units can
|
||||
// be added to the classpath
|
||||
JavaSourceSet javaSourceSet = sourceSet("main", dependencies, typeCache);
|
||||
List<Marker> mainProjectProvenance = new ArrayList<>(provenanceMarkers);
|
||||
javaSourceSet = appendToClasspath(localClassesCp, javaSourceSet);
|
||||
mainProjectProvenance.add(javaSourceSet);
|
||||
ClasspathDependencies classpathDependencies = new ClasspathDependencies(dependencies);
|
||||
mainProjectProvenance.add(classpathDependencies);
|
||||
|
||||
List<Path> parsedJavaPaths = javaSourcesInTarget.stream().map(ResourceUtil::getPath).toList();
|
||||
Stream<SourceFile> parsedMainJava = cus.stream()
|
||||
.map(addProvenance(baseDir, mainProjectProvenance, parsedJavaPaths));
|
||||
LOGGER.debug(
|
||||
"[%s] Scanned %d java source files in main scope.".formatted(currentProject, mainJavaSources.size()));
|
||||
|
||||
// Filter out any generated source files from the returned list, as we do not want
|
||||
// to apply the recipe to the
|
||||
// generated files.
|
||||
Path buildDirectory = LinuxWindowsPathUnifier.unifiedPath(Paths.get(currentProject.getBuildDirectory()));
|
||||
List<SourceFile> filteredMainJava = filterOutResourcesInDir(parsedMainJava, buildDirectory);
|
||||
|
||||
int sourcesParsedBefore = alreadyParsed.size();
|
||||
alreadyParsed.addAll(parsedJavaPaths);
|
||||
|
||||
List<Resource> resourcesLeft = resources.stream()
|
||||
.filter(r -> alreadyParsed.stream().noneMatch(path -> LinuxWindowsPathUnifier.pathStartsWith(r, path)))
|
||||
.toList();
|
||||
|
||||
LOGGER.info("Parsing main resources");
|
||||
List<SourceFile> parsedResourceFiles = rp
|
||||
.parseSourceFiles(currentProject.getModulePath().resolve("src/main/resources"), resourcesLeft,
|
||||
alreadyParsed, executionContext)
|
||||
.map(addProvenance(baseDir, mainProjectProvenance, null))
|
||||
.toList();
|
||||
|
||||
LOGGER.info("Parsed %d main resources".formatted(parsedResourceFiles.size()));
|
||||
|
||||
// TODO: Remove
|
||||
// List<SourceFile> parsedResourceFiles = rp
|
||||
// .parse(currentProject.getModulePath().resolve("src/main/resources"), resources,
|
||||
// alreadyParsed)
|
||||
// .map(addProvenance(baseDir, mainProjectProvenance, null))
|
||||
// .toList();
|
||||
|
||||
LOGGER.debug("[%s] Scanned %d resource files in main scope.".formatted(currentProject,
|
||||
(alreadyParsed.size() - sourcesParsedBefore)));
|
||||
// Any resources parsed from "main/resources" should also have the main source set
|
||||
// added to them.
|
||||
filteredMainJava.addAll(parsedResourceFiles);
|
||||
return new SourceSetParsingResult(filteredMainJava, javaSourceSet.getClasspath());
|
||||
private static JavaTypeCache getJavaTypeCache(MavenProject currentProject,
|
||||
Map<MavenProject, ModuleParsingResult> parsingResultsMap, List<SourceFile> sourceFilesFromOtherModules) {
|
||||
JavaTypeCache typeCache;
|
||||
if (!sourceFilesFromOtherModules.isEmpty()) {
|
||||
Optional<JavaTypeCache> optJavaTypeCache = currentProject.getDependencyProjects()
|
||||
.stream()
|
||||
.map(mp -> parsingResultsMap.get(mp).mainSourcesParsingResult().typeCache())
|
||||
.max(Comparator.comparing(JavaTypeCache::size));
|
||||
typeCache = optJavaTypeCache.orElseThrow(() -> new IllegalStateException(
|
||||
"No TypeCahche from previous build found for project " + currentProject.getProjectId()));
|
||||
}
|
||||
else {
|
||||
typeCache = new JavaTypeCache();
|
||||
}
|
||||
return typeCache;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -321,84 +406,4 @@ public class MavenModuleParser {
|
||||
return JavaSourceSet.build(name, dependencies, typeCache, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse Java sources and resource files under {@code src/test}.
|
||||
*/
|
||||
public SourceSetParsingResult processTestSources(Path baseDir, Xml.Document moduleBuildFile,
|
||||
JavaParser.Builder<? extends JavaParser, ?> javaParserBuilder, RewriteResourceParser rp,
|
||||
List<Marker> provenanceMarkers, Set<Path> alreadyParsed, ExecutionContext executionContext,
|
||||
MavenProject currentProject, List<Resource> resources, List<JavaType.FullyQualified> classpath) {
|
||||
LOGGER.info("Processing test sources in module '%s'".formatted(currentProject.getProjectId()));
|
||||
|
||||
List<Path> testDependencies = currentProject.getTestClasspathElements();
|
||||
|
||||
javaParserBuilder.classpath(testDependencies);
|
||||
JavaTypeCache typeCache = new JavaTypeCache();
|
||||
javaParserBuilder.typeCache(typeCache);
|
||||
|
||||
List<Resource> testJavaSources = currentProject.getTestJavaSources();
|
||||
// listJavaSources(resources,
|
||||
// currentProject.getBasedir().resolve(currentProject.getTestSourceDirectory()));
|
||||
// alreadyParsed.addAll(testJavaSources.stream().map(ResourceUtil::getPath).toList());
|
||||
|
||||
Iterable<Parser.Input> inputs = testJavaSources.stream()
|
||||
.map(r -> new Parser.Input(ResourceUtil.getPath(r), () -> ResourceUtil.getInputStream(r)))
|
||||
.toList();
|
||||
|
||||
final List<JavaType.FullyQualified> localClassesCp = new ArrayList<>();
|
||||
List<? extends SourceFile> cus = javaParserBuilder.build()
|
||||
.parseInputs(inputs, baseDir, executionContext)
|
||||
.peek(s -> {
|
||||
((J.CompilationUnit) s).getClasses()
|
||||
.stream()
|
||||
.map(J.ClassDeclaration::getType)
|
||||
.forEach(localClassesCp::add);
|
||||
alreadyParsed.add(baseDir.resolve(s.getSourcePath()));
|
||||
})
|
||||
.toList();
|
||||
|
||||
List<Marker> markers = new ArrayList<>(provenanceMarkers);
|
||||
|
||||
JavaSourceSet javaSourceSet = sourceSet("test", testDependencies, typeCache);
|
||||
Set<JavaType.FullyQualified> curClasspath = Stream.concat(classpath.stream(), localClassesCp.stream())
|
||||
.collect(Collectors.toSet());
|
||||
javaSourceSet = appendToClasspath(curClasspath, javaSourceSet);
|
||||
markers.add(javaSourceSet);
|
||||
Stream<SourceFile> parsedJava = cus.stream().map(addProvenance(baseDir, markers, null));
|
||||
|
||||
LOGGER.debug(
|
||||
"[%s] Scanned %d java source files in test scope.".formatted(currentProject, testJavaSources.size()));
|
||||
Stream<SourceFile> sourceFiles = parsedJava;
|
||||
|
||||
// Any resources parsed from "test/resources" should also have the test source set
|
||||
// added to them.
|
||||
int sourcesParsedBefore = alreadyParsed.size();
|
||||
Stream<SourceFile> parsedResourceFiles = rp
|
||||
.parse(currentProject.getBasedir().resolve("src/test/resources"), resources, alreadyParsed)
|
||||
.map(addProvenance(baseDir, markers, null));
|
||||
LOGGER.debug("[%s] Scanned %d resource files in test scope.".formatted(currentProject,
|
||||
(alreadyParsed.size() - sourcesParsedBefore)));
|
||||
sourceFiles = Stream.concat(sourceFiles, parsedResourceFiles);
|
||||
List<SourceFile> result = sourceFiles.toList();
|
||||
return new SourceSetParsingResult(result, javaSourceSet.getClasspath());
|
||||
}
|
||||
|
||||
// FIXME: 945 take Java sources from resources
|
||||
private static List<Resource> listJavaSources(List<Resource> resources, Path sourceDirectory) {
|
||||
return resources.stream()
|
||||
.filter(whenIn(LinuxWindowsPathUnifier.unifiedPath(sourceDirectory)))
|
||||
.filter(whenFileNameEndsWithJava())
|
||||
.toList();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Predicate<Resource> whenFileNameEndsWithJava() {
|
||||
return p -> ResourceUtil.getPath(p).getFileName().toString().endsWith(".java");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Predicate<Resource> whenIn(Path sourceDirectory) {
|
||||
return r -> ResourceUtil.getPath(r).toString().startsWith(sourceDirectory.toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,23 +15,12 @@
|
||||
*/
|
||||
package org.springframework.rewrite.parsers.maven;
|
||||
|
||||
import org.apache.maven.model.*;
|
||||
import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
|
||||
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.openrewrite.maven.utilities.MavenArtifactDownloader;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.rewrite.parsers.MavenProject;
|
||||
import org.springframework.rewrite.parsers.ParserContext;
|
||||
import org.springframework.rewrite.utils.LinuxWindowsPathUnifier;
|
||||
import org.springframework.rewrite.utils.ResourceUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Implements the ordering of Maven (reactor) build projects. See <a href=
|
||||
@@ -42,393 +31,37 @@ import java.util.stream.Collectors;
|
||||
*/
|
||||
public class MavenProjectAnalyzer {
|
||||
|
||||
private static final String POM_XML = "pom.xml";
|
||||
private final MavenProjectSorter mavenProjectSorter;
|
||||
|
||||
private static final MavenXpp3Reader XPP_3_READER = new MavenXpp3Reader();
|
||||
private final MavenProjectFactory mavenProjectFactory;
|
||||
|
||||
private final MavenArtifactDownloader rewriteMavenArtifactDownloader;
|
||||
|
||||
public MavenProjectAnalyzer(MavenArtifactDownloader rewriteMavenArtifactDownloader) {
|
||||
this.rewriteMavenArtifactDownloader = rewriteMavenArtifactDownloader;
|
||||
public MavenProjectAnalyzer(MavenProjectSorter mavenProjectSorter, MavenProjectFactory mavenProjectFactory) {
|
||||
this.mavenProjectSorter = mavenProjectSorter;
|
||||
this.mavenProjectFactory = mavenProjectFactory;
|
||||
}
|
||||
|
||||
public List<MavenProject> getSortedProjects(Path baseDir, List<Resource> resources) {
|
||||
|
||||
List<Resource> allPomFiles = resources.stream()
|
||||
.filter(r -> ResourceUtil.getPath(r).getFileName().toString().equals(POM_XML))
|
||||
.toList();
|
||||
|
||||
if (allPomFiles.isEmpty()) {
|
||||
throw new IllegalArgumentException("The provided resources did not contain any 'pom.xml' file.");
|
||||
}
|
||||
|
||||
Resource rootPom = allPomFiles.stream()
|
||||
.filter(r -> LinuxWindowsPathUnifier.pathEquals(r, baseDir.resolve(POM_XML)))
|
||||
.findFirst()
|
||||
.orElseThrow(
|
||||
() -> new IllegalArgumentException("The provided resources do not contain a root 'pom.xml' file."));
|
||||
Model rootPomModel = new Model(rootPom);
|
||||
|
||||
if (isSingleModuleProject(rootPomModel)) {
|
||||
return List.of(new MavenProject(baseDir, rootPom, rootPomModel, rewriteMavenArtifactDownloader, resources));
|
||||
}
|
||||
List<Model> reactorModels = new ArrayList<>();
|
||||
recursivelyFindReactorModules(baseDir, null, reactorModels, allPomFiles, rootPomModel);
|
||||
List<Model> sortedModels = sortModels(reactorModels);
|
||||
return map(baseDir, resources, sortedModels);
|
||||
public List<MavenProject> getBuildProjects(Path baseDir, List<Resource> resources) {
|
||||
List<MavenProject> allMavenProjects = mavenProjectFactory.create(baseDir, resources);
|
||||
List<MavenProject> mavenProjects = mavenProjectSorter.sort(baseDir, allMavenProjects);
|
||||
return map(baseDir, resources, mavenProjects);
|
||||
}
|
||||
|
||||
private List<MavenProject> map(Path baseDir, List<Resource> resources, List<Model> sortedModels) {
|
||||
private List<MavenProject> map(Path baseDir, List<Resource> resources, List<MavenProject> sortedModels) {
|
||||
|
||||
List<MavenProject> mavenProjects = new ArrayList<>();
|
||||
sortedModels.stream().filter(Objects::nonNull).forEach(m -> {
|
||||
sortedModels.stream().filter(Objects::nonNull).forEach(mavenProject -> {
|
||||
String projectDir = LinuxWindowsPathUnifier
|
||||
.unifiedPathString(baseDir.resolve(m.getProjectDirectory().toPath()).normalize());
|
||||
.unifiedPathString(baseDir.resolve(mavenProject.getModuleDir()).normalize());
|
||||
List<Resource> filteredResources = resources.stream()
|
||||
.filter(r -> LinuxWindowsPathUnifier.unifiedPathString(r).startsWith(projectDir))
|
||||
.toList();
|
||||
MavenProject mavenProject = new MavenProject(baseDir, m.getResource(), m, rewriteMavenArtifactDownloader,
|
||||
filteredResources);
|
||||
mavenProjects.add(mavenProject);
|
||||
});
|
||||
// set all non parent poms as collected projects for root parent pom
|
||||
// set all non parent poms as collected projects for root parent p
|
||||
List<MavenProject> collected = new ArrayList<>(mavenProjects);
|
||||
collected.remove(0);
|
||||
mavenProjects.get(0).setCollectedProjects(collected);
|
||||
mavenProjects.get(0).setReactorProjects(collected);
|
||||
return mavenProjects;
|
||||
}
|
||||
|
||||
private List<Model> recursivelyFindReactorModules(Path baseDir, String path, List<Model> reactorModels,
|
||||
List<Resource> allPomFiles, Model pomModel) {
|
||||
// TODO: verify given module is root pom
|
||||
if (pomModel != null) {
|
||||
reactorModels.add(pomModel);
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("PomModel was null.");
|
||||
}
|
||||
|
||||
List<String> moduleNames = pomModel.getModules();
|
||||
|
||||
moduleNames.stream().forEach(moduleName -> {
|
||||
|
||||
String modulePathSegment = path == null ? moduleName : path + "/" + moduleName;
|
||||
|
||||
allPomFiles.stream()
|
||||
.filter(getResourcePredicate(baseDir, modulePathSegment))
|
||||
.map(Model::new)
|
||||
.forEach(m -> recursivelyFindReactorModules(baseDir, modulePathSegment, reactorModels, allPomFiles, m)
|
||||
.stream());
|
||||
});
|
||||
return reactorModels;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Predicate<Resource> getResourcePredicate(Path baseDir, String modulePathSegment) {
|
||||
return resource -> {
|
||||
Path pomPath = baseDir.resolve(modulePathSegment).resolve(POM_XML).toAbsolutePath().normalize();
|
||||
String modulePath = LinuxWindowsPathUnifier.unifiedPathString(pomPath);
|
||||
Path resourcePath = ResourceUtil.getPath(resource).toAbsolutePath().normalize();
|
||||
return LinuxWindowsPathUnifier.pathEquals(resourcePath, modulePath);
|
||||
};
|
||||
}
|
||||
|
||||
public List<Model> sortModels(List<Model> reactorModels) {
|
||||
List<Model> sortedModels = new ArrayList<>();
|
||||
Map<String, Model> gaToModelMap = reactorModels.stream().collect(Collectors.toMap(m -> {
|
||||
return (m.getGroupId() == null ? (m.getParent() == null ? null : m.getParent().getGroupId())
|
||||
: m.getGroupId()) + ":" + m.getArtifactId();
|
||||
}, m -> m));
|
||||
|
||||
Map<Model, List<Model>> dependsOn = new HashMap<>();
|
||||
|
||||
for (Model m : reactorModels) {
|
||||
addToDependants(m, dependsOn, null);
|
||||
if (hasParent(m)) {
|
||||
String parentGa = m.getParent().getGroupId() + ":" + m.getParent().getArtifactId();
|
||||
Model parentModel = gaToModelMap.get(parentGa);
|
||||
addToDependants(m, dependsOn, parentModel);
|
||||
}
|
||||
for (Dependency d : m.getDependencies()) {
|
||||
String dependencyGa = getGav(d);
|
||||
if (gaToModelMap.containsKey(dependencyGa)) {
|
||||
Model dependencyModel = gaToModelMap.get(dependencyGa);
|
||||
addToDependants(m, dependsOn, dependencyModel);
|
||||
}
|
||||
}
|
||||
if (m.getBuild() != null && m.getBuild().getPluginsAsMap() != null
|
||||
&& !m.getBuild().getPluginsAsMap().isEmpty()) {
|
||||
for (String pluginName : m.getBuild().getPluginsAsMap().keySet()) {
|
||||
// TODO: find plugin dependencies
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ArrayList<Map.Entry<Model, List<Model>>> entries = new ArrayList<>(dependsOn.entrySet());
|
||||
|
||||
// sort entries by number of values
|
||||
entries.stream().sorted((e1, e2) -> {
|
||||
int compare = Integer.compare(e1.getValue().size(), e2.getValue().size());
|
||||
if (compare != 0) {
|
||||
return compare;
|
||||
}
|
||||
else {
|
||||
// with same number of dependencies order by dependant
|
||||
if (e1.getValue().contains(e2.getKey())) {
|
||||
return 1;
|
||||
}
|
||||
else if (e2.getValue().contains(e1.getKey())) {
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
// default is order as given by reactorModels
|
||||
return Integer.compare(reactorModels.indexOf(e1.getKey()), reactorModels.indexOf(e2.getKey()));
|
||||
}
|
||||
}
|
||||
}).forEach(e -> sortedModels.add(e.getKey()));
|
||||
return sortedModels;
|
||||
}
|
||||
|
||||
private void addToDependants(Model model, Map<Model, List<Model>> dependsOn, Model dependantModel) {
|
||||
if (!dependsOn.containsKey(model)) {
|
||||
dependsOn.put(model, new ArrayList<>());
|
||||
}
|
||||
if (dependantModel != null) {
|
||||
dependsOn.get(model).add(dependantModel);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasParent(Model m) {
|
||||
return m.getParent() != null;
|
||||
}
|
||||
|
||||
private String getGav(Dependency d) {
|
||||
return d.getGroupId() + ":" + d.getArtifactId();
|
||||
}
|
||||
|
||||
private static boolean isSingleModuleProject(Model rootPomModel) {
|
||||
return !rootPomModel.getPackaging().equals("pom");
|
||||
}
|
||||
|
||||
public ParserContext createParserContext(Path baseDir, List<Resource> resources) {
|
||||
List<MavenProject> sortedProjectsList = getSortedProjects(baseDir, resources);
|
||||
ParserContext parserContext = new ParserContext(baseDir, resources, sortedProjectsList);
|
||||
return parserContext;
|
||||
}
|
||||
|
||||
static class Model extends org.apache.maven.model.Model {
|
||||
|
||||
private final Resource resource;
|
||||
|
||||
private final org.apache.maven.model.Model delegate;
|
||||
|
||||
public Model(Resource resource) {
|
||||
this.resource = resource;
|
||||
try {
|
||||
this.delegate = XPP_3_READER.read(ResourceUtil.getInputStream(resource));
|
||||
this.delegate.setPomFile(resource.getFile());
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
catch (XmlPullParserException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return (delegate.getGroupId() == null ? delegate.getParent().getGroupId() : delegate.getGroupId()) + ":"
|
||||
+ delegate.getArtifactId();
|
||||
}
|
||||
|
||||
public Resource getResource() {
|
||||
return resource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getArtifactId() {
|
||||
return delegate.getArtifactId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Build getBuild() {
|
||||
return delegate.getBuild();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getChildProjectUrlInheritAppendPath() {
|
||||
return delegate.getChildProjectUrlInheritAppendPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CiManagement getCiManagement() {
|
||||
return delegate.getCiManagement();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Contributor> getContributors() {
|
||||
return delegate.getContributors();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return delegate.getDescription();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Developer> getDevelopers() {
|
||||
return delegate.getDevelopers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getGroupId() {
|
||||
return delegate.getGroupId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getInceptionYear() {
|
||||
return delegate.getInceptionYear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IssueManagement getIssueManagement() {
|
||||
return delegate.getIssueManagement();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<License> getLicenses() {
|
||||
return delegate.getLicenses();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MailingList> getMailingLists() {
|
||||
return delegate.getMailingLists();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getModelEncoding() {
|
||||
return delegate.getModelEncoding();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getModelVersion() {
|
||||
return delegate.getModelVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
String name = delegate.getName();
|
||||
if (name == null) {
|
||||
name = delegate.getArtifactId();
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Organization getOrganization() {
|
||||
return delegate.getOrganization();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPackaging() {
|
||||
return delegate.getPackaging();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Parent getParent() {
|
||||
return delegate.getParent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Prerequisites getPrerequisites() {
|
||||
return delegate.getPrerequisites();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Profile> getProfiles() {
|
||||
return delegate.getProfiles();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Scm getScm() {
|
||||
return delegate.getScm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUrl() {
|
||||
return delegate.getUrl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return delegate.getVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getPomFile() {
|
||||
return delegate.getPomFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getProjectDirectory() {
|
||||
return delegate.getPomFile().toPath().getParent().toFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return delegate.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Dependency> getDependencies() {
|
||||
return delegate.getDependencies();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DependencyManagement getDependencyManagement() {
|
||||
return delegate.getDependencyManagement();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DistributionManagement getDistributionManagement() {
|
||||
return delegate.getDistributionManagement();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputLocation getLocation(Object key) {
|
||||
return delegate.getLocation(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getModules() {
|
||||
return delegate.getModules();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Repository> getPluginRepositories() {
|
||||
return delegate.getPluginRepositories();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Properties getProperties() {
|
||||
return delegate.getProperties();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Reporting getReporting() {
|
||||
return delegate.getReporting();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getReports() {
|
||||
return delegate.getReports();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Repository> getRepositories() {
|
||||
return delegate.getRepositories();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2021 - 2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://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.springframework.rewrite.parsers.maven;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.openrewrite.maven.utilities.MavenArtifactDownloader;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.rewrite.parsers.MavenProject;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Fabian Krüger
|
||||
*/
|
||||
public class MavenProjectFactory {
|
||||
|
||||
private final MavenArtifactDownloader artifactDownloader;
|
||||
|
||||
public MavenProjectFactory(MavenArtifactDownloader artifactDownloader) {
|
||||
this.artifactDownloader = artifactDownloader;
|
||||
}
|
||||
|
||||
public List<MavenProject> create(Path baseDir, List<Resource> projectResources) {
|
||||
List<Resource> allPomFiles = MavenBuildFileFilter.filterBuildFiles(projectResources);
|
||||
if (allPomFiles.isEmpty()) {
|
||||
throw new IllegalArgumentException("The provided resources did not contain any 'pom.xml' file.");
|
||||
}
|
||||
return allPomFiles.stream().map(pf -> create(baseDir, pf, projectResources)).toList();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public MavenProject create(Path baseDir, Resource pomFile, List<Resource> projectResources) {
|
||||
return new MavenProject(baseDir, pomFile, artifactDownloader, projectResources);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright 2021 - 2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://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.springframework.rewrite.parsers.maven;
|
||||
|
||||
import org.springframework.rewrite.parsers.MavenProject;
|
||||
import org.springframework.rewrite.parsers.ProjectId;
|
||||
import org.springframework.rewrite.utils.LinuxWindowsPathUnifier;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Create directed acyclic graph from MavenProjects.
|
||||
*
|
||||
* @author Fabian Krüger
|
||||
*/
|
||||
public class MavenProjectGraph {
|
||||
|
||||
private static final String POM_XML = "pom.xml";
|
||||
|
||||
private Map<ProjectId, MavenProject> gaToMavenProjectMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Create a Maven module dependency graph starting from pom.xml in {@code baseDir}.
|
||||
* The dependency and dependants for all {@MavenModule}s are added when they are part
|
||||
* of the build.
|
||||
*/
|
||||
public Map<MavenProject, Set<MavenProject>> from(Path baseDir, List<MavenProject> allMavenProjects) {
|
||||
// build map of projectId -> MavenProject
|
||||
// this is used to decide if dependencies come from the project
|
||||
initGaToMavenProjectMap(allMavenProjects);
|
||||
MavenProject rootProject = findRootProject(baseDir, allMavenProjects);
|
||||
// the dag with child/parent dependencies
|
||||
Map<MavenProject, Set<MavenProject>> dag = new HashMap<>();
|
||||
buildDependencyGraph(rootProject, allMavenProjects, dag);
|
||||
enrichGraphWithDependencies(dag);
|
||||
return dag;
|
||||
}
|
||||
|
||||
private void enrichGraphWithDependencies(Map<MavenProject, Set<MavenProject>> dag) {
|
||||
dag.keySet().stream().forEach(curProject -> {
|
||||
List<MavenProject> dependencyProjects = new ArrayList<>();
|
||||
curProject.getBuildFile().getDependencies().forEach(d -> {
|
||||
ProjectId projectId = new ProjectId(d.getGroupId(), d.getArtifactId());
|
||||
if (gaToMavenProjectMap.containsKey(projectId)) {
|
||||
MavenProject dependencyMavenProject = gaToMavenProjectMap.get(projectId);
|
||||
dependencyProjects.add(dependencyMavenProject);
|
||||
}
|
||||
});
|
||||
curProject.setDependencyProjects(dependencyProjects);
|
||||
});
|
||||
|
||||
dag.keySet().stream().forEach(curProject -> {
|
||||
Set<MavenProject> dependentProjects = dag.get(curProject);
|
||||
curProject.getBuildFile().getDependencies().stream().forEach(dependency -> {
|
||||
ProjectId projectId = new ProjectId(dependency.getGroupId(), dependency.getArtifactId());
|
||||
if (gaToMavenProjectMap.containsKey(projectId)) {
|
||||
MavenProject dependantProject = gaToMavenProjectMap.get(projectId);
|
||||
if (dag.containsKey(dependantProject)) {
|
||||
dependentProjects.add(dependantProject);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private void initGaToMavenProjectMap(List<MavenProject> allPomFiles) {
|
||||
gaToMavenProjectMap = allPomFiles.stream()
|
||||
.collect(Collectors.toMap(mp -> new ProjectId(mp.getGroupId(), mp.getArtifactId()), mp -> mp));
|
||||
}
|
||||
|
||||
private void buildDependencyGraph(MavenProject currentProject, List<MavenProject> reactorProjects,
|
||||
Map<MavenProject, Set<MavenProject>> dag) {
|
||||
if (isSingleModuleProject(reactorProjects)) {
|
||||
logDependentProject(currentProject, dag, null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isMultiModuleProject(currentProject)) {
|
||||
currentProject.getBuildFile().getModules().stream().map(moduleName -> {
|
||||
Path modulePath = currentProject.getModulePath().resolve(moduleName).normalize();
|
||||
MavenProject mavenProject = reactorProjects.stream()
|
||||
.filter(p -> LinuxWindowsPathUnifier.pathEquals(p.getModulePath(), modulePath))
|
||||
.findFirst()
|
||||
.get();
|
||||
return mavenProject;
|
||||
}).forEach(childProject -> {
|
||||
// add dependent project
|
||||
logDependentProject(childProject, dag, currentProject);
|
||||
buildDependencyGraph(childProject, reactorProjects, dag);
|
||||
});
|
||||
}
|
||||
dag.computeIfAbsent(currentProject, __ -> new HashSet<>());
|
||||
}
|
||||
|
||||
private static boolean isMultiModuleProject(MavenProject currentProject) {
|
||||
return hasPomPackaging(currentProject);
|
||||
}
|
||||
|
||||
private static boolean isSingleModuleProject(List<MavenProject> reactorProjects) {
|
||||
return reactorProjects.size() == 1;
|
||||
}
|
||||
|
||||
private static void logDependentProject(MavenProject dependingProject, Map<MavenProject, Set<MavenProject>> dag,
|
||||
MavenProject dependantProject) {
|
||||
Set<MavenProject> mavenProjects = dag.computeIfAbsent(dependingProject, __ -> new HashSet<>());
|
||||
if (dependantProject != null) {
|
||||
mavenProjects.add(dependantProject);
|
||||
}
|
||||
}
|
||||
|
||||
private MavenProject findRootProject(Path baseDir, List<MavenProject> reactorProjects) {
|
||||
return reactorProjects.stream()
|
||||
.filter(p -> LinuxWindowsPathUnifier.pathEquals(p.getBuildFile().getPath(), baseDir.resolve(POM_XML)))
|
||||
.findFirst()
|
||||
.get();
|
||||
}
|
||||
|
||||
private static boolean hasPomPackaging(MavenProject curMavenProject) {
|
||||
return "pom".equals(curMavenProject.getBuildFile().getPackaging());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2021 - 2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://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.springframework.rewrite.parsers.maven;
|
||||
|
||||
import org.springframework.rewrite.parsers.MavenProject;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author Fabian Krüger
|
||||
*/
|
||||
public class MavenProjectSorter {
|
||||
|
||||
private static final String POM_XML = "pom.xml";
|
||||
|
||||
private final MavenProjectGraph mavenProjectGraph;
|
||||
|
||||
public MavenProjectSorter(MavenProjectGraph mavenProjectGraph) {
|
||||
this.mavenProjectGraph = mavenProjectGraph;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter {@link MavenProject}s that belong to a reactor build. Create ADG of the
|
||||
* relevant projects and their dependencies to other {@link MavenProject}s. Sort the
|
||||
* ADG and return the ordered list of {@link MavenProject}s.
|
||||
*/
|
||||
public List<MavenProject> sort(Path baseDir, List<MavenProject> allPomFiles) {
|
||||
Map<MavenProject, Set<MavenProject>> graph = mavenProjectGraph.from(baseDir, allPomFiles);
|
||||
List<MavenProject> buildOrder = new ArrayList<>();
|
||||
|
||||
Map<MavenProject, Set<MavenProject>> dependingProjects = new HashMap<>();
|
||||
graph.keySet().forEach(mavenProject -> {
|
||||
graph.entrySet()
|
||||
.stream()
|
||||
.peek(e -> dependingProjects.computeIfAbsent(mavenProject, __ -> new HashSet<>()))
|
||||
.filter(e -> e.getValue().contains(mavenProject))
|
||||
.forEach(e -> {
|
||||
dependingProjects.get(mavenProject).add(e.getKey());
|
||||
});
|
||||
});
|
||||
|
||||
Map<MavenProject, Integer> inDegree = dependingProjects.keySet()
|
||||
.stream()
|
||||
.collect(Collectors.toMap(k -> k, __ -> 0));
|
||||
dependingProjects.entrySet()
|
||||
.stream()
|
||||
.flatMap(e -> e.getValue().stream())
|
||||
.forEach(d -> inDegree.put(d, inDegree.get(d) + 1));
|
||||
|
||||
Queue<MavenProject> sources = new PriorityQueue<>(Comparator.comparing(p -> p.getBuildFile().getArtifactId()));
|
||||
for (Map.Entry<MavenProject, Integer> entry : inDegree.entrySet()) {
|
||||
if (entry.getValue() == 0) {
|
||||
sources.add(entry.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
while (!sources.isEmpty()) {
|
||||
MavenProject project = sources.poll();
|
||||
buildOrder.add(project);
|
||||
List<MavenProject> mavenProjects1 = new ArrayList<>(dependingProjects.get(project));
|
||||
for (MavenProject child : mavenProjects1) {
|
||||
inDegree.put(child, inDegree.get(child) - 1);
|
||||
if (inDegree.get(child) == 0) {
|
||||
sources.add(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (buildOrder.size() != inDegree.size()) {
|
||||
throw new RuntimeException("Cycle detected Maven projects");
|
||||
}
|
||||
|
||||
return buildOrder;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
/*
|
||||
* Copyright 2021 - 2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://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.openrewrite;
|
||||
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openrewrite.java.JavaParser;
|
||||
import org.openrewrite.java.internal.JavaTypeCache;
|
||||
import org.openrewrite.java.internal.TypesInUse;
|
||||
import org.openrewrite.java.marker.JavaSourceSet;
|
||||
import org.openrewrite.java.tree.J;
|
||||
import org.openrewrite.java.tree.JavaType;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Fabian Krüger
|
||||
*/
|
||||
public class JavaParserTest {
|
||||
|
||||
@Test
|
||||
@DisplayName("shouldHaveTypeInUse")
|
||||
@Disabled("Examination the JavaParser")
|
||||
void shouldHaveTypeInUse() {
|
||||
|
||||
String localM2Repo = Path.of(System.getProperty("user.home")).resolve(".m2/repository").toString();
|
||||
List<Path> classpath = List.of(
|
||||
Path.of(localM2Repo
|
||||
+ "/org/springframework/boot/spring-boot-starter/3.1.1/spring-boot-starter-3.1.1.jar"),
|
||||
Path.of(localM2Repo + "/org/springframework/boot/spring-boot/3.1.1/spring-boot-3.1.1.jar"),
|
||||
Path.of(localM2Repo + "/org/springframework/spring-context/6.0.10/spring-context-6.0.10.jar"),
|
||||
Path.of(localM2Repo + "/org/springframework/spring-aop/6.0.10/spring-aop-6.0.10.jar"),
|
||||
Path.of(localM2Repo + "/org/springframework/spring-beans/6.0.10/spring-beans-6.0.10.jar"),
|
||||
Path.of(localM2Repo + "/org/springframework/spring-expression/6.0.10/spring-expression-6.0.10.jar"),
|
||||
Path.of(localM2Repo
|
||||
+ "/org/springframework/boot/spring-boot-autoconfigure/3.1.1/spring-boot-autoconfigure-3.1.1.jar"),
|
||||
Path.of(localM2Repo
|
||||
+ "/org/springframework/boot/spring-boot-starter-logging/3.1.1/spring-boot-starter-logging-3.1.1.jar"),
|
||||
Path.of(localM2Repo + "/ch/qos/logback/logback-classic/1.4.8/logback-classic-1.4.8.jar"),
|
||||
Path.of(localM2Repo + "/ch/qos/logback/logback-core/1.4.8/logback-core-1.4.8.jar"),
|
||||
Path.of(localM2Repo + "/org/slf4j/slf4j-api/2.0.7/slf4j-api-2.0.7.jar"),
|
||||
Path.of(localM2Repo + "/org/apache/logging/log4j/log4j-to-slf4j/2.20.0/log4j-to-slf4j-2.20.0.jar"),
|
||||
Path.of(localM2Repo + "/org/apache/logging/log4j/log4j-api/2.20.0/log4j-api-2.20.0.jar"),
|
||||
Path.of(localM2Repo + "/org/slf4j/jul-to-slf4j/2.0.7/jul-to-slf4j-2.0.7.jar"),
|
||||
Path.of(localM2Repo
|
||||
+ "/jakarta/annotation/jakarta.annotation-api/2.1.1/jakarta.annotation-api-2.1.1.jar"),
|
||||
Path.of(localM2Repo + "/org/springframework/spring-core/6.0.10/spring-core-6.0.10.jar"),
|
||||
Path.of(localM2Repo + "/org/springframework/spring-jcl/6.0.10/spring-jcl-6.0.10.jar"),
|
||||
Path.of(localM2Repo + "/org/yaml/snakeyaml/1.33/snakeyaml-1.33.jar"));
|
||||
JavaTypeCache javaTypeCache = new JavaTypeCache();
|
||||
SourceFile sourceFile = JavaParser.fromJavaVersion()
|
||||
.classpath(classpath)
|
||||
.typeCache(javaTypeCache)
|
||||
.build()
|
||||
.parse("""
|
||||
package com.example;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class MyMain {
|
||||
public static void main(String[] args){
|
||||
SpringApplication.run(MyMain.class, args);
|
||||
}
|
||||
}
|
||||
""")
|
||||
.toList()
|
||||
.get(0);
|
||||
|
||||
J.CompilationUnit compilationUnit = (J.CompilationUnit) sourceFile;
|
||||
List<String> typesInUse = compilationUnit.getTypesInUse()
|
||||
.getTypesInUse()
|
||||
.stream()
|
||||
.map(s -> s.toString())
|
||||
.toList();
|
||||
assertThat(typesInUse).contains("org.springframework.boot.SpringApplication",
|
||||
"org.springframework.boot.SpringApplication", "com.example.MyMain");
|
||||
JavaSourceSet main = JavaSourceSet.build("main", classpath, javaTypeCache, true);
|
||||
List<String> typesOnClasspath = main.getClasspath()
|
||||
.stream()
|
||||
.map(JavaType.FullyQualified::getFullyQualifiedName)
|
||||
.toList();
|
||||
assertThat(typesOnClasspath).doesNotContain("com.example.MyMain"); // By design
|
||||
|
||||
javaTypeCache.put("com.example.MyMain", sourceFile);
|
||||
|
||||
main = JavaSourceSet.build("main", classpath, javaTypeCache, true);
|
||||
List<JavaType.FullyQualified> mainCp = main.getClasspath();
|
||||
TypesInUse typesInUseBefore = compilationUnit.getTypesInUse();
|
||||
|
||||
typesInUse = typesInUseBefore.getTypesInUse().stream().map(s -> s.toString()).toList();
|
||||
assertThat(typesInUse).contains("org.springframework.boot.SpringApplication",
|
||||
"org.springframework.boot.SpringApplication", "com.example.MyMain");
|
||||
compilationUnit.getClasses().stream().map(c -> c.getType()).forEach(mainCp::add);
|
||||
|
||||
main = main.withClasspath(mainCp);
|
||||
assertThat(main.getClasspath().stream().map(JavaType.FullyQualified::getFullyQualifiedName).toList())
|
||||
.contains("org.springframework.boot.SpringApplication");
|
||||
assertThat(main.getClasspath().stream().map(JavaType.FullyQualified::getFullyQualifiedName).toList())
|
||||
.contains("com.example.MyMain");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -101,7 +101,7 @@ public class CalculateClasspathTest {
|
||||
}
|
||||
""";
|
||||
|
||||
Path baseDir = LinuxWindowsPathUnifier.unifiedPath(tmpDir.resolve("/example-1").toAbsolutePath().normalize());
|
||||
Path baseDir = LinuxWindowsPathUnifier.unifiedPath(tmpDir.resolve("example-1").toAbsolutePath().normalize());
|
||||
List<Resource> resources = List.of(new DummyResource(baseDir.resolve("pom.xml"), pom),
|
||||
new DummyResource(baseDir.resolve("src/main/java/com/example/MainClass.java"), mainClass),
|
||||
new DummyResource(baseDir.resolve("src/test/java/com/example/TestClass.java"), testClass));
|
||||
|
||||
@@ -25,7 +25,6 @@ import org.openrewrite.Parser;
|
||||
import org.openrewrite.SourceFile;
|
||||
import org.openrewrite.tree.ParsingEventListener;
|
||||
import org.openrewrite.tree.ParsingExecutionContextView;
|
||||
import org.sonatype.plexus.components.cipher.PlexusCipherException;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
@@ -88,7 +87,7 @@ class RewriteProjectParserTest {
|
||||
|
||||
@Test
|
||||
@DisplayName("Parse simple Maven project")
|
||||
void parseSimpleMavenProject(@TempDir Path tempDir) throws PlexusCipherException {
|
||||
void parseSimpleMavenProject(@TempDir Path tempDir) {
|
||||
Path basePath = tempDir;
|
||||
SpringRewriteProperties springRewriteProperties = new SpringRewriteProperties();
|
||||
ExecutionContext executionContext = new InMemoryExecutionContext(t -> {
|
||||
@@ -98,13 +97,16 @@ class RewriteProjectParserTest {
|
||||
ProjectMetadata projectMetadata = new ProjectMetadata();
|
||||
MavenSettingsInitializer mavenSettingsInitializer = new MavenSettingsInitializer(executionContext,
|
||||
projectMetadata);
|
||||
RewriteMavenArtifactDownloader artifactDownloader = mock(RewriteMavenArtifactDownloader.class);
|
||||
MavenProjectGraph projectCollector = new MavenProjectGraph();
|
||||
MavenProjectFactory mavenProjectFactory = new MavenProjectFactory(artifactDownloader);
|
||||
RewriteProjectParser projectParser = new RewriteProjectParser(
|
||||
new ProvenanceMarkerFactory(new MavenProvenanceMarkerFactory()),
|
||||
new BuildFileParser(mavenSettingsInitializer), new SourceFileParser(mavenModuleParser),
|
||||
new StyleDetector(), springRewriteProperties, mock(ParsingEventListener.class),
|
||||
mock(ApplicationEventPublisher.class), new ScanScope(), mock(ConfigurableListableBeanFactory.class),
|
||||
new ProjectScanner(new DefaultResourceLoader(), springRewriteProperties), executionContext,
|
||||
new MavenProjectAnalyzer(mock(RewriteMavenArtifactDownloader.class)));
|
||||
new MavenProjectAnalyzer(new MavenProjectSorter(projectCollector), mavenProjectFactory));
|
||||
|
||||
List<String> parsedFiles = new ArrayList<>();
|
||||
ParsingExecutionContextView.view(executionContext).setParsingListener(new ParsingEventListener() {
|
||||
|
||||
@@ -0,0 +1,346 @@
|
||||
/*
|
||||
* Copyright 2021 - 2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://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.springframework.rewrite.parsers.maven;
|
||||
|
||||
import com.google.common.base.Supplier;
|
||||
import org.assertj.core.api.AbstractAssert;
|
||||
import org.assertj.core.api.SoftAssertions;
|
||||
import org.intellij.lang.annotations.Language;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.function.Executable;
|
||||
import org.openrewrite.maven.cache.LocalMavenArtifactCache;
|
||||
import org.openrewrite.maven.utilities.MavenArtifactDownloader;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.rewrite.parsers.MavenProject;
|
||||
import org.springframework.rewrite.test.util.DummyResource;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.rewrite.parsers.maven.MavenProjectGraphTest.MavenProjectBuilder.none;
|
||||
|
||||
/**
|
||||
* @author Fabian Krüger
|
||||
*/
|
||||
class MavenProjectGraphTest {
|
||||
|
||||
private MavenProjectGraph sut = new MavenProjectGraph();
|
||||
|
||||
private static MavenArtifactDownloader artifactDownloader = new RewriteMavenArtifactDownloader(
|
||||
new LocalMavenArtifactCache(Path.of(System.getProperty("user.home")).resolve(".m2/repository")), null,
|
||||
e -> {
|
||||
throw new RuntimeException(e);
|
||||
});
|
||||
|
||||
private static MavenProjectFactory projectFactory = new MavenProjectFactory(artifactDownloader);
|
||||
|
||||
@Test
|
||||
@DisplayName("single module")
|
||||
void singleModule() {
|
||||
@Language("xml")
|
||||
String pomCode = """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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>
|
||||
<groupId>com.acme</groupId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
<artifactId>example</artifactId>
|
||||
</project>
|
||||
""";
|
||||
Path baseDir = Path.of("./target").toAbsolutePath().normalize();
|
||||
Resource pomResource = new DummyResource(baseDir.resolve("pom.xml"), pomCode);
|
||||
MavenProject mavenProject = projectFactory.create(baseDir, pomResource, List.of(pomResource));
|
||||
List<MavenProject> allMavenProjects = List.of(mavenProject);
|
||||
|
||||
Map<MavenProject, Set<MavenProject>> mavenProjectSetMap = sut.from(baseDir, allMavenProjects);
|
||||
|
||||
assertThat(mavenProjectSetMap).hasSize(1);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Dangling pom should will be collected")
|
||||
void danglingPomShouldBeIgnored() {
|
||||
|
||||
@Language("xml")
|
||||
String parentPom = """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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>
|
||||
<groupId>com.acme</groupId>
|
||||
<artifactId>parent</artifactId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
<modules>
|
||||
<module>example</module>
|
||||
</modules>
|
||||
</project>
|
||||
""";
|
||||
|
||||
@Language("xml")
|
||||
String modulePom = """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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>com.acme</groupId>
|
||||
<artifactId>parent</artifactId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>example</artifactId>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.acme</groupId>
|
||||
<artifactId>dangling</artifactId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
""";
|
||||
|
||||
@Language("xml")
|
||||
String danglingPom = """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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>
|
||||
<groupId>com.acme</groupId>
|
||||
<artifactId>dangling</artifactId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
</project>
|
||||
""";
|
||||
|
||||
Path baseDir = Path.of(".").toAbsolutePath();
|
||||
MavenProjectBuilder.buildMavenProject(baseDir)
|
||||
.withResource("pom.xml", parentPom)
|
||||
.withResource("dangling/pom.xml", danglingPom)
|
||||
.withResource("example/pom.xml", modulePom)
|
||||
.afterSort()
|
||||
.assertDependencies(parentPom, none())
|
||||
.assertDependencies(modulePom, parentPom)
|
||||
.assertRemoved(danglingPom)
|
||||
.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Multi module with dependant projects")
|
||||
void multiModuleWithDependantProjects() {
|
||||
// Modules declared in order a,b,c
|
||||
@Language("xml")
|
||||
String parentPom = """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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>
|
||||
<groupId>com.acme</groupId>
|
||||
<artifactId>parent</artifactId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
<modules>
|
||||
<module>module-a</module>
|
||||
<module>module-b</module>
|
||||
<module>module-c</module>
|
||||
</modules>
|
||||
</project>
|
||||
""";
|
||||
|
||||
// Module A depends on C, so C must be built first effectively changing the order
|
||||
// in <modules>
|
||||
@Language("xml")
|
||||
String moduleAPom = """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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>com.acme</groupId>
|
||||
<artifactId>parent</artifactId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>module-a</artifactId>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.acme</groupId>
|
||||
<artifactId>module-c</artifactId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
""";
|
||||
|
||||
@Language("xml")
|
||||
String moduleBPom = """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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>com.acme</groupId>
|
||||
<artifactId>parent</artifactId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>module-b</artifactId>
|
||||
</project>
|
||||
""";
|
||||
|
||||
// C depends on B
|
||||
@Language("xml")
|
||||
String moduleCPom = """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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>com.acme</groupId>
|
||||
<artifactId>parent</artifactId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>module-c</artifactId>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.acme</groupId>
|
||||
<artifactId>module-b</artifactId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
""";
|
||||
|
||||
Path baseDir = Path.of(".").toAbsolutePath();
|
||||
MavenProjectBuilder.buildMavenProject(baseDir)
|
||||
.withResource("pom.xml", parentPom)
|
||||
.withResource("module-a/pom.xml", moduleAPom)
|
||||
.withResource("module-b/pom.xml", moduleBPom)
|
||||
.withResource("module-c/pom.xml", moduleCPom)
|
||||
.afterSort()
|
||||
.assertDependencies(parentPom, none())
|
||||
.assertDependencies(moduleAPom, parentPom, moduleCPom)
|
||||
.assertDependencies(moduleBPom, parentPom)
|
||||
.assertDependencies(moduleCPom, parentPom, moduleBPom)
|
||||
.verify();
|
||||
}
|
||||
|
||||
static class MavenProjectBuilder {
|
||||
|
||||
private final MavenProjectGraph sut = new MavenProjectGraph();
|
||||
|
||||
private static MavenArtifactDownloader artifactDownloader = new RewriteMavenArtifactDownloader(
|
||||
new LocalMavenArtifactCache(Path.of(System.getProperty("user.home")).resolve(".m2/repository")), null,
|
||||
e -> {
|
||||
throw new RuntimeException(e);
|
||||
});
|
||||
|
||||
;
|
||||
private static MavenProjectFactory projectFactory = new MavenProjectFactory(artifactDownloader);
|
||||
|
||||
;
|
||||
|
||||
private final Path baseDir;
|
||||
|
||||
private List<Resource> resources = new ArrayList<>();
|
||||
|
||||
private List<MavenProject> mavenProjects = new ArrayList<>();
|
||||
|
||||
private List<AbstractAssert> assertions = new ArrayList<>();
|
||||
|
||||
private Map<MavenProject, Set<MavenProject>> dependencyGraph;
|
||||
|
||||
private List<Supplier<Executable>> listAssertSupplier;
|
||||
|
||||
private SoftAssertions softAssertions = new SoftAssertions();
|
||||
|
||||
private Set<MavenProject> assertedProjects = new HashSet<>();
|
||||
|
||||
private Set<MavenProject> removedProjects = new HashSet<>();
|
||||
|
||||
private Set<MavenProject> selectedProjects = new HashSet<>();
|
||||
|
||||
public MavenProjectBuilder(Path baseDir) {
|
||||
this.baseDir = baseDir;
|
||||
}
|
||||
|
||||
public static MavenProjectBuilder buildMavenProject(Path baseDir) {
|
||||
return new MavenProjectBuilder(baseDir);
|
||||
}
|
||||
|
||||
public static List<String> none() {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
public MavenProjectBuilder withResource(String path, String content) {
|
||||
Resource r = new DummyResource(baseDir.resolve(path), content);
|
||||
this.resources.add(r);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MavenProjectBuilder afterSort() {
|
||||
mavenProjects = MavenProjectGraphTest.projectFactory.create(baseDir, resources);
|
||||
dependencyGraph = sut.from(baseDir, mavenProjects);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MavenProjectBuilder assertDependencies(String pomContent, String... dependantPomContents) {
|
||||
MavenProject moduleProject = findMavenProject(pomContent);
|
||||
this.assertedProjects.add(moduleProject);
|
||||
List<MavenProject> dependantProjects = getDependantProjects(dependantPomContents);
|
||||
// softAssertions.
|
||||
assertThat(dependencyGraph).containsKey(moduleProject);
|
||||
MavenProject[] dependantProjectsArray = dependantProjects.toArray(MavenProject[]::new);
|
||||
Set<MavenProject> actualDependantProjects = dependencyGraph.get(moduleProject);
|
||||
// softAssertions.
|
||||
assertThat(actualDependantProjects).containsExactlyInAnyOrder(dependantProjectsArray);
|
||||
return this;
|
||||
}
|
||||
|
||||
private List<MavenProject> getDependantProjects(String... dependantPomContents) {
|
||||
List<String> contents = Arrays.asList(dependantPomContents);
|
||||
return mavenProjects.stream().filter(p -> contents.contains(p.getBuildFile().getContent())).toList();
|
||||
}
|
||||
|
||||
private MavenProject findMavenProject(String pomContent) {
|
||||
return mavenProjects.stream()
|
||||
.filter(p -> pomContent.equals(p.getBuildFile().getContent()))
|
||||
.findFirst()
|
||||
.get();
|
||||
}
|
||||
|
||||
public MavenProjectBuilder assertDependencies(String parentPom, List<String> empty) {
|
||||
assertDependencies(parentPom);
|
||||
return this;
|
||||
}
|
||||
|
||||
public void verify() {
|
||||
softAssertions.assertThat(assertedProjects)
|
||||
.containsExactlyInAnyOrder(dependencyGraph.keySet().toArray(MavenProject[]::new));
|
||||
softAssertions.assertAll();
|
||||
}
|
||||
|
||||
public MavenProjectBuilder assertRemoved(String danglingPom) {
|
||||
MavenProject mavenProject = findMavenProject(danglingPom);
|
||||
this.removedProjects.add(mavenProject);
|
||||
this.selectedProjects.remove(mavenProject);
|
||||
softAssertions.assertThat(selectedProjects).doesNotContain(mavenProject);
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -33,6 +33,7 @@ import org.openrewrite.maven.tree.Scope;
|
||||
import org.openrewrite.style.Style;
|
||||
import org.springframework.rewrite.parsers.SpringRewriteProperties;
|
||||
import org.springframework.rewrite.parsers.RewriteProjectParsingResult;
|
||||
import org.springframework.rewrite.parsers.maven.ClasspathDependencies;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
@@ -236,7 +237,9 @@ public class ParserParityTestHelper {
|
||||
static void verifyEqualSourceFileMarkers(SourceFile curExpectedSourceFile, SourceFile curGivenSourceFile) {
|
||||
Markers expectedMarkers = curExpectedSourceFile.getMarkers();
|
||||
List<Marker> expectedMarkersList = expectedMarkers.getMarkers();
|
||||
Markers givenMarkers = curGivenSourceFile.getMarkers();
|
||||
|
||||
// Remove custom marker that only exists here
|
||||
Markers givenMarkers = curGivenSourceFile.getMarkers().removeByType(ClasspathDependencies.class);
|
||||
List<Marker> actualMarkersList = givenMarkers.getMarkers();
|
||||
|
||||
assertThat(actualMarkersList.stream().map(m -> m.getClass().getSimpleName()).toList())
|
||||
|
||||
Reference in New Issue
Block a user