From 74211008b73e3aab89356d78939c6fe1f562cd9d Mon Sep 17 00:00:00 2001 From: Kun Li Date: Mon, 9 Oct 2023 16:44:05 -0700 Subject: [PATCH] add kotlin parser --- .../maven/MavenMojoProjectParser.java | 87 ++++++++++++++++--- .../org/openrewrite/maven/ResourceParser.java | 5 +- 2 files changed, 79 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/openrewrite/maven/MavenMojoProjectParser.java b/src/main/java/org/openrewrite/maven/MavenMojoProjectParser.java index 5c7e709a..908c35de 100644 --- a/src/main/java/org/openrewrite/maven/MavenMojoProjectParser.java +++ b/src/main/java/org/openrewrite/maven/MavenMojoProjectParser.java @@ -41,6 +41,7 @@ import org.openrewrite.java.marker.JavaSourceSet; import org.openrewrite.java.marker.JavaVersion; import org.openrewrite.java.tree.J; +import org.openrewrite.kotlin.KotlinParser; import org.openrewrite.marker.*; import org.openrewrite.marker.ci.BuildEnvironment; import org.openrewrite.maven.cache.CompositeMavenPomCache; @@ -54,6 +55,7 @@ import org.openrewrite.tree.ParsingExecutionContextView; import org.openrewrite.xml.tree.Xml; +import java.io.File; import java.io.IOException; import java.nio.charset.Charset; import java.nio.file.Files; @@ -175,10 +177,14 @@ public Stream listSourceFiles(MavenProject mavenProject, @Nullable X JavaParser.Builder javaParserBuilder = JavaParser.fromJavaVersion() .styles(styles) .logCompilationWarningsAndErrors(false); - ResourceParser rp = new ResourceParser(baseDir, logger, exclusions, plainTextMasks, sizeThresholdMb, pathsToOtherMavenProjects(mavenProject), javaParserBuilder.clone()); - sourceFiles = Stream.concat(sourceFiles, processMainSources(mavenProject, javaParserBuilder.clone(), rp, projectProvenance, alreadyParsed, ctx)); - sourceFiles = Stream.concat(sourceFiles, processTestSources(mavenProject, javaParserBuilder.clone(), rp, projectProvenance, alreadyParsed, ctx)); + // todo, add styles from autoDetect + KotlinParser.Builder kotlinParserBuilder = KotlinParser.builder(); + ResourceParser rp = new ResourceParser(baseDir, logger, exclusions, plainTextMasks, sizeThresholdMb, pathsToOtherMavenProjects(mavenProject), + javaParserBuilder.clone(), kotlinParserBuilder.clone()); + + sourceFiles = Stream.concat(sourceFiles, processMainSources(mavenProject, javaParserBuilder.clone(), kotlinParserBuilder.clone(), rp, projectProvenance, alreadyParsed, ctx)); + sourceFiles = Stream.concat(sourceFiles, processTestSources(mavenProject, javaParserBuilder.clone(), kotlinParserBuilder.clone(), rp, projectProvenance, alreadyParsed, ctx)); Collection exclusionMatchers = exclusions.stream() .map(pattern -> baseDir.getFileSystem().getPathMatcher("glob:" + pattern)) .collect(toList()); @@ -287,6 +293,7 @@ public List generateProvenance(MavenProject mavenProject) { private Stream processMainSources( MavenProject mavenProject, JavaParser.Builder javaParserBuilder, + KotlinParser.Builder kotlinParserBuilder, ResourceParser resourceParser, List projectProvenance, Set alreadyParsed, @@ -302,29 +309,41 @@ private Stream processMainSources( alreadyParsed.addAll(mainJavaSources); + // scan Kotlin files + String kotlinSourceDir = getKotlinDirectory(mavenProject.getBuild().getSourceDirectory()); + List mainKotlinSources = (kotlinSourceDir != null) ? listKotlinSources(mavenProject.getBasedir().toPath().resolve(kotlinSourceDir)) : Collections.emptyList(); + alreadyParsed.addAll(mainKotlinSources); + logInfo(mavenProject, "Parsing source files"); List dependencies = mavenProject.getCompileClasspathElements().stream() .distinct() .map(Paths::get) .collect(toList()); - javaParserBuilder.classpath(dependencies); JavaTypeCache typeCache = new JavaTypeCache(); - javaParserBuilder.typeCache(typeCache); + javaParserBuilder.classpath(dependencies).typeCache(typeCache); + kotlinParserBuilder.classpath(dependencies).typeCache(new JavaTypeCache()); Stream cus = Stream.of(javaParserBuilder) .map(JavaParser.Builder::build) .flatMap(parser -> parser.parse(mainJavaSources, baseDir, ctx)); + Stream kcus = Stream.of(kotlinParserBuilder) + .map(KotlinParser.Builder::build) + .flatMap(parser -> parser.parse(mainKotlinSources, baseDir, ctx)); + List mainProjectProvenance = new ArrayList<>(projectProvenance); mainProjectProvenance.add(sourceSet("main", dependencies, typeCache)); Stream parsedJava = cus.map(addProvenance(baseDir, mainProjectProvenance, generatedSourcePaths)); logDebug(mavenProject, "Scanned " + mainJavaSources.size() + " java source files in main scope."); + Stream parsedKotlin = kcus.map(addProvenance(baseDir, mainProjectProvenance, generatedSourcePaths)); + logDebug(mavenProject, "Scanned " + mainKotlinSources.size() + " kotlin source files in main scope."); + //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 = baseDir.relativize(Paths.get(mavenProject.getBuild().getDirectory())); - Stream sourceFiles = parsedJava.filter(s -> !s.getSourcePath().startsWith(buildDirectory)); + Stream sourceFiles = Stream.concat(parsedJava, parsedKotlin).filter(s -> !s.getSourcePath().startsWith(buildDirectory)); int sourcesParsedBefore = alreadyParsed.size(); Stream parsedResourceFiles = resourceParser.parse(mavenProject.getBasedir().toPath().resolve("src/main/resources"), alreadyParsed) @@ -339,6 +358,7 @@ private Stream processMainSources( private Stream processTestSources( MavenProject mavenProject, JavaParser.Builder javaParserBuilder, + KotlinParser.Builder kotlinParserBuilder, ResourceParser resourceParser, List projectProvenance, Set alreadyParsed, @@ -349,24 +369,37 @@ private Stream processTestSources( .map(Paths::get) .collect(toList()); - javaParserBuilder.classpath(testDependencies); JavaTypeCache typeCache = new JavaTypeCache(); - javaParserBuilder.typeCache(typeCache); + javaParserBuilder.classpath(testDependencies).typeCache(typeCache); + kotlinParserBuilder.classpath(testDependencies).typeCache(new JavaTypeCache()); List testJavaSources = listJavaSources(mavenProject.getBasedir().toPath().resolve(mavenProject.getBuild().getTestSourceDirectory())); alreadyParsed.addAll(testJavaSources); + // scan Kotlin files + String kotlinSourceDir = getKotlinDirectory(mavenProject.getBuild().getSourceDirectory()); + List mainKotlinSources = (kotlinSourceDir != null) ? listKotlinSources(mavenProject.getBasedir().toPath().resolve(kotlinSourceDir)) : Collections.emptyList(); + + alreadyParsed.addAll(mainKotlinSources); + Stream cus = Stream.of(javaParserBuilder) .map(JavaParser.Builder::build) .flatMap(parser -> parser.parse(testJavaSources, baseDir, ctx)); + Stream kcus = Stream.of(kotlinParserBuilder) + .map(KotlinParser.Builder::build) + .flatMap(parser -> parser.parse(mainKotlinSources, baseDir, ctx)); + List markers = new ArrayList<>(projectProvenance); markers.add(sourceSet("test", testDependencies, typeCache)); Stream parsedJava = cus.map(addProvenance(baseDir, markers, null)); - logDebug(mavenProject, "Scanned " + testJavaSources.size() + " java source files in test scope."); - Stream sourceFiles = parsedJava; + + Stream parsedKotlin = kcus.map(addProvenance(baseDir, markers, null)); + logDebug(mavenProject, "Scanned " + mainKotlinSources.size() + " kotlin source files in main scope."); + + Stream sourceFiles = Stream.concat(parsedJava, parsedKotlin); // Any resources parsed from "test/resources" should also have the test source set added to them. int sourcesParsedBefore = alreadyParsed.size(); @@ -377,6 +410,28 @@ private Stream processTestSources( return sourceFiles; } + @Nullable + private String getKotlinDirectory(@Nullable String sourceDirectory) { + if (sourceDirectory == null) { + return null; + } + + File directory = new File(sourceDirectory); + File parentDirectory = directory.getParentFile(); + if (parentDirectory != null) { + File[] subdirectories = parentDirectory.listFiles(File::isDirectory); + if (subdirectories != null) { + for (File subdirectory : subdirectories) { + if (subdirectory.getName().equals("kotlin")) { + return subdirectory.getAbsolutePath(); + } + } + } + } + + return null; + } + @NotNull private static JavaSourceSet sourceSet(String name, List dependencies, JavaTypeCache typeCache) { return JavaSourceSet.build(name, dependencies, typeCache, false); @@ -641,14 +696,22 @@ private UnaryOperator addProvenance(Path baseDir, List } private static List listJavaSources(Path sourceDirectory) throws MojoExecutionException { + return listSources(sourceDirectory, ".java"); + } + + private static List listKotlinSources(Path sourceDirectory) throws MojoExecutionException { + return listSources(sourceDirectory, ".kt"); + } + + private static List listSources(Path sourceDirectory, String extension) throws MojoExecutionException { if (!Files.exists(sourceDirectory)) { return emptyList(); } - try (Stream files = Files.find(sourceDirectory, 16, (f, a) -> !a.isDirectory() && f.toString().endsWith(".java"))) { + try (Stream files = Files.find(sourceDirectory, 16, (f, a) -> !a.isDirectory() && f.toString().endsWith(extension))) { return files.collect(toList()); } catch (IOException e) { - throw new MojoExecutionException("Unable to list Java source files", e); + throw new MojoExecutionException("Unable to list source files of " + extension, e); } } diff --git a/src/main/java/org/openrewrite/maven/ResourceParser.java b/src/main/java/org/openrewrite/maven/ResourceParser.java index 37a81333..4abe1e65 100644 --- a/src/main/java/org/openrewrite/maven/ResourceParser.java +++ b/src/main/java/org/openrewrite/maven/ResourceParser.java @@ -55,11 +55,14 @@ public class ResourceParser { */ private final JavaParser.Builder javaParserBuilder; + private final KotlinParser.Builder kotlinParserBuilder; + public ResourceParser(Path baseDir, Log logger, Collection exclusions, Collection plainTextMasks, int sizeThresholdMb, Collection excludedDirectories, - JavaParser.Builder javaParserBuilder) { + JavaParser.Builder javaParserBuilder, KotlinParser.Builder kotlinParserBuilder) { this.baseDir = baseDir; this.logger = logger; this.javaParserBuilder = javaParserBuilder; + this.kotlinParserBuilder = kotlinParserBuilder; this.exclusions = pathMatchers(baseDir, exclusions); this.sizeThresholdMb = sizeThresholdMb; this.excludedDirectories = excludedDirectories;