Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add .dependsOn method for KotlinParser #617

Merged
merged 5 commits into from
Nov 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 42 additions & 5 deletions src/main/java/org/openrewrite/kotlin/KotlinParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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.determinePath;

@SuppressWarnings("CommentedOutCode")
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
Expand All @@ -115,6 +116,9 @@ public class KotlinParser implements Parser {
@Nullable
private final Collection<Path> classpath;

@Nullable
private final List<Input> dependsOn;

private final List<NamedStyles> styles;
private final boolean logCompilationWarningsAndErrors;
private final JavaTypeCache typeCache;
Expand Down Expand Up @@ -162,10 +166,11 @@ public Stream<SourceFile> parseInputs(Iterable<Input> sources, @Nullable Path re
// TODO: FIR and disposable may not be necessary using the IR.
Disposable disposable = Disposer.newDisposable();
CompiledSource compilerCus;
List<Input> acceptedInputs = ListUtils.concatAll(dependsOn, acceptedInputs(sources).collect(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();
Expand Down Expand Up @@ -201,7 +206,8 @@ public Stream<SourceFile> parseInputs(Iterable<Input> sources, @Nullable Path re
return (SourceFile) null;
})
.limit(1))
.filter(Objects::nonNull);
.filter(Objects::nonNull)
.filter(source -> !source.getSourcePath().getFileName().toString().startsWith("dependsOn-"));
}

@Override
Expand Down Expand Up @@ -252,6 +258,7 @@ public static class Builder extends Parser.Builder {
@Nullable
private Collection<Path> classpath = emptyList();

private List<Input> dependsOn = emptyList();
private JavaTypeCache typeCache = new JavaTypeCache();
private boolean logCompilationWarningsAndErrors;
private final List<NamedStyles> styles = new ArrayList<>();
Expand Down Expand Up @@ -303,6 +310,13 @@ public Builder addClasspathEntry(Path classpath) {
return this;
}

public Builder dependsOn(@Language("kotlin") String... inputsAsStrings) {
this.dependsOn = Arrays.stream(inputsAsStrings)
.map(input -> Input.fromString(determinePath("dependsOn-", input), input))
.collect(toList());
return this;
}

public Builder typeCache(JavaTypeCache typeCache) {
this.typeCache = typeCache;
return this;
Expand Down Expand Up @@ -335,7 +349,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
Expand Down Expand Up @@ -609,4 +623,27 @@ private List<Integer> getCRLFLocations(String source) {

return cRLFIndices;
}

static class SourcePathFromSourceTextResolver {
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<String> 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");
}
}
}
43 changes: 43 additions & 0 deletions src/test/java/org/openrewrite/kotlin/KotlinParserTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright 2023 the original author or authors.
* <p>
* 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
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.junit.jupiter.api.Test;
import org.openrewrite.test.RewriteTest;

import static org.openrewrite.kotlin.Assertions.kotlin;

class KotlinParserTest implements RewriteTest {

@Test
void classDefinitionFromDependsOn() {
rewriteRun(
spec -> spec.parser(KotlinParser.builder().dependsOn("""
package foo.bar

class MyClass
""")),
kotlin(
"""
import foo.bar.MyClass

val myClass: MyClass? = null
"""
)
);
}

}