From 8e42e14a1731476a86274a0d8f24519ff8c7e7f5 Mon Sep 17 00:00:00 2001 From: Tiemen Schut Date: Fri, 15 Nov 2024 12:26:52 +0100 Subject: [PATCH 1/5] Add .dependsOn method for KotlinParser --- .../org/openrewrite/kotlin/KotlinParser.java | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/openrewrite/kotlin/KotlinParser.java b/src/main/java/org/openrewrite/kotlin/KotlinParser.java index d1dd8782..c9d9c0c5 100644 --- a/src/main/java/org/openrewrite/kotlin/KotlinParser.java +++ b/src/main/java/org/openrewrite/kotlin/KotlinParser.java @@ -19,6 +19,7 @@ import kotlin.Unit; import kotlin.annotation.AnnotationTarget; import kotlin.jvm.functions.Function1; +import kotlin.random.Random; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; import org.intellij.lang.annotations.Language; @@ -101,6 +102,7 @@ import static org.jetbrains.kotlin.config.JVMConfigurationKeys.DO_NOT_CLEAR_BINDING_CONTEXT; import static org.jetbrains.kotlin.config.JVMConfigurationKeys.LINK_VIA_SIGNATURES; import static org.jetbrains.kotlin.incremental.IncrementalFirJvmCompilerRunnerKt.configureBaseRoots; +import static org.openrewrite.java.JavaParser.resolveSourcePathFromSourceText; @SuppressWarnings("CommentedOutCode") @RequiredArgsConstructor(access = AccessLevel.PRIVATE) @@ -115,6 +117,9 @@ public class KotlinParser implements Parser { @Nullable private final Collection classpath; + @Nullable + private final Collection dependsOn; + private final List styles; private final boolean logCompilationWarningsAndErrors; private final JavaTypeCache typeCache; @@ -139,7 +144,7 @@ public Stream parse(@Language("kotlin") String... sources) { String pkg = packageMatcher.find() ? packageMatcher.group(1).replace('.', '/') + "/" : ""; String className = Optional.ofNullable(simpleName.apply(sourceFile)) - .orElse(Long.toString(System.nanoTime())) + ".kt"; + .orElse(Long.toString(System.nanoTime())) + ".kt"; Path path = Paths.get(pkg + className); return new Input( @@ -162,10 +167,12 @@ public Stream parseInputs(Iterable sources, @Nullable Path re // TODO: FIR and disposable may not be necessary using the IR. Disposable disposable = Disposer.newDisposable(); CompiledSource compilerCus; + List acceptedInputs = dependsOn == null ? new ArrayList<>() : new ArrayList<>(dependsOn); + acceptedInputs.addAll(acceptedInputs(sources).collect(Collectors.toList())); try { - compilerCus = parse(acceptedInputs(sources).collect(Collectors.toList()), disposable, pctx); + compilerCus = parse(acceptedInputs, disposable, pctx); } catch (Exception e) { - return acceptedInputs(sources).map(input -> ParseError.build(this, input, relativeTo, ctx, e)); + return acceptedInputs.stream().map(input -> ParseError.build(this, input, relativeTo, ctx, e)); } FirSession firSession = compilerCus.getFirSession(); @@ -201,7 +208,8 @@ public Stream parseInputs(Iterable sources, @Nullable Path re return (SourceFile) null; }) .limit(1)) - .filter(Objects::nonNull); + .filter(Objects::nonNull) + .filter(source -> !source.getSourcePath().getFileName().toString().matches("dependsOn-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\\.kt")); } @Override @@ -252,6 +260,7 @@ public static class Builder extends Parser.Builder { @Nullable private Collection classpath = emptyList(); + private Collection dependsOn = emptyList(); private JavaTypeCache typeCache = new JavaTypeCache(); private boolean logCompilationWarningsAndErrors; private final List styles = new ArrayList<>(); @@ -303,6 +312,13 @@ public Builder addClasspathEntry(Path classpath) { return this; } + public Builder dependsOn(@Language("kotlin") String... inputsAsStrings) { + this.dependsOn = Arrays.stream(inputsAsStrings) + .map(input -> Input.fromString(Paths.get("dependsOn-" + UUID.randomUUID() + ".kt"), input)) + .collect(toList()); + return this; + } + public Builder typeCache(JavaTypeCache typeCache) { this.typeCache = typeCache; return this; @@ -335,7 +351,7 @@ public Builder languageLevel(KotlinLanguageLevel languageLevel) { @Override public KotlinParser build() { - return new KotlinParser(resolvedClasspath(), styles, logCompilationWarningsAndErrors, typeCache, moduleName, languageLevel, isKotlinScript); + return new KotlinParser(resolvedClasspath(), dependsOn, styles, logCompilationWarningsAndErrors, typeCache, moduleName, languageLevel, isKotlinScript); } @Override From 64bf95e7ca781c2e7771054414ced55430e132cd Mon Sep 17 00:00:00 2001 From: Tiemen Schut Date: Fri, 15 Nov 2024 12:30:39 +0100 Subject: [PATCH 2/5] Add .dependsOn method for KotlinParser --- src/main/java/org/openrewrite/kotlin/KotlinParser.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/org/openrewrite/kotlin/KotlinParser.java b/src/main/java/org/openrewrite/kotlin/KotlinParser.java index c9d9c0c5..3a8097e5 100644 --- a/src/main/java/org/openrewrite/kotlin/KotlinParser.java +++ b/src/main/java/org/openrewrite/kotlin/KotlinParser.java @@ -19,7 +19,6 @@ import kotlin.Unit; import kotlin.annotation.AnnotationTarget; import kotlin.jvm.functions.Function1; -import kotlin.random.Random; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; import org.intellij.lang.annotations.Language; @@ -102,7 +101,6 @@ import static org.jetbrains.kotlin.config.JVMConfigurationKeys.DO_NOT_CLEAR_BINDING_CONTEXT; import static org.jetbrains.kotlin.config.JVMConfigurationKeys.LINK_VIA_SIGNATURES; import static org.jetbrains.kotlin.incremental.IncrementalFirJvmCompilerRunnerKt.configureBaseRoots; -import static org.openrewrite.java.JavaParser.resolveSourcePathFromSourceText; @SuppressWarnings("CommentedOutCode") @RequiredArgsConstructor(access = AccessLevel.PRIVATE) From f8a3819928d5e264cd02b5a4ce25be8dc3475909 Mon Sep 17 00:00:00 2001 From: Tiemen Schut Date: Fri, 15 Nov 2024 15:26:28 +0100 Subject: [PATCH 3/5] Add basic testcase --- .../openrewrite/kotlin/KotlinParserTest.java | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 src/test/java/org/openrewrite/kotlin/KotlinParserTest.java diff --git a/src/test/java/org/openrewrite/kotlin/KotlinParserTest.java b/src/test/java/org/openrewrite/kotlin/KotlinParserTest.java new file mode 100644 index 00000000..bda3b055 --- /dev/null +++ b/src/test/java/org/openrewrite/kotlin/KotlinParserTest.java @@ -0,0 +1,46 @@ +/* + * Copyright 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.kotlin; + +import org.intellij.lang.annotations.Language; +import org.junit.jupiter.api.Test; +import org.openrewrite.test.RewriteTest; + +import static org.openrewrite.kotlin.Assertions.kotlin; + +class KotlinParserTest implements RewriteTest { + + @Language("kotlin") + private String myClassDefinition = """ + package foo.bar + + class MyClass + """; + + @Test + void classDefinitionFromDependsOn() { + rewriteRun( + spec -> spec.parser(KotlinParser.builder().dependsOn(myClassDefinition)), + kotlin( + """ + import foo.bar.MyClass + + val myClass: MyClass? = null + """ + ) + ); + } +} From a5d18d50c9400f0c11997c659d3846bda60a24f0 Mon Sep 17 00:00:00 2001 From: Tiemen Schut Date: Fri, 15 Nov 2024 16:11:40 +0100 Subject: [PATCH 4/5] Fix up sourcePath determine logic --- .../org/openrewrite/kotlin/KotlinParser.java | 30 +++++++++++++++++-- .../openrewrite/kotlin/KotlinParserTest.java | 4 +-- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/openrewrite/kotlin/KotlinParser.java b/src/main/java/org/openrewrite/kotlin/KotlinParser.java index 3a8097e5..f7631a38 100644 --- a/src/main/java/org/openrewrite/kotlin/KotlinParser.java +++ b/src/main/java/org/openrewrite/kotlin/KotlinParser.java @@ -101,6 +101,7 @@ import static org.jetbrains.kotlin.config.JVMConfigurationKeys.DO_NOT_CLEAR_BINDING_CONTEXT; import static org.jetbrains.kotlin.config.JVMConfigurationKeys.LINK_VIA_SIGNATURES; import static org.jetbrains.kotlin.incremental.IncrementalFirJvmCompilerRunnerKt.configureBaseRoots; +import static org.openrewrite.kotlin.KotlinParser.SourcePathFromSourceTextResolver.*; @SuppressWarnings("CommentedOutCode") @RequiredArgsConstructor(access = AccessLevel.PRIVATE) @@ -207,7 +208,7 @@ public Stream parseInputs(Iterable sources, @Nullable Path re }) .limit(1)) .filter(Objects::nonNull) - .filter(source -> !source.getSourcePath().getFileName().toString().matches("dependsOn-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\\.kt")); + .filter(source -> !source.getSourcePath().getFileName().toString().startsWith("dependsOn-")); } @Override @@ -312,7 +313,7 @@ public Builder addClasspathEntry(Path classpath) { public Builder dependsOn(@Language("kotlin") String... inputsAsStrings) { this.dependsOn = Arrays.stream(inputsAsStrings) - .map(input -> Input.fromString(Paths.get("dependsOn-" + UUID.randomUUID() + ".kt"), input)) + .map(input -> Input.fromString(determinePath("dependsOn-", input), input)) .collect(toList()); return this; } @@ -623,4 +624,29 @@ private List getCRLFLocations(String source) { return cRLFIndices; } + + static class SourcePathFromSourceTextResolver { + static Pattern packagePattern = Pattern.compile("^package\\s+(.+)\\s"); + static Pattern classPattern = Pattern.compile("(class|interface|enum class)\\s*(<[^>]*>)?\\s+(\\w+)"); + static Pattern publicClassPattern = Pattern.compile("public\\s+" + classPattern.pattern()); + + private static Optional matchClassPattern(Pattern pattern, String source) { + Matcher classMatcher = pattern.matcher(source); + if (classMatcher.find()) { + return Optional.of(classMatcher.group(3)); + } + return Optional.empty(); + } + + static Path determinePath(String prefix, String sourceCode) { + String className = matchClassPattern(publicClassPattern, sourceCode) + .orElseGet(() -> matchClassPattern(classPattern, sourceCode).orElse(Long.toString(System.nanoTime()))); + + Matcher packageMatcher = packagePattern.matcher(sourceCode); + String pkg = packageMatcher.find() ? packageMatcher.group(1).replace('.', '/') + "/" : ""; + + return Paths.get(pkg, prefix + className + ".kt"); + } + + } } diff --git a/src/test/java/org/openrewrite/kotlin/KotlinParserTest.java b/src/test/java/org/openrewrite/kotlin/KotlinParserTest.java index bda3b055..a2b6f2a2 100644 --- a/src/test/java/org/openrewrite/kotlin/KotlinParserTest.java +++ b/src/test/java/org/openrewrite/kotlin/KotlinParserTest.java @@ -25,9 +25,9 @@ class KotlinParserTest implements RewriteTest { @Language("kotlin") private String myClassDefinition = """ - package foo.bar + package foo.bar - class MyClass + class MyClass """; @Test From ec039e04b58e5e16d388f5da604e78889210f90c Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Mon, 18 Nov 2024 22:29:30 +0100 Subject: [PATCH 5/5] Minor polish --- .../org/openrewrite/kotlin/KotlinParser.java | 25 ++++++++----------- .../openrewrite/kotlin/KotlinParserTest.java | 15 +++++------ 2 files changed, 17 insertions(+), 23 deletions(-) diff --git a/src/main/java/org/openrewrite/kotlin/KotlinParser.java b/src/main/java/org/openrewrite/kotlin/KotlinParser.java index f7631a38..50338f12 100644 --- a/src/main/java/org/openrewrite/kotlin/KotlinParser.java +++ b/src/main/java/org/openrewrite/kotlin/KotlinParser.java @@ -65,6 +65,7 @@ import org.jetbrains.kotlin.utils.PathUtil; import org.jspecify.annotations.Nullable; import org.openrewrite.*; +import org.openrewrite.internal.ListUtils; import org.openrewrite.java.JavaParser; import org.openrewrite.java.internal.JavaTypeCache; import org.openrewrite.java.marker.JavaSourceSet; @@ -87,7 +88,6 @@ import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.Collectors; import java.util.stream.Stream; import static java.util.Collections.emptyList; @@ -101,7 +101,7 @@ import static org.jetbrains.kotlin.config.JVMConfigurationKeys.DO_NOT_CLEAR_BINDING_CONTEXT; import static org.jetbrains.kotlin.config.JVMConfigurationKeys.LINK_VIA_SIGNATURES; import static org.jetbrains.kotlin.incremental.IncrementalFirJvmCompilerRunnerKt.configureBaseRoots; -import static org.openrewrite.kotlin.KotlinParser.SourcePathFromSourceTextResolver.*; +import static org.openrewrite.kotlin.KotlinParser.SourcePathFromSourceTextResolver.determinePath; @SuppressWarnings("CommentedOutCode") @RequiredArgsConstructor(access = AccessLevel.PRIVATE) @@ -117,7 +117,7 @@ public class KotlinParser implements Parser { private final Collection classpath; @Nullable - private final Collection dependsOn; + private final List dependsOn; private final List styles; private final boolean logCompilationWarningsAndErrors; @@ -143,7 +143,7 @@ public Stream parse(@Language("kotlin") String... sources) { String pkg = packageMatcher.find() ? packageMatcher.group(1).replace('.', '/') + "/" : ""; String className = Optional.ofNullable(simpleName.apply(sourceFile)) - .orElse(Long.toString(System.nanoTime())) + ".kt"; + .orElse(Long.toString(System.nanoTime())) + ".kt"; Path path = Paths.get(pkg + className); return new Input( @@ -166,8 +166,7 @@ public Stream parseInputs(Iterable sources, @Nullable Path re // TODO: FIR and disposable may not be necessary using the IR. Disposable disposable = Disposer.newDisposable(); CompiledSource compilerCus; - List acceptedInputs = dependsOn == null ? new ArrayList<>() : new ArrayList<>(dependsOn); - acceptedInputs.addAll(acceptedInputs(sources).collect(Collectors.toList())); + List acceptedInputs = ListUtils.concatAll(dependsOn, acceptedInputs(sources).collect(toList())); try { compilerCus = parse(acceptedInputs, disposable, pctx); } catch (Exception e) { @@ -259,7 +258,7 @@ public static class Builder extends Parser.Builder { @Nullable private Collection classpath = emptyList(); - private Collection dependsOn = emptyList(); + private List dependsOn = emptyList(); private JavaTypeCache typeCache = new JavaTypeCache(); private boolean logCompilationWarningsAndErrors; private final List styles = new ArrayList<>(); @@ -626,9 +625,9 @@ private List getCRLFLocations(String source) { } static class SourcePathFromSourceTextResolver { - static Pattern packagePattern = Pattern.compile("^package\\s+(.+)\\s"); - static Pattern classPattern = Pattern.compile("(class|interface|enum class)\\s*(<[^>]*>)?\\s+(\\w+)"); - static Pattern publicClassPattern = Pattern.compile("public\\s+" + classPattern.pattern()); + private static final Pattern packagePattern = Pattern.compile("^package\\s+(\\S+)"); + private static final Pattern classPattern = Pattern.compile("(class|interface|enum class)\\s*(<[^>]*>)?\\s+(\\w+)"); + private static final Pattern publicClassPattern = Pattern.compile("public\\s+" + classPattern.pattern()); private static Optional matchClassPattern(Pattern pattern, String source) { Matcher classMatcher = pattern.matcher(source); @@ -640,13 +639,11 @@ private static Optional matchClassPattern(Pattern pattern, String source static Path determinePath(String prefix, String sourceCode) { String className = matchClassPattern(publicClassPattern, sourceCode) - .orElseGet(() -> matchClassPattern(classPattern, sourceCode).orElse(Long.toString(System.nanoTime()))); - + .orElseGet(() -> matchClassPattern(classPattern, sourceCode) + .orElse(Long.toString(System.nanoTime()))); Matcher packageMatcher = packagePattern.matcher(sourceCode); String pkg = packageMatcher.find() ? packageMatcher.group(1).replace('.', '/') + "/" : ""; - return Paths.get(pkg, prefix + className + ".kt"); } - } } diff --git a/src/test/java/org/openrewrite/kotlin/KotlinParserTest.java b/src/test/java/org/openrewrite/kotlin/KotlinParserTest.java index a2b6f2a2..1b1b1d74 100644 --- a/src/test/java/org/openrewrite/kotlin/KotlinParserTest.java +++ b/src/test/java/org/openrewrite/kotlin/KotlinParserTest.java @@ -15,7 +15,6 @@ */ package org.openrewrite.kotlin; -import org.intellij.lang.annotations.Language; import org.junit.jupiter.api.Test; import org.openrewrite.test.RewriteTest; @@ -23,17 +22,14 @@ class KotlinParserTest implements RewriteTest { - @Language("kotlin") - private String myClassDefinition = """ - package foo.bar - - class MyClass - """; - @Test void classDefinitionFromDependsOn() { rewriteRun( - spec -> spec.parser(KotlinParser.builder().dependsOn(myClassDefinition)), + spec -> spec.parser(KotlinParser.builder().dependsOn(""" + package foo.bar + + class MyClass + """)), kotlin( """ import foo.bar.MyClass @@ -43,4 +39,5 @@ void classDefinitionFromDependsOn() { ) ); } + }