From 2cc261751636dc2b98e90732d6f45a5cc43b7e83 Mon Sep 17 00:00:00 2001 From: aboyko Date: Mon, 17 Apr 2023 12:39:43 -0400 Subject: [PATCH] Adopt Rewrite changes around JavaSourceSet --- .../commons/rewrite/java/ORAstUtils.java | 35 ++++++++++++++- .../rewrite/maven/MavenProjectParser.java | 20 +++++---- .../rewrite/RewriteCompilationUnitCache.java | 43 +++++++++++++++++-- .../boot/java/rewrite/RewriteReconciler.java | 21 ++++++--- 4 files changed, 100 insertions(+), 19 deletions(-) diff --git a/headless-services/commons/commons-rewrite/src/main/java/org/springframework/ide/vscode/commons/rewrite/java/ORAstUtils.java b/headless-services/commons/commons-rewrite/src/main/java/org/springframework/ide/vscode/commons/rewrite/java/ORAstUtils.java index 8bc0962fd..958d2ae5d 100644 --- a/headless-services/commons/commons-rewrite/src/main/java/org/springframework/ide/vscode/commons/rewrite/java/ORAstUtils.java +++ b/headless-services/commons/commons-rewrite/src/main/java/org/springframework/ide/vscode/commons/rewrite/java/ORAstUtils.java @@ -40,8 +40,11 @@ import org.openrewrite.java.JavaParser.Builder; import org.openrewrite.java.JavaParsingException; import org.openrewrite.java.JavaVisitor; import org.openrewrite.java.UpdateSourcePositions; +import org.openrewrite.java.marker.JavaSourceSet; import org.openrewrite.java.tree.J; import org.openrewrite.java.tree.J.CompilationUnit; +import org.openrewrite.java.tree.JavaSourceFile; +import org.openrewrite.java.tree.JavaType; import org.openrewrite.marker.Range; import org.openrewrite.tree.ParsingExecutionContextView; import org.slf4j.Logger; @@ -53,6 +56,8 @@ import org.springframework.ide.vscode.commons.rewrite.maven.MavenProjectParser; import org.springframework.ide.vscode.commons.util.ExceptionUtil; import org.springframework.ide.vscode.commons.util.text.TextDocument; +import javolution.util.function.Supplier; + public class ORAstUtils { private static final Logger log = LoggerFactory.getLogger(ORAstUtils.class); @@ -234,8 +239,12 @@ public class ORAstUtils { // } public static JavaParser createJavaParser(IJavaProject project) { + return createJavaParser(() -> createJavaParserBuilder(project)); + } + + public static JavaParser createJavaParser(Supplier> f) { try { - return createJavaParserBuilder(project).build(); + return f.get().build(); } catch (Exception e) { if (isExceptionFromInterrupedThread(e)) { log.debug("", e); @@ -458,4 +467,28 @@ public class ORAstUtils { return MavenProjectParser.MAIN; } } + + public static JavaSourceSet addJavaSourceSet(List sourceFiles, String sourceSetName, List classpath) { + JavaSourceSet sourceSet = JavaSourceSet.build(sourceSetName, classpath, null, false); + List types = sourceSet.getClasspath(); + for (SourceFile sourceFile : sourceFiles) { + if (!(sourceFile instanceof JavaSourceFile)) { + continue; + } + + for (JavaType type : ((JavaSourceFile) sourceFile).getTypesInUse().getTypesInUse()) { + if (type instanceof JavaType.FullyQualified) { + types.add((JavaType.FullyQualified) type); + } + } + } + sourceSet = sourceSet.withClasspath(types); + + for (int i = 0; i < sourceFiles.size(); i++) { + SourceFile sourceFile = sourceFiles.get(i); + sourceFiles.set(i, sourceFile + .withMarkers(sourceFile.getMarkers().computeByType(sourceSet, (original, updated) -> updated))); + } + return sourceSet; + } } diff --git a/headless-services/commons/commons-rewrite/src/main/java/org/springframework/ide/vscode/commons/rewrite/maven/MavenProjectParser.java b/headless-services/commons/commons-rewrite/src/main/java/org/springframework/ide/vscode/commons/rewrite/maven/MavenProjectParser.java index 6338b280f..ba325638d 100644 --- a/headless-services/commons/commons-rewrite/src/main/java/org/springframework/ide/vscode/commons/rewrite/maven/MavenProjectParser.java +++ b/headless-services/commons/commons-rewrite/src/main/java/org/springframework/ide/vscode/commons/rewrite/maven/MavenProjectParser.java @@ -41,6 +41,7 @@ import org.openrewrite.java.JavaParser; import org.openrewrite.java.marker.JavaProject; import org.openrewrite.java.marker.JavaSourceSet; import org.openrewrite.java.marker.JavaVersion; +import org.openrewrite.java.tree.J.CompilationUnit; import org.openrewrite.marker.BuildTool; import org.openrewrite.marker.Marker; import org.openrewrite.maven.MavenParser; @@ -56,6 +57,7 @@ import org.openrewrite.xml.tree.Xml.Document; import org.openrewrite.yaml.YamlParser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.ide.vscode.commons.rewrite.java.ORAstUtils; /** * Parse a Maven project on disk into a list of {@link org.openrewrite.SourceFile} including @@ -126,20 +128,22 @@ public class MavenProjectParser { sourceFiles.add(addProjectProvenance(maven, projectProvenance)); // List dependencies = downloadArtifacts(getResolvedPom(maven).getDependencies().get(Scope.Compile)); - javaParser.setSourceSet(MAIN); javaParser.setClasspath(dependencies); - sourceFiles.addAll(ListUtils.map(javaParser.parseInputs( - getJavaSources(getModel(maven).getRequested(), projectDirectory, ctx, parserInputProvider), projectDirectory, ctx), addProvenance(projectProvenance))); + List mainJavaSources = ListUtils.map(javaParser.parseInputs( + getJavaSources(getModel(maven).getRequested(), projectDirectory, ctx, parserInputProvider), projectDirectory, ctx), addProvenance(projectProvenance)); + JavaSourceSet mainSourceSet = ORAstUtils.addJavaSourceSet(mainJavaSources, MAIN, dependencies); + sourceFiles.addAll(mainJavaSources); //Resources in the src/main should also have the main source set attached to them. - parseResources(getResources(getModel(maven).getRequested(), projectDirectory, ctx, parserInputProvider), projectDirectory, sourceFiles, projectProvenance, javaParser.getSourceSet(ctx)); + parseResources(getResources(getModel(maven).getRequested(), projectDirectory, ctx, parserInputProvider), projectDirectory, sourceFiles, projectProvenance, mainSourceSet); // List testDependencies = downloadArtifacts(maven.getModel().getDependencies(Scope.Test)); - javaParser.setSourceSet(TEST); // javaParser.setClasspath(testDependencies); - sourceFiles.addAll(ListUtils.map(javaParser.parseInputs( - getTestJavaSources(getModel(maven).getRequested(), projectDirectory, ctx, parserInputProvider), projectDirectory, ctx), addProvenance(projectProvenance))); + List testJavaSources = ListUtils.map(javaParser.parseInputs( + getTestJavaSources(getModel(maven).getRequested(), projectDirectory, ctx, parserInputProvider), projectDirectory, ctx), addProvenance(projectProvenance)); + JavaSourceSet testSourceSet = ORAstUtils.addJavaSourceSet(testJavaSources, TEST, dependencies); + sourceFiles.addAll(testJavaSources); //Resources in the src/test should also have the test source set attached to them. - parseResources(getTestResources(getModel(maven).getRequested(), projectDirectory, ctx, parserInputProvider), projectDirectory, sourceFiles, projectProvenance, javaParser.getSourceSet(ctx)); + parseResources(getTestResources(getModel(maven).getRequested(), projectDirectory, ctx, parserInputProvider), projectDirectory, sourceFiles, projectProvenance, testSourceSet); } return sourceFiles; diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteCompilationUnitCache.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteCompilationUnitCache.java index f8089eaa9..5b2ffcd74 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteCompilationUnitCache.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteCompilationUnitCache.java @@ -24,15 +24,21 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.function.Function; +import java.util.stream.Collectors; import org.apache.commons.io.IOUtils; import org.eclipse.lsp4j.TextDocumentIdentifier; +import org.openrewrite.Tree; import org.openrewrite.Parser.Input; import org.openrewrite.java.JavaParser; +import org.openrewrite.java.marker.JavaSourceSet; import org.openrewrite.java.tree.J.CompilationUnit; +import org.openrewrite.java.tree.JavaType; +import org.openrewrite.java.tree.JavaType.FullyQualified; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.ide.vscode.boot.java.utils.DocumentContentProvider; +import org.springframework.ide.vscode.commons.java.IClasspathUtil; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver; @@ -65,6 +71,8 @@ public class RewriteCompilationUnitCache implements DocumentContentProvider, Dis private final Cache> projectToDocs; private final Cache javaParsers; + private final Cache> sourceSetClasspath; + public RewriteCompilationUnitCache(JavaProjectFinder projectFinder, SimpleLanguageServer server, ProjectObserver projectObserver) { // this.projectFinder = projectFinder; this.projectObserver = projectObserver; @@ -100,7 +108,17 @@ public class RewriteCompilationUnitCache implements DocumentContentProvider, Dis .build(); this.projectToDocs = CacheBuilder.newBuilder().build(); - this.javaParsers = CacheBuilder.newBuilder().build(); + this.javaParsers = CacheBuilder.newBuilder() + .removalListener(new RemovalListener() { + + @Override + public void onRemoval(RemovalNotification notification) { + sourceSetClasspath.invalidate(notification.getKey()); + } + }) + .build(); + + this.sourceSetClasspath = CacheBuilder.newBuilder().build(); this.documentService = server == null ? null : server.getTextDocumentService(); @@ -222,14 +240,13 @@ public class RewriteCompilationUnitCache implements DocumentContentProvider, Dis } private CompilationUnit doParse(IJavaProject project, URI uri) throws Exception { - boolean newParser = javaParsers.getIfPresent(project) == null; - JavaParser javaParser = null;; + boolean newParser = javaParsers.getIfPresent(project.getLocationUri()) == null; + JavaParser javaParser = null; try { logger.debug("Parsing CU {}", uri); javaParser = loadJavaParser(project); Path sourcePath = Paths.get(uri); - javaParser.setSourceSet(ORAstUtils.getSourceSetName(project, sourcePath)); Input input = new Input(sourcePath, () -> { try { @@ -241,6 +258,10 @@ public class RewriteCompilationUnitCache implements DocumentContentProvider, Dis List cus = ORAstUtils.parseInputs(javaParser, List.of(input), null); CompilationUnit cu = cus.get(0); + // Manually add source set + JavaSourceSet sourceSet = createSourceSet(project, ORAstUtils.getSourceSetName(project, sourcePath)); + cu = cu.withMarkers(cu.getMarkers().computeByType(sourceSet, (original, updated) -> updated)); + if (cu != null) { projectToDocs.get(project.getLocationUri(), () -> new HashSet<>()).add(uri); return cu; @@ -259,6 +280,20 @@ public class RewriteCompilationUnitCache implements DocumentContentProvider, Dis } } + private JavaSourceSet createSourceSet(IJavaProject project, String name) { + List fqNames; + try { + fqNames = sourceSetClasspath.get(project.getLocationUri(), () -> { + List classpath = IClasspathUtil.getAllBinaryRoots(project.getClasspath()).stream().map(f -> f.toPath()).collect(Collectors.toList()); + return JavaSourceSet.build("", classpath, null, false).getClasspath(); + }); + } catch (ExecutionException e) { + logger.error("", e); + fqNames = Collections.emptyList(); + } + return new JavaSourceSet(Tree.randomId(), name, fqNames); + } + /** * Does not need to be via callback - kept the same in order to keep the same API to replace JDT with Rewrite in distant future */ diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteReconciler.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteReconciler.java index 70e9d0bb7..833e7eefb 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteReconciler.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteReconciler.java @@ -28,7 +28,9 @@ import org.openrewrite.ExecutionContext; import org.openrewrite.InMemoryExecutionContext; import org.openrewrite.Parser; import org.openrewrite.Tree; +import org.openrewrite.internal.ListUtils; import org.openrewrite.java.JavaParser; +import org.openrewrite.java.marker.JavaSourceSet; import org.openrewrite.java.tree.J; import org.openrewrite.java.tree.J.CompilationUnit; import org.openrewrite.marker.Marker; @@ -157,11 +159,16 @@ public class RewriteReconciler implements JavaReconciler { } } - JavaParser javaParser = ORAstUtils.createJavaParser(project); - javaParser.setSourceSet(MavenProjectParser.MAIN); - allProblems.putAll(doReconcile(project, mainSources, javaParser, incrementProgress)); - javaParser.setSourceSet(MavenProjectParser.TEST); - allProblems.putAll(doReconcile(project, testSources, javaParser, incrementProgress)); + List classpath = IClasspathUtil.getAllBinaryRoots(project.getClasspath()).stream().map(f -> f.toPath()).collect(Collectors.toList()); + JavaParser javaParser = ORAstUtils.createJavaParser(() -> JavaParser.fromJavaVersion().classpath(classpath)); + + // Pass in source sets created from classpath. (Perhaps it is a good idea to have separate classpath and parsers for test and main, TBD) + // Perhaps it is even better to create empty classpath java source sets as reconcile step seem to only need name of the java source set + // Usually java source set classpath is required to figure out how to organize imports for sources + JavaSourceSet mainJavaSourceSet = JavaSourceSet.build(MavenProjectParser.MAIN, classpath, null, false); + JavaSourceSet testJavaSourceSet = new JavaSourceSet(Tree.randomId(), MavenProjectParser.TEST, mainJavaSourceSet.getClasspath()); + allProblems.putAll(doReconcile(project, mainSources, javaParser, mainJavaSourceSet, incrementProgress)); + allProblems.putAll(doReconcile(project, testSources, javaParser, testJavaSourceSet, incrementProgress)); long end = System.currentTimeMillis(); log.info("reconciling project (OpenRewrite): " + project.getElementName() + " - " + docs.size() + " done in " + (end - start) + "ms"); @@ -242,7 +249,7 @@ public class RewriteReconciler implements JavaReconciler { private static final int BATCH = 50; // Parse in batches and share the parser - private Map> doReconcile(IJavaProject project, List docs, JavaParser javaParser, Runnable incrementProgress) { + private Map> doReconcile(IJavaProject project, List docs, JavaParser javaParser, JavaSourceSet javaSourceSet, Runnable incrementProgress) { Map> allProblems = new HashMap<>(); if (javaParser != null && config.isRewriteReconcileEnabled()) { try { @@ -259,6 +266,8 @@ public class RewriteReconciler implements JavaReconciler { return new ByteArrayInputStream(d.get().getBytes()); })).collect(Collectors.toList()), source -> incrementProgress.run()); + cus = ListUtils.map(cus, cu -> cu.withMarkers(cu.getMarkers().computeByType(javaSourceSet, (original, updated) -> updated))); + /* * If exception occurs during parsing inputs the list of inputs would become shorter than the list of corresponding documents */