From dd1aaad43f39e5fb0950b25c7f1ffe207b413811 Mon Sep 17 00:00:00 2001 From: BoykoAlex Date: Fri, 11 Feb 2022 15:40:55 -0500 Subject: [PATCH] Remove JDT --- .../spring-boot-language-server/pom.xml | 4 +- .../boot/app/BootLanguageServerBootApp.java | 5 +- .../BootJavaLanguageServerComponents.java | 2 +- .../annotations/AnnotationHierarchies.java | 21 +- .../boot/java/beans/BeansSymbolProvider.java | 5 - .../handlers/BootJavaReferencesHandler.java | 86 ++- .../boot/java/handlers/ReferenceProvider.java | 12 +- .../boot/java/jdt/imports/ImportRewrite.java | 558 ------------------ .../DefaultJavaElementLocationProvider.java | 10 - .../BeanInjectedIntoHoverProvider.java | 2 - .../boot/java/livehover/LiveHoverUtils.java | 2 - .../ide/vscode/boot/java/utils/ASTUtils.java | 306 ---------- .../vscode/boot/java/utils/CUResolver.java | 361 ----------- .../boot/java/utils/CompilationUnitCache.java | 307 ---------- .../NameEnvironmentAwareASTRequestor.java | 63 -- .../java/utils/ORCompilationUnitCache.java | 5 +- .../java/utils/SpringIndexerJavaContext.java | 1 - .../SpringIndexerJavaDependencyTracker.java | 6 +- .../java/value/ValueCompletionProcessor.java | 14 +- .../ValuePropertyReferencesProvider.java | 35 +- .../boot/bootiful/PropertyEditorTestConf.java | 6 +- .../boot/bootiful/XmlBeansTestConf.java | 4 +- .../boot/java/utils/test/AstParserTest.java | 123 ---- .../utils/test/CompilationUnitCacheTest.java | 4 +- .../java/value/test/ValueCompletionTest.java | 4 +- .../ValueSpelExpressionValidationTest.java | 3 +- .../boot/test/DefinitionLinkAsserts.java | 154 +++-- 27 files changed, 163 insertions(+), 1940 deletions(-) delete mode 100644 headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/jdt/imports/ImportRewrite.java delete mode 100644 headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/ASTUtils.java delete mode 100644 headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/CUResolver.java delete mode 100644 headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/CompilationUnitCache.java delete mode 100644 headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/NameEnvironmentAwareASTRequestor.java delete mode 100644 headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/AstParserTest.java diff --git a/headless-services/spring-boot-language-server/pom.xml b/headless-services/spring-boot-language-server/pom.xml index 298c81400..2b6231485 100644 --- a/headless-services/spring-boot-language-server/pom.xml +++ b/headless-services/spring-boot-language-server/pom.xml @@ -99,12 +99,12 @@ commons-language-server ${dependencies.version} - + org.openrewrite rewrite-java ${rewrite-version} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguageServerBootApp.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguageServerBootApp.java index 7f82a279b..940ae382f 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguageServerBootApp.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguageServerBootApp.java @@ -35,7 +35,6 @@ import org.springframework.ide.vscode.boot.java.links.JdtJavaDocumentUriProvider import org.springframework.ide.vscode.boot.java.links.SourceLinkFactory; import org.springframework.ide.vscode.boot.java.links.SourceLinks; import org.springframework.ide.vscode.boot.java.livehover.v2.SpringProcessLiveDataProvider; -import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache; import org.springframework.ide.vscode.boot.java.utils.ORCompilationUnitCache; import org.springframework.ide.vscode.boot.java.utils.SymbolCache; import org.springframework.ide.vscode.boot.java.utils.SymbolCacheOnDisc; @@ -159,8 +158,8 @@ public class BootLanguageServerBootApp { return new ORCompilationUnitCache(params.projectFinder, server, params.projectObserver); } - @Bean CompilationUnitCache cuCache(SimpleLanguageServer server, BootLanguageServerParams params) { - return new CompilationUnitCache(params.projectFinder, server, params.projectObserver); + @Bean ORCompilationUnitCache cuCache(SimpleLanguageServer server, BootLanguageServerParams params) { + return new ORCompilationUnitCache(params.projectFinder, server, params.projectObserver); } @Bean SpringXMLCompletionEngine xmlCompletionEngine(SimpleLanguageServer server, JavaProjectFinder projectFinder, SpringSymbolIndex symbolIndex, BootJavaConfig config) { diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServerComponents.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServerComponents.java index 71f2135c9..b0e4848e5 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServerComponents.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServerComponents.java @@ -302,7 +302,7 @@ public class BootJavaLanguageServerComponents implements LanguageServerComponent providers.put(org.springframework.ide.vscode.boot.java.value.Constants.SPRING_VALUE, new ValuePropertyReferencesProvider(server)); - return new BootJavaReferencesHandler(this, projectFinder, providers); + return new BootJavaReferencesHandler(this, projectFinder, providers, cuCache); } protected BootJavaCodeLensEngine createCodeLensEngine(SpringSymbolIndex index) { diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/annotations/AnnotationHierarchies.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/annotations/AnnotationHierarchies.java index 08956a01c..084fc143c 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/annotations/AnnotationHierarchies.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/annotations/AnnotationHierarchies.java @@ -18,7 +18,6 @@ import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; -import org.eclipse.jdt.internal.compiler.problem.AbortCompilation; import org.openrewrite.java.tree.J.Annotation; import org.openrewrite.java.tree.JavaType.FullyQualified; import org.openrewrite.java.tree.TypeUtils; @@ -48,21 +47,15 @@ public abstract class AnnotationHierarchies { }; public static Collection getDirectSuperAnnotations(FullyQualified type) { - try { - List annotations = type.getAnnotations(); - if (annotations != null && !annotations.isEmpty()) { - ImmutableList.Builder superAnnotations = ImmutableList.builder(); - for (FullyQualified ab : annotations) { - if (!ignoreAnnotation(ab.getFullyQualifiedName())) { - superAnnotations.add(ab); - } + List annotations = type.getAnnotations(); + if (annotations != null && !annotations.isEmpty()) { + ImmutableList.Builder superAnnotations = ImmutableList.builder(); + for (FullyQualified ab : annotations) { + if (!ignoreAnnotation(ab.getFullyQualifiedName())) { + superAnnotations.add(ab); } - return superAnnotations.build(); } - } catch (AbortCompilation e) { - log.debug("compilation aborted ", e); - // ignore this, it is most likely caused by broken source code, a broken - // classpath, or some optional dependencies not being on the classpath + return superAnnotations.build(); } return ImmutableList.of(); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/beans/BeansSymbolProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/beans/BeansSymbolProvider.java index 17a6db6b9..d64401ddc 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/beans/BeansSymbolProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/beans/BeansSymbolProvider.java @@ -11,12 +11,10 @@ package org.springframework.ide.vscode.boot.java.beans; import java.util.Collection; -import java.util.List; import org.eclipse.lsp4j.Location; import org.eclipse.lsp4j.SymbolInformation; import org.eclipse.lsp4j.SymbolKind; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.tree.J; import org.openrewrite.java.tree.J.Annotation; import org.openrewrite.java.tree.J.ClassDeclaration; @@ -25,13 +23,11 @@ import org.openrewrite.java.tree.J.MethodDeclaration; import org.openrewrite.java.tree.J.Modifier; import org.openrewrite.java.tree.JavaType.FullyQualified; import org.openrewrite.java.tree.JavaType.Method; -import org.openrewrite.java.tree.JavaType.Parameterized; import org.openrewrite.java.tree.TypeUtils; import org.springframework.ide.vscode.boot.java.Annotations; import org.springframework.ide.vscode.boot.java.handlers.AbstractSymbolProvider; import org.springframework.ide.vscode.boot.java.handlers.EnhancedSymbolInformation; import org.springframework.ide.vscode.boot.java.handlers.SymbolAddOnInformation; -import org.springframework.ide.vscode.boot.java.utils.ASTUtils; import org.springframework.ide.vscode.boot.java.utils.CachedSymbol; import org.springframework.ide.vscode.boot.java.utils.FunctionUtils; import org.springframework.ide.vscode.boot.java.utils.ORAstUtils; @@ -42,7 +38,6 @@ import org.springframework.ide.vscode.commons.util.text.DocumentRegion; import org.springframework.ide.vscode.commons.util.text.TextDocument; import com.google.common.collect.ImmutableList; -import com.google.common.reflect.Parameter; import reactor.util.function.Tuple2; import reactor.util.function.Tuple3; diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaReferencesHandler.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaReferencesHandler.java index 289fd3bcd..de6e7c2c0 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaReferencesHandler.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaReferencesHandler.java @@ -11,24 +11,24 @@ package org.springframework.ide.vscode.boot.java.handlers; import java.io.File; +import java.net.URI; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.concurrent.CancellationException; import java.util.stream.Stream; -import org.eclipse.jdt.core.JavaCore; -import org.eclipse.jdt.core.dom.AST; -import org.eclipse.jdt.core.dom.ASTNode; -import org.eclipse.jdt.core.dom.ASTParser; -import org.eclipse.jdt.core.dom.Annotation; -import org.eclipse.jdt.core.dom.CompilationUnit; -import org.eclipse.jdt.core.dom.ITypeBinding; -import org.eclipse.jdt.core.dom.NodeFinder; import org.eclipse.lsp4j.Location; import org.eclipse.lsp4j.ReferenceParams; import org.eclipse.lsp4j.TextDocumentIdentifier; import org.eclipse.lsp4j.jsonrpc.CancelChecker; +import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.J.Annotation; +import org.openrewrite.java.tree.JavaType.FullyQualified; +import org.openrewrite.java.tree.TypeUtils; import org.springframework.ide.vscode.boot.java.BootJavaLanguageServerComponents; +import org.springframework.ide.vscode.boot.java.utils.ORAstUtils; +import org.springframework.ide.vscode.boot.java.utils.ORCompilationUnitCache; import org.springframework.ide.vscode.commons.java.IClasspath; import org.springframework.ide.vscode.commons.java.IClasspathUtil; import org.springframework.ide.vscode.commons.java.IJavaProject; @@ -46,11 +46,14 @@ public class BootJavaReferencesHandler implements ReferencesHandler { private JavaProjectFinder projectFinder; private BootJavaLanguageServerComponents server; private Map referenceProviders; + private ORCompilationUnitCache cuCache; - public BootJavaReferencesHandler(BootJavaLanguageServerComponents server, JavaProjectFinder projectFinder, Map specificProviders) { + public BootJavaReferencesHandler(BootJavaLanguageServerComponents server, JavaProjectFinder projectFinder, Map specificProviders, ORCompilationUnitCache cuCache) { this.server = server; this.projectFinder = projectFinder; this.referenceProviders = specificProviders; + this.cuCache = cuCache; + } @Override @@ -83,59 +86,38 @@ public class BootJavaReferencesHandler implements ReferencesHandler { } private List provideReferences(CancelChecker cancelToken, TextDocument document, int offset) throws Exception { - ASTParser parser = ASTParser.newParser(AST.JLS16); - Map options = JavaCore.getOptions(); - JavaCore.setComplianceOptions(JavaCore.VERSION_16, options); - parser.setCompilerOptions(options); - parser.setKind(ASTParser.K_COMPILATION_UNIT); - parser.setStatementsRecovery(true); - parser.setBindingsRecovery(true); - parser.setResolveBindings(true); + Optional project = projectFinder.find(document.getId()); + if (project.isPresent()) { + return cuCache.withCompilationUnit(project.get(), URI.create(document.getUri()), cu -> { + J node = ORAstUtils.findAstNodeAt(cu, offset); + + if (node != null) { + cancelToken.checkCanceled(); + return provideReferencesForAnnotation(cancelToken, node, offset, document); + } - String[] classpathEntries = getClasspathEntries(document); - String[] sourceEntries = new String[] {}; - parser.setEnvironment(classpathEntries, sourceEntries, null, true); - - String docURI = document.getUri(); - String unitName = docURI.substring(docURI.lastIndexOf("/")); - parser.setUnitName(unitName); - parser.setSource(document.get(0, document.getLength()).toCharArray()); - - cancelToken.checkCanceled(); - - CompilationUnit cu = (CompilationUnit) parser.createAST(null); - ASTNode node = NodeFinder.perform(cu, offset, 0); - - if (node != null) { - cancelToken.checkCanceled(); - return provideReferencesForAnnotation(cancelToken, node, offset, document); + return null; + }); + } - return null; } - private List provideReferencesForAnnotation(CancelChecker cancelToken, ASTNode node, int offset, TextDocument doc) { - Annotation annotation = null; - - while (node != null && !(node instanceof Annotation)) { - node = node.getParent(); - } - + private List provideReferencesForAnnotation(CancelChecker cancelToken, J node, int offset, TextDocument doc) { if (node != null) { - annotation = (Annotation) node; - ITypeBinding type = annotation.resolveTypeBinding(); - if (type != null) { + Annotation annotation = ORAstUtils.findNode(node, Annotation.class); - String qualifiedName = type.getQualifiedName(); - if (qualifiedName != null) { - ReferenceProvider provider = this.referenceProviders.get(qualifiedName); - if (provider != null) { - return provider.provideReferences(cancelToken, node, annotation, type, offset, doc); - } + if (annotation != null) { + FullyQualified type = TypeUtils.asFullyQualified(annotation.getType()); + if (type != null) { + ReferenceProvider provider = this.referenceProviders.get(type.getFullyQualifiedName()); + if (provider != null) { + return provider.provideReferences(cancelToken, node, annotation, type, offset, doc); + } } } } - + return null; } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/ReferenceProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/ReferenceProvider.java index 42d8a056c..29c833170 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/ReferenceProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/ReferenceProvider.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017, 2021 Pivotal, Inc. + * Copyright (c) 2017, 2022 Pivotal, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -12,11 +12,11 @@ package org.springframework.ide.vscode.boot.java.handlers; import java.util.List; -import org.eclipse.jdt.core.dom.ASTNode; -import org.eclipse.jdt.core.dom.Annotation; -import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.lsp4j.Location; import org.eclipse.lsp4j.jsonrpc.CancelChecker; +import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.J.Annotation; +import org.openrewrite.java.tree.JavaType.FullyQualified; import org.springframework.ide.vscode.commons.util.text.TextDocument; /** @@ -24,7 +24,7 @@ import org.springframework.ide.vscode.commons.util.text.TextDocument; */ public interface ReferenceProvider { - List provideReferences(CancelChecker cancelToken, ASTNode node, Annotation annotation, - ITypeBinding type, int offset, TextDocument doc); + List provideReferences(CancelChecker cancelToken, J node, Annotation annotation, + FullyQualified type, int offset, TextDocument doc); } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/jdt/imports/ImportRewrite.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/jdt/imports/ImportRewrite.java deleted file mode 100644 index 4f296061b..000000000 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/jdt/imports/ImportRewrite.java +++ /dev/null @@ -1,558 +0,0 @@ -/******************************************************************************* - * Derived from: - * org.eclipse.jdt.core.dom.rewrite.ImportRewrite - * - * for use in STS4, where IProject and ICompilationUnit are not available when parsing a Java source. - * - * Original license: - * - * Copyright (c) 2000, 2016 IBM Corporation and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * IBM Corporation - initial API and implementation - * John Glassmyer - import group sorting is broken - https://bugs.eclipse.org/430303 - * Lars Vogel - Contributions for - * Bug 473178 - *******************************************************************************/ -package org.springframework.ide.vscode.boot.java.jdt.imports; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.jdt.core.ICompilationUnit; -import org.eclipse.jdt.core.compiler.CharOperation; -import org.eclipse.jdt.core.dom.ASTParser; -import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; -import org.eclipse.jdt.core.dom.Comment; -import org.eclipse.jdt.core.dom.CompilationUnit; -import org.eclipse.jdt.core.dom.ImportDeclaration; -import org.eclipse.jdt.core.dom.PackageDeclaration; -import org.eclipse.jdt.core.dom.PrimitiveType; -import org.eclipse.jdt.core.dom.SimpleName; -import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits; -import org.springframework.ide.vscode.commons.util.text.IDocument; - - -/** - * The {@link ImportRewrite} helps updating imports following a import order and on-demand imports threshold as configured by a project. - *

- * The import rewrite is created on a compilation unit and collects references to types that are added or removed. When adding imports, e.g. using - * {@link #addImport(String)}, the import rewrite evaluates if the type can be imported and returns the a reference to the type that can be used in code. - * This reference is either unqualified if the import could be added, or fully qualified if the import failed due to a conflict with another element of the same name. - *

- *

- * On {@link #rewriteImports(IProgressMonitor)} the rewrite translates these descriptions into - * text edits that can then be applied to the original source. The rewrite infrastructure tries to generate minimal text changes and only - * works on the import statements. It is possible to combine the result of an import rewrite with the result of a {@link org.eclipse.jdt.core.dom.rewrite.ASTRewrite} - * as long as no import statements are modified by the AST rewrite. - *

- *

The options controlling the import order and on-demand thresholds are: - *

  • {@link #setImportOrder(String[])} specifies the import groups and their preferred order
  • - *
  • {@link #setOnDemandImportThreshold(int)} specifies the number of imports in a group needed for a on-demand import statement (star import)
  • - *
  • {@link #setStaticOnDemandImportThreshold(int)} specifies the number of static imports in a group needed for a on-demand import statement (star import)
  • - *
- * This class is not intended to be subclassed. - *

- * @since 3.2 - */ -@SuppressWarnings({ "rawtypes", "unchecked" }) -public final class ImportRewrite { - - /** - * A {@link ImportRewrite.ImportRewriteContext} can optionally be used in e.g. {@link ImportRewrite#addImport(String, ImportRewrite.ImportRewriteContext)} to - * give more information about the types visible in the scope. These types can be for example inherited inner types where it is - * unnecessary to add import statements for. - * - *

- *

- * This class can be implemented by clients. - *

- */ - public static abstract class ImportRewriteContext { - - /** - * Result constant signaling that the given element is know in the context. - */ - public final static int RES_NAME_FOUND= 1; - - /** - * Result constant signaling that the given element is not know in the context. - */ - public final static int RES_NAME_UNKNOWN= 2; - - /** - * Result constant signaling that the given element is conflicting with an other element in the context. - */ - public final static int RES_NAME_CONFLICT= 3; - - /** - * Result constant signaling that the given element must be imported explicitly (and must not be folded into - * an on-demand import or filtered as an implicit import). - * - * @since 3.11 - */ - public final static int RES_NAME_UNKNOWN_NEEDS_EXPLICIT_IMPORT= 4; - - /** - * Kind constant specifying that the element is a type import. - */ - public final static int KIND_TYPE= 1; - - /** - * Kind constant specifying that the element is a static field import. - */ - public final static int KIND_STATIC_FIELD= 2; - - /** - * Kind constant specifying that the element is a static method import. - */ - public final static int KIND_STATIC_METHOD= 3; - - /** - * Searches for the given element in the context and reports if the element is known ({@link #RES_NAME_FOUND}), - * unknown ({@link #RES_NAME_UNKNOWN}), unknown in the context but known to require an explicit import - * ({@link #RES_NAME_UNKNOWN_NEEDS_EXPLICIT_IMPORT}), or if its name conflicts ({@link #RES_NAME_CONFLICT}) - * with an other element. - * - * @param qualifier The qualifier of the element, can be package or the qualified name of a type - * @param name The simple name of the element; either a type, method or field name or * for on-demand imports. - * @param kind The kind of the element. Can be either {@link #KIND_TYPE}, {@link #KIND_STATIC_FIELD} or - * {@link #KIND_STATIC_METHOD}. Implementors should be prepared for new, currently unspecified kinds and return - * {@link #RES_NAME_UNKNOWN} by default. - * @return Returns the result of the lookup. Can be either {@link #RES_NAME_FOUND}, {@link #RES_NAME_UNKNOWN}, - * {@link #RES_NAME_CONFLICT}, or {@link #RES_NAME_UNKNOWN_NEEDS_EXPLICIT_IMPORT}. - */ - public abstract int findInContext(String qualifier, String name, int kind); - } - - private static final char STATIC_PREFIX= 's'; - private static final char NORMAL_PREFIX= 'n'; - - private final ImportRewriteContext defaultContext; - - private final CompilationUnit astRoot; - - private final boolean restoreExistingImports; - private final List existingImports; - - - private List addedImports; - - /** - * Simple names of non-static imports which must not be reduced into on-demand imports - * or filtered out as implicit. - */ - private Set typeExplicitSimpleNames; - - - private boolean filterImplicitImports; - private boolean useContextToFilterImplicitImports; - - - /** - * Creates an {@link ImportRewrite} from an AST ({@link CompilationUnit}). The AST has to be created from an - * {@link ICompilationUnit}, that means {@link ASTParser#setSource(ICompilationUnit)} has been used when creating the - * AST. If restoreExistingImports is true, all existing imports are kept, and new imports - * will be inserted at best matching locations. If restoreExistingImports is false, the - * existing imports will be removed and only the newly added imports will be created. - *

- * Note that this method is more efficient than using {@link #create(ICompilationUnit, boolean)} if an AST is already available. - *

- * @param astRoot the AST root node to create the imports for - * @param restoreExistingImports specifies if the existing imports should be kept or removed. - * @return the created import rewriter. - * @throws IllegalArgumentException thrown when the passed AST is null or was not created from a compilation unit. - */ - public static ImportRewrite create(CompilationUnit astRoot, boolean restoreExistingImports) { - if (astRoot == null) { - throw new IllegalArgumentException("AST must not be null"); //$NON-NLS-1$ - } - - List existingImport= null; - if (restoreExistingImports) { - existingImport= new ArrayList(); - List imports= astRoot.imports(); - for (int i= 0; i < imports.size(); i++) { - ImportDeclaration curr= (ImportDeclaration) imports.get(i); - StringBuffer buf= new StringBuffer(); - buf.append(curr.isStatic() ? STATIC_PREFIX : NORMAL_PREFIX).append(curr.getName().getFullyQualifiedName()); - if (curr.isOnDemand()) { - if (buf.length() > 1) - buf.append('.'); - buf.append('*'); - } - existingImport.add(buf.toString()); - } - } - return new ImportRewrite(astRoot, existingImport); - } - - private ImportRewrite(CompilationUnit astRoot, List existingImports) { - this.astRoot= astRoot; // might be null - if (existingImports != null) { - this.existingImports= existingImports; - this.restoreExistingImports= !existingImports.isEmpty(); - } else { - this.existingImports= new ArrayList(); - this.restoreExistingImports= false; - } - this.filterImplicitImports= true; - // consider that no contexts are used - this.useContextToFilterImplicitImports = false; - - this.defaultContext= new ImportRewriteContext() { - @Override - public int findInContext(String qualifier, String name, int kind) { - return findInImports(qualifier, name, kind); - } - }; - this.addedImports= new ArrayList<>(); - this.typeExplicitSimpleNames = new HashSet<>(); - } - - /** - * Returns the default rewrite context that only knows about the imported types. Clients - * can write their own context and use the default context for the default behavior. - * @return the default import rewrite context. - */ - public ImportRewriteContext getDefaultImportRewriteContext() { - return this.defaultContext; - } - - /** - * Specifies that implicit imports (for types in java.lang, types in the same package as the rewrite - * compilation unit, and types in the compilation unit's main type) should not be created, except if necessary to - * resolve an on-demand import conflict. - *

- * The filter is enabled by default. - *

- *

- * Note: {@link #setUseContextToFilterImplicitImports(boolean)} can be used to filter implicit imports - * when a context is used. - *

- * - * @param filterImplicitImports - * if true, implicit imports will be filtered - * - * @see #setUseContextToFilterImplicitImports(boolean) - */ - public void setFilterImplicitImports(boolean filterImplicitImports) { - this.filterImplicitImports= filterImplicitImports; - } - - /** - * Sets whether a context should be used to properly filter implicit imports. - *

- * By default, the option is disabled to preserve pre-3.6 behavior. - *

- *

- * When this option is set, the context passed to the addImport*(...) methods is used to determine - * whether an import can be filtered because the type is implicitly visible. Note that too many imports - * may be kept if this option is set and addImport*(...) methods are called without a context. - *

- * - * @param useContextToFilterImplicitImports the given setting - * - * @see #setFilterImplicitImports(boolean) - * @since 3.6 - */ - public void setUseContextToFilterImplicitImports(boolean useContextToFilterImplicitImports) { - this.useContextToFilterImplicitImports = useContextToFilterImplicitImports; - } - - private static int compareImport(char prefix, String qualifier, String name, String curr) { - if (curr.charAt(0) != prefix || !curr.endsWith(name)) { - return ImportRewriteContext.RES_NAME_UNKNOWN; - } - - curr= curr.substring(1); // remove the prefix - - if (curr.length() == name.length()) { - if (qualifier.length() == 0) { - return ImportRewriteContext.RES_NAME_FOUND; - } - return ImportRewriteContext.RES_NAME_CONFLICT; - } - // at this place: curr.length > name.length - - int dotPos= curr.length() - name.length() - 1; - if (curr.charAt(dotPos) != '.') { - return ImportRewriteContext.RES_NAME_UNKNOWN; - } - if (qualifier.length() != dotPos || !curr.startsWith(qualifier)) { - return ImportRewriteContext.RES_NAME_CONFLICT; - } - return ImportRewriteContext.RES_NAME_FOUND; - } - - /** - * Not API, package visibility as accessed from an anonymous type - */ - /* package */ final int findInImports(String qualifier, String name, int kind) { - boolean allowAmbiguity= (kind == ImportRewriteContext.KIND_STATIC_METHOD) || (name.length() == 1 && name.charAt(0) == '*'); - List imports= this.existingImports; - char prefix= (kind == ImportRewriteContext.KIND_TYPE) ? NORMAL_PREFIX : STATIC_PREFIX; - - for (int i= imports.size() - 1; i >= 0 ; i--) { - String curr= (String) imports.get(i); - int res= compareImport(prefix, qualifier, name, curr); - if (res != ImportRewriteContext.RES_NAME_UNKNOWN) { - if (!allowAmbiguity || res == ImportRewriteContext.RES_NAME_FOUND) { - if (prefix != STATIC_PREFIX) { - return res; - } - } - } - } - - String packageName = getPackageName(); - if (kind == ImportRewriteContext.KIND_TYPE) { - if (this.filterImplicitImports && this.useContextToFilterImplicitImports) { - - // [STS4] No ICompilationUnit available as there is no class file or associated IJavaElement available for the source - -// String mainTypeSimpleName= JavaCore.removeJavaLikeExtension(this.compilationUnit.getElementName()); -// String mainTypeName= Util.concatenateName(packageName, mainTypeSimpleName, '.'); -// if (qualifier.equals(packageName) -// || mainTypeName.equals(Util.concatenateName(qualifier, name, '.'))) { -// return ImportRewriteContext.RES_NAME_FOUND; -// } - - if (this.astRoot != null) { - List types = this.astRoot.types(); - int nTypes = types.size(); - for (int i = 0; i < nTypes; i++) { - AbstractTypeDeclaration type = types.get(i); - SimpleName simpleName = type.getName(); - if (simpleName.getIdentifier().equals(name)) { - return qualifier.equals(packageName) - ? ImportRewriteContext.RES_NAME_FOUND - : ImportRewriteContext.RES_NAME_CONFLICT; - } - } - } else { - - // [STS4] No ICompilationUnit available as there is no class file or associated IJavaElement available for the source -// try { -// IType[] types = this.compilationUnit.getTypes(); -// int nTypes = types.length; -// for (int i = 0; i < nTypes; i++) { -// IType type = types[i]; -// String typeName = type.getElementName(); -// if (typeName.equals(name)) { -// return qualifier.equals(packageName) -// ? ImportRewriteContext.RES_NAME_FOUND -// : ImportRewriteContext.RES_NAME_CONFLICT; -// } -// } -// } catch (JavaModelException e) { -// // don't want to throw an exception here -// } - } - } - } - - return ImportRewriteContext.RES_NAME_UNKNOWN; - } - - private String getPackageName() { - // [STS4] No ICompilationUnit available as there is no class file or associated IJavaElement available for the source -// this.compilationUnit.getParent().getElementName(); - return this.astRoot.getPackage().getName().getFullyQualifiedName(); - } - - /** - * Adds a new import to the rewriter's record and returns a type reference that can be used - * in the code. The type binding can only be an array or non-generic type. - *

- * No imports are added for types that are already known. If a import for a type is recorded to be removed, this record is discarded instead. - *

- *

- * The content of the compilation unit itself is actually not modified - * in any way by this method; rather, the rewriter just records that a new import has been added. - *

- * @param qualifiedTypeName the qualified type name of the type to be added - * @param context an optional context that knows about types visible in the current scope or null - * to use the default context only using the available imports. - * @return a type reference for the given qualified type name. The type name is a simple name if an import could be used, - * or else a qualified name if an import conflict prevented an import. - */ - public String addImport(String qualifiedTypeName, ImportRewriteContext context) { - int angleBracketOffset= qualifiedTypeName.indexOf('<'); - if (angleBracketOffset != -1) { - return internalAddImport(qualifiedTypeName.substring(0, angleBracketOffset), context) + qualifiedTypeName.substring(angleBracketOffset); - } - int bracketOffset= qualifiedTypeName.indexOf('['); - if (bracketOffset != -1) { - return internalAddImport(qualifiedTypeName.substring(0, bracketOffset), context) + qualifiedTypeName.substring(bracketOffset); - } - return internalAddImport(qualifiedTypeName, context); - } - - /** - * Adds a new import to the rewriter's record and returns a type reference that can be used - * in the code. The type binding can only be an array or non-generic type. - *

- * No imports are added for types that are already known. If a import for a type is recorded to be removed, this record is discarded instead. - *

- *

- * The content of the compilation unit itself is actually not modified - * in any way by this method; rather, the rewriter just records that a new import has been added. - *

- * @param qualifiedTypeName the qualified type name of the type to be added - * @return a type reference for the given qualified type name. The type name is a simple name if an import could be used, - * or else a qualified name if an import conflict prevented an import. - */ - public String addImport(String qualifiedTypeName) { - return addImport(qualifiedTypeName, this.defaultContext); - } - - - - private String internalAddImport(String fullTypeName, ImportRewriteContext context) { - int idx= fullTypeName.lastIndexOf('.'); - String typeContainerName, typeName; - if (idx != -1) { - typeContainerName= fullTypeName.substring(0, idx); - typeName= fullTypeName.substring(idx + 1); - } else { - typeContainerName= ""; //$NON-NLS-1$ - typeName= fullTypeName; - } - - if (typeContainerName.length() == 0 && PrimitiveType.toCode(typeName) != null) { - return fullTypeName; - } - - if (context == null) - context= this.defaultContext; - - int res= context.findInContext(typeContainerName, typeName, ImportRewriteContext.KIND_TYPE); - if (res == ImportRewriteContext.RES_NAME_CONFLICT) { - return fullTypeName; - } - if (res == ImportRewriteContext.RES_NAME_UNKNOWN) { - addEntry(NORMAL_PREFIX + fullTypeName); - } - if (res == ImportRewriteContext.RES_NAME_UNKNOWN_NEEDS_EXPLICIT_IMPORT) { - addEntry(NORMAL_PREFIX + fullTypeName); - this.typeExplicitSimpleNames.add(typeName); - } - return typeName; - } - - private void addEntry(String entry) { - this.existingImports.add(entry); - - this.addedImports.add(entry); - } - - - - /** - * Returns all non-static imports that are recorded to be added. - * - * @return the imports recorded to be added. - */ - public String[] getAddedImports() { - return filterFromList(this.addedImports, NORMAL_PREFIX); - } - - /** - * Returns true if imports have been recorded to be added or removed. - * @return boolean returns if any changes to imports have been recorded. - */ - public boolean hasRecordedChanges() { - return !this.restoreExistingImports - || !this.addedImports.isEmpty(); - } - - private static String[] filterFromList(List imports, char prefix) { - if (imports == null) { - return CharOperation.NO_STRINGS; - } - List res= new ArrayList<>(); - for (String curr : imports) { - if (prefix == curr.charAt(0)) { - res.add(curr.substring(1)); - } - } - return res.toArray(new String[res.size()]); - } - - - /** - * Reads the positions of each existing import declaration along with any associated comments, - * and returns these in a list whose iteration order reflects the existing order of the imports - * in the compilation unit. - */ - private int getAddedImportsInsertLocation() { - List importDeclarations = astRoot.imports(); - - if (importDeclarations == null) { - importDeclarations = Collections.emptyList(); - } - - List comments = astRoot.getCommentList(); - - int currentCommentIndex = 0; - - // Skip over package and file header comments (see https://bugs.eclipse.org/121428). - ImportDeclaration firstImport = importDeclarations.get(0); - PackageDeclaration packageDeclaration = astRoot.getPackage(); - int firstImportStartPosition = packageDeclaration == null - ? firstImport.getStartPosition() - : astRoot.getExtendedStartPosition(packageDeclaration) - + astRoot.getExtendedLength(packageDeclaration); - while (currentCommentIndex < comments.size() - && comments.get(currentCommentIndex).getStartPosition() < firstImportStartPosition) { - currentCommentIndex++; - } - - int previousExtendedEndPosition = -1; - for (ImportDeclaration currentImport : importDeclarations) { - int extendedEndPosition = astRoot.getExtendedStartPosition(currentImport) - + astRoot.getExtendedLength(currentImport); - - int commentAfterImportIndex = currentCommentIndex; - while (commentAfterImportIndex < comments.size() - && comments.get(commentAfterImportIndex).getStartPosition() < extendedEndPosition) { - commentAfterImportIndex++; - } - - - currentCommentIndex = commentAfterImportIndex; - previousExtendedEndPosition = extendedEndPosition; - } - - return previousExtendedEndPosition; - } - - public DocumentEdits createEdit(IDocument doc) { - DocumentEdits edits = null; - StringBuffer buffer = new StringBuffer(); - - String[] createdImprts = getAddedImports(); - if (createdImprts != null && createdImprts.length >0) { - edits =new DocumentEdits(doc, false); - buffer.append('\n'); - for (String imp : createdImprts) { - buffer.append("import "); - buffer.append(imp); - buffer.append(';'); - buffer.append('\n'); - } - edits.insert(getAddedImportsInsertLocation(), buffer.toString()); - - } - return edits; - } -} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/links/DefaultJavaElementLocationProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/links/DefaultJavaElementLocationProvider.java index bb7927fea..4bdeb4efe 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/links/DefaultJavaElementLocationProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/links/DefaultJavaElementLocationProvider.java @@ -15,24 +15,14 @@ import java.net.URL; import java.util.Optional; import java.util.concurrent.atomic.AtomicReference; -import org.eclipse.jdt.core.dom.ASTVisitor; -import org.eclipse.jdt.core.dom.EnumConstantDeclaration; -import org.eclipse.jdt.core.dom.EnumDeclaration; -import org.eclipse.jdt.core.dom.MethodDeclaration; -import org.eclipse.jdt.core.dom.SimpleName; -import org.eclipse.jdt.core.dom.TypeDeclaration; import org.eclipse.lsp4j.Location; import org.eclipse.lsp4j.Position; import org.eclipse.lsp4j.Range; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.ide.vscode.boot.java.utils.ORCompilationUnitCache; -import org.springframework.ide.vscode.commons.java.IField; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.java.IMember; -import org.springframework.ide.vscode.commons.java.IMethod; -import org.springframework.ide.vscode.commons.java.IType; -import org.springframework.ide.vscode.commons.util.text.TextDocument; public class DefaultJavaElementLocationProvider implements JavaElementLocationProvider { diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/BeanInjectedIntoHoverProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/BeanInjectedIntoHoverProvider.java index 3a2a69520..37e7c8a6d 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/BeanInjectedIntoHoverProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/BeanInjectedIntoHoverProvider.java @@ -14,7 +14,6 @@ import java.util.Collections; import java.util.List; import java.util.Optional; -import org.eclipse.jdt.core.dom.SingleVariableDeclaration; import org.eclipse.lsp4j.CodeLens; import org.eclipse.lsp4j.Hover; import org.eclipse.lsp4j.Range; @@ -28,7 +27,6 @@ import org.springframework.ide.vscode.boot.java.autowired.AutowiredHoverProvider import org.springframework.ide.vscode.boot.java.links.SourceLinks; import org.springframework.ide.vscode.boot.java.livehover.v2.LiveBean; import org.springframework.ide.vscode.boot.java.livehover.v2.SpringProcessLiveData; -import org.springframework.ide.vscode.boot.java.utils.ASTUtils; import org.springframework.ide.vscode.boot.java.utils.ORAstUtils; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.util.Optionals; diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/LiveHoverUtils.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/LiveHoverUtils.java index a507195fc..e6df47b3d 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/LiveHoverUtils.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/LiveHoverUtils.java @@ -19,7 +19,6 @@ import java.util.Optional; import java.util.regex.Pattern; import java.util.stream.Collectors; -import org.eclipse.jdt.core.dom.SingleVariableDeclaration; import org.eclipse.lsp4j.CodeLens; import org.eclipse.lsp4j.Command; import org.eclipse.lsp4j.Range; @@ -33,7 +32,6 @@ import org.springframework.ide.vscode.boot.java.links.SourceLinks; import org.springframework.ide.vscode.boot.java.livehover.v2.LiveBean; import org.springframework.ide.vscode.boot.java.livehover.v2.LiveBeansModel; import org.springframework.ide.vscode.boot.java.livehover.v2.SpringProcessLiveData; -import org.springframework.ide.vscode.boot.java.utils.ASTUtils; import org.springframework.ide.vscode.boot.java.utils.ORAstUtils; import org.springframework.ide.vscode.boot.java.utils.SpringResource; import org.springframework.ide.vscode.commons.java.IJavaProject; diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/ASTUtils.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/ASTUtils.java deleted file mode 100644 index 2e792c8a7..000000000 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/ASTUtils.java +++ /dev/null @@ -1,306 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2017, 2019 Pivotal, Inc. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Pivotal, Inc. - initial API and implementation - *******************************************************************************/ -package org.springframework.ide.vscode.boot.java.utils; - -import java.util.Collection; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.function.Consumer; -import java.util.stream.Stream; - -import org.eclipse.jdt.core.dom.ASTNode; -import org.eclipse.jdt.core.dom.Annotation; -import org.eclipse.jdt.core.dom.ArrayInitializer; -import org.eclipse.jdt.core.dom.Expression; -import org.eclipse.jdt.core.dom.IBinding; -import org.eclipse.jdt.core.dom.ITypeBinding; -import org.eclipse.jdt.core.dom.IVariableBinding; -import org.eclipse.jdt.core.dom.MemberValuePair; -import org.eclipse.jdt.core.dom.MethodDeclaration; -import org.eclipse.jdt.core.dom.Name; -import org.eclipse.jdt.core.dom.NormalAnnotation; -import org.eclipse.jdt.core.dom.QualifiedName; -import org.eclipse.jdt.core.dom.SimpleName; -import org.eclipse.jdt.core.dom.SingleMemberAnnotation; -import org.eclipse.jdt.core.dom.StringLiteral; -import org.eclipse.jdt.core.dom.TypeDeclaration; -import org.eclipse.lsp4j.Range; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.ide.vscode.boot.java.Annotations; -import org.springframework.ide.vscode.commons.util.CollectorUtil; -import org.springframework.ide.vscode.commons.util.text.DocumentRegion; -import org.springframework.ide.vscode.commons.util.text.TextDocument; - -import com.google.common.collect.ImmutableList; - -public class ASTUtils { - - private static final Logger log = LoggerFactory.getLogger(ASTUtils.class); - - public static DocumentRegion nameRegion(TextDocument doc, Annotation annotation) { - int start = annotation.getTypeName().getStartPosition(); - int end = start + annotation.getTypeName().getLength(); - if (doc.getSafeChar(start - 1) == '@') { - start--; - } - return new DocumentRegion(doc, start, end); - } - - public static Optional nameRange(TextDocument doc, Annotation annotation) { - try { - return Optional.of(nameRegion(doc, annotation).asRange()); - } catch (Exception e) { - log.error("", e); - return Optional.empty(); - } - } - - public static DocumentRegion stringRegion(TextDocument doc, StringLiteral node) { - DocumentRegion nodeRegion = nodeRegion(doc, node); - if (nodeRegion.startsWith("\"")) { - nodeRegion = nodeRegion.subSequence(1); - } - if (nodeRegion.endsWith("\"")) { - nodeRegion = nodeRegion.subSequence(0, nodeRegion.getLength()-1); - } - return nodeRegion; - } - - - public static DocumentRegion nodeRegion(TextDocument doc, ASTNode node) { - int start = node.getStartPosition(); - int end = start + node.getLength(); - return new DocumentRegion(doc, start, end); - } - - public static Optional getAttribute(Annotation annotation, String name) { - if (annotation != null) { - try { - if (annotation.isSingleMemberAnnotation() && name.equals("value")) { - SingleMemberAnnotation sma = (SingleMemberAnnotation) annotation; - return Optional.ofNullable(sma.getValue()); - } else if (annotation.isNormalAnnotation()) { - NormalAnnotation na = (NormalAnnotation) annotation; - Object attributeObjs = na.getStructuralProperty(NormalAnnotation.VALUES_PROPERTY); - if (attributeObjs instanceof List) { - for (Object atrObj : (List)attributeObjs) { - if (atrObj instanceof MemberValuePair) { - MemberValuePair mvPair = (MemberValuePair) atrObj; - if (name.equals(mvPair.getName().getIdentifier())) { - return Optional.ofNullable(mvPair.getValue()); - } - } - } - } - } - } catch (Exception e) { - log.error("", e); - } - } - return Optional.empty(); - } - - /** - * For case where a expression can be either a String or a array of Strings and - * we are interested in the first element of the array. (I.e. typical case - * when annotation attribute is of type String[] (because Java allows using a single - * value as a convenient syntax for writing an array of length 1 in that case. - */ - public static Optional getFirstString(Expression exp) { - if (exp instanceof StringLiteral) { - return Optional.ofNullable(getLiteralValue((StringLiteral) exp)); - } else if (exp instanceof ArrayInitializer) { - ArrayInitializer array = (ArrayInitializer) exp; - Object objs = array.getStructuralProperty(ArrayInitializer.EXPRESSIONS_PROPERTY); - if (objs instanceof List) { - List list = (List) objs; - if (!list.isEmpty()) { - Object firstObj = list.get(0); - if (firstObj instanceof Expression) { - return getFirstString((Expression) firstObj); - } - } - } - } - return Optional.empty(); - } - - public static TypeDeclaration findDeclaringType(ASTNode node) { - while (node != null && !(node instanceof TypeDeclaration)) { - node = node.getParent(); - } - - return node != null ? (TypeDeclaration) node : null; - } - - public static boolean hasExactlyOneConstructor(TypeDeclaration typeDecl) { - boolean oneFound = false; - MethodDeclaration[] methods = typeDecl.getMethods(); - for (MethodDeclaration methodDeclaration : methods) { - if (methodDeclaration.isConstructor()) { - if (oneFound) { - return false; - } else { - oneFound = true; - } - } - } - return oneFound; - } - - public static MethodDeclaration getAnnotatedMethod(Annotation annotation) { - ASTNode parent = annotation.getParent(); - if (parent instanceof MethodDeclaration) { - return (MethodDeclaration)parent; - } - return null; - } - - public static TypeDeclaration getAnnotatedType(Annotation annotation) { - ASTNode parent = annotation.getParent(); - if (parent instanceof TypeDeclaration) { - return (TypeDeclaration)parent; - } - return null; - } - - public static String getLiteralValue(StringLiteral node) { - synchronized (node.getAST()) { - return node.getLiteralValue(); - } - } - - public static String getExpressionValueAsString(Expression exp, Consumer dependencies) { - if (exp instanceof StringLiteral) { - return getLiteralValue((StringLiteral) exp); - } else if (exp instanceof Name) { - IBinding binding = ((Name) exp).resolveBinding(); - if (binding != null && binding.getKind() == IBinding.VARIABLE) { - IVariableBinding varBinding = (IVariableBinding) binding; - ITypeBinding klass = varBinding.getDeclaringClass(); - if (klass!=null) { - dependencies.accept(klass); - - - } - Object constValue = varBinding.getConstantValue(); - if (constValue != null) { - return constValue.toString(); - } - } - if (exp instanceof QualifiedName) { - return getExpressionValueAsString(((QualifiedName) exp).getName(), dependencies); - } - else if (exp instanceof SimpleName) { - return ((SimpleName) exp).getIdentifier(); - } - else { - return null; - } - } else { - return null; - } - } - - @SuppressWarnings("unchecked") - public static String[] getExpressionValueAsArray(Expression exp, Consumer dependencies) { - if (exp instanceof ArrayInitializer) { - ArrayInitializer array = (ArrayInitializer) exp; - return ((List) array.expressions()).stream().map(e -> getExpressionValueAsString(e, dependencies)) - .filter(Objects::nonNull).toArray(String[]::new); - } else { - String rm = getExpressionValueAsString(exp, dependencies); - if (rm != null) { - return new String[] { rm }; - } - } - return null; - } - - @SuppressWarnings("unchecked") - public static List getExpressionValueAsListOfLiterals(Expression exp) { - if (exp instanceof ArrayInitializer) { - ArrayInitializer array = (ArrayInitializer) exp; - return ((List) array.expressions()).stream() - .flatMap(e -> e instanceof StringLiteral - ? Stream.of((StringLiteral)e) - : Stream.empty() - ) - .collect(CollectorUtil.toImmutableList()); - } else if (exp instanceof StringLiteral){ - return ImmutableList.of((StringLiteral)exp); - } - return ImmutableList.of(); - } - - - public static Collection getAnnotations(TypeDeclaration declaringType) { - Object modifiersObj = declaringType.getStructuralProperty(TypeDeclaration.MODIFIERS2_PROPERTY); - if (modifiersObj instanceof List) { - ImmutableList.Builder annotations = ImmutableList.builder(); - for (Object node : (List)modifiersObj) { - if (node instanceof Annotation) { - annotations.add((Annotation) node); - } - } - return annotations.build(); - } - return ImmutableList.of(); - } - - - public static String getAnnotationType(Annotation annotation) { - ITypeBinding binding = annotation.resolveTypeBinding(); - if (binding!=null) { - return binding.getQualifiedName(); - } - return null; - } - - public static Optional beanId(List modifiers) { - return modifiers.stream() - .filter(m -> m instanceof SingleMemberAnnotation) - .map(m -> (SingleMemberAnnotation) m) - .filter(m -> { - ITypeBinding typeBinding = m.resolveTypeBinding(); - if (typeBinding != null) { - return Annotations.QUALIFIER.equals(typeBinding.getQualifiedName()); - } - return false; - }) - .findFirst() - .map(a -> a.getValue()) - .filter(e -> e != null) - .map(e -> e.resolveConstantExpressionValue()) - .filter(o -> o instanceof String) - .map(o -> (String) o); - } - - public static Annotation getBeanAnnotation(MethodDeclaration method) { - List modifiers = method.modifiers(); - for (Object modifier : modifiers) { - if (modifier instanceof Annotation) { - Annotation annotation = (Annotation) modifier; - ITypeBinding typeBinding = annotation.resolveTypeBinding(); - if (typeBinding != null) { - String fqName = typeBinding.getQualifiedName(); - if (Annotations.BEAN.equals(fqName)) { - return annotation; - } - } - } - } - return null; - } - -} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/CUResolver.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/CUResolver.java deleted file mode 100644 index 395501302..000000000 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/CUResolver.java +++ /dev/null @@ -1,361 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2019 Pivotal, Inc. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Pivotal, Inc. - initial API and implementation - *******************************************************************************/ -package org.springframework.ide.vscode.boot.java.utils; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.List; -import java.util.Map; - -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.NullProgressMonitor; -import org.eclipse.jdt.core.ICompilationUnit; -import org.eclipse.jdt.core.JavaModelException; -import org.eclipse.jdt.core.WorkingCopyOwner; -import org.eclipse.jdt.core.dom.ASTParser; -import org.eclipse.jdt.core.dom.CompilationUnit; -import org.eclipse.jdt.internal.compiler.ICompilerRequestor; -import org.eclipse.jdt.internal.compiler.IErrorHandlingPolicy; -import org.eclipse.jdt.internal.compiler.IProblemFactory; -import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; -import org.eclipse.jdt.internal.compiler.batch.FileSystem.Classpath; -import org.eclipse.jdt.internal.compiler.env.INameEnvironment; -import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; -import org.eclipse.jdt.internal.core.CancelableProblemFactory; -import org.eclipse.jdt.internal.core.INameEnvironmentWithProgress; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.base.Supplier; - -/** - * Reflection based implementation of JDT package public CompilationUnitResolver. - * It is used to resolve the {@link CompilationUnitDeclaration} - * - * @author Alex Boyko - * - */ -class CUResolver { - - private static final Logger log = LoggerFactory.getLogger(CUResolver.class); - - private static final Supplier> BINDING_TABLES_CLASS = () -> { - try { - return Class.forName("org.eclipse.jdt.core.dom.DefaultBindingResolver$BindingTables"); - } catch (ClassNotFoundException e) { - log.error("{}", e); - return null; - } - }; - - private static final Supplier> BINDING_TABLES_CONSTRUCTOR = () -> { - try { - Class clazz = BINDING_TABLES_CLASS.get(); - if (clazz != null) { - Constructor ctor = clazz.getDeclaredConstructor(); - ctor.setAccessible(true); - return ctor; - } - } catch (NoSuchMethodException | SecurityException e) { - log.error("{}", e); - } - return null; - }; - - private static final Supplier GET_CLASSPATH_METHOD = () -> { - try { - Method getClasspathMethod = ASTParser.class.getDeclaredMethod("getClasspath"); - getClasspathMethod.setAccessible(true); - return getClasspathMethod; - } catch (NoSuchMethodException | SecurityException e) { - log.error("{}", e); - return null; - } - }; - - private static final Supplier> COMPILATION_UNIT_RESOLVER_CLASS = () -> { - try { - return Class.forName("org.eclipse.jdt.core.dom.CompilationUnitResolver"); - } catch (ClassNotFoundException e) { - log.error("{}", e); - return null; - } - - }; - - private static final Supplier> COMPILATION_UNIT_RESOLVER_CONSTRUCTOR = () -> { - try { - Class clazz = COMPILATION_UNIT_RESOLVER_CLASS.get(); - if (clazz != null) { - Constructor ctor = clazz.getDeclaredConstructor(INameEnvironment.class, IErrorHandlingPolicy.class, - CompilerOptions.class, ICompilerRequestor.class, IProblemFactory.class, IProgressMonitor.class, - boolean.class); - ctor.setAccessible(true); - return ctor; - } - } catch (NoSuchMethodException | SecurityException e) { - log.error("{}", e); - } - return null; - }; - - private static final Supplier> LOOKUP_ENVIRONMENT_CONSTRUCTOR = () -> { - Class nameEnvironmentWithProgressClass; - try { - nameEnvironmentWithProgressClass = Class.forName("org.eclipse.jdt.core.dom.NameEnvironmentWithProgress"); - Constructor lookupCtor = nameEnvironmentWithProgressClass.getDeclaredConstructor( - Classpath[].class, - String[].class, - IProgressMonitor.class - ); - lookupCtor.setAccessible(true); - return lookupCtor; - } catch (ClassNotFoundException | NoSuchMethodException | SecurityException e) { - log.error("{}", e); - return null; - } - }; - - private static final Supplier GET_HANDLER_POLICY_METHOD = () -> { - try { - Class clazz = COMPILATION_UNIT_RESOLVER_CLASS.get(); - if (clazz != null) { - Method handlerPolicyMethod = clazz.getDeclaredMethod("getHandlingPolicy"); - handlerPolicyMethod.setAccessible(true); - return handlerPolicyMethod; - } - } catch (NoSuchMethodException | SecurityException e) { - log.error("{}", e); - } - return null; - }; - - private static final Supplier GET_REQUESTOR_METHOD = () -> { - try { - Class clazz = COMPILATION_UNIT_RESOLVER_CLASS.get(); - if (clazz != null) { - Method getRequestorMethod = clazz.getDeclaredMethod("getRequestor"); - getRequestorMethod.setAccessible(true); - return getRequestorMethod; - } - } catch (NoSuchMethodException | SecurityException e) { - log.error("{}", e); - } - return null; - }; - - private static final Supplier GET_COMPILER_OPTIONS_METHOD = () -> { - try { - Class clazz = COMPILATION_UNIT_RESOLVER_CLASS.get(); - if (clazz != null) { - Method compilerOptionsMethod = clazz.getDeclaredMethod("getCompilerOptions", Map.class, boolean.class); - compilerOptionsMethod.setAccessible(true); - return compilerOptionsMethod; - } - } catch (NoSuchMethodException | SecurityException e) { - log.error("{}", e); - } - return null; - }; - - private static final Supplier> NODE_SEARCHER_CLASS = () -> { - try { - return Class.forName("org.eclipse.jdt.core.dom.NodeSearcher"); - } catch (ClassNotFoundException e) { - log.error("{}", e); - return null; - } - }; - - private static final Supplier PARSE_METHOD = () -> { - try { - Class clazz = COMPILATION_UNIT_RESOLVER_CLASS.get(); - Class nodeSearcherClass = NODE_SEARCHER_CLASS.get(); - if (clazz != null && nodeSearcherClass != null) { - Method parseMethod = clazz.getDeclaredMethod("parse", - org.eclipse.jdt.internal.compiler.env.ICompilationUnit.class, - nodeSearcherClass, - Map.class, - int.class); - parseMethod.setAccessible(true); - return parseMethod; - } - } catch (NoSuchMethodException | SecurityException e) { - log.error("{}", e); - } - return null; - }; - - private static final Supplier RESOLVE_METHOD = () -> { - try { - Class clazz = COMPILATION_UNIT_RESOLVER_CLASS.get(); - Class nodeSearcherClass = NODE_SEARCHER_CLASS.get(); - if (clazz != null && nodeSearcherClass != null) { - Method resolveMethod = clazz.getDeclaredMethod("resolve", - CompilationUnitDeclaration.class, - org.eclipse.jdt.internal.compiler.env.ICompilationUnit.class, - nodeSearcherClass, - boolean.class, - boolean.class, - boolean.class); - resolveMethod.setAccessible(true); - return resolveMethod; - } - } catch (NoSuchMethodException | SecurityException e) { - log.error("{}", e); - } - return null; - }; - - private static final Supplier CONVERT_METHOD = () -> { - try { - Class clazz = COMPILATION_UNIT_RESOLVER_CLASS.get(); - if (clazz != null) { - Method convertMethod = clazz.getDeclaredMethod("convert", - CompilationUnitDeclaration.class, - char[].class, - int.class, - Map.class, - boolean.class, - WorkingCopyOwner.class, - BINDING_TABLES_CLASS.get(), - int.class, - IProgressMonitor.class, - boolean.class); - convertMethod.setAccessible(true); - return convertMethod; - } - } catch (NoSuchMethodException | SecurityException e) { - log.error("{}", e); - } - return null; - }; - - private static final Supplier HAS_COMPILATION_ABORTED_FIELD = () -> { - try { - Class clazz = COMPILATION_UNIT_RESOLVER_CLASS.get(); - if (clazz != null) { - Field field = clazz.getDeclaredField("hasCompilationAborted"); - field.setAccessible(true); - return field; - } - } catch (Exception e) { - log.error("{}", e); - } - return null; - }; - - static CompilationUnitDeclaration resolve(org.eclipse.jdt.internal.compiler.env.ICompilationUnit sourceUnit, - List classpaths, Map options, int flags, INameEnvironmentWithProgress environment) - throws JavaModelException { - try { - - CompilerOptions compilerOptions = (CompilerOptions) GET_COMPILER_OPTIONS_METHOD.get().invoke(null, options, - (flags & ICompilationUnit.ENABLE_STATEMENTS_RECOVERY) != 0); - CancelableProblemFactory problemFactory = new CancelableProblemFactory(new NullProgressMonitor()); - boolean ignoreMethodBodies = (flags & ICompilationUnit.IGNORE_METHOD_BODIES) != 0; - compilerOptions.ignoreMethodBodies = ignoreMethodBodies; - Object resolver = COMPILATION_UNIT_RESOLVER_CONSTRUCTOR.get().newInstance(environment, - GET_HANDLER_POLICY_METHOD.get().invoke(null), compilerOptions, - GET_REQUESTOR_METHOD.get().invoke(null), problemFactory, new NullProgressMonitor(), false); - boolean analyzeAndGenerateCode = !ignoreMethodBodies; - // no existing compilation unit declaration - CompilationUnitDeclaration unit = (CompilationUnitDeclaration) RESOLVE_METHOD.get().invoke(resolver, null, - sourceUnit, null, true, // method verification - analyzeAndGenerateCode, // analyze code - analyzeAndGenerateCode); // generate code - boolean hasCompilationAborted = HAS_COMPILATION_ABORTED_FIELD.get().getBoolean(resolver); - if (hasCompilationAborted) { - // the bindings could not be resolved due to missing types in name environment - // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=86541 - CompilationUnitDeclaration unitDeclaration = parse(sourceUnit, options, flags); -// if (unit != null) { -// final int problemCount = unit.compilationResult.problemCount; -// if (problemCount != 0) { -// unitDeclaration.compilationResult.problems = new CategorizedProblem[problemCount]; -// System.arraycopy(unit.compilationResult.problems, 0, unitDeclaration.compilationResult.problems, 0, problemCount); -// unitDeclaration.compilationResult.problemCount = problemCount; -// } -// } else if (resolver.abortProblem != null) { -// unitDeclaration.compilationResult.problemCount = 1; -// unitDeclaration.compilationResult.problems = new CategorizedProblem[] { resolver.abortProblem }; -// } - return unitDeclaration; - } - return unit; - - } catch (Exception e) { - log.error("{}", e); - } - return null; - } - - static CompilationUnitDeclaration parse(org.eclipse.jdt.internal.compiler.env.ICompilationUnit sourceUnit, Map options, int flags) { - try { - return (CompilationUnitDeclaration) PARSE_METHOD.get() - .invoke(null, sourceUnit, null, options, flags); - } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { - log.error("{}", e); - } - return null; - } - - static CompilationUnit convert( - CompilationUnitDeclaration compilationUnitDeclaration, - char[] source, - int apiLevel, - Map options, - boolean needToResolveBindings, - WorkingCopyOwner owner, - int flags) { - try { - return (CompilationUnit) CONVERT_METHOD.get().invoke(null, - compilationUnitDeclaration, - source, - apiLevel, - options, - needToResolveBindings, - owner, - needToResolveBindings ? BINDING_TABLES_CONSTRUCTOR.get().newInstance() : null, - flags, - new NullProgressMonitor(), - false); - } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException - | InstantiationException e) { - log.error("{}", e); - } - return null; - } - - - static INameEnvironmentWithProgress createLookupEnvironment(Classpath[] classpath) { - try { - return (INameEnvironmentWithProgress) LOOKUP_ENVIRONMENT_CONSTRUCTOR.get().newInstance(classpath, null, new NullProgressMonitor()); - } catch (InstantiationException | IllegalAccessException | IllegalArgumentException - | InvocationTargetException e) { - log.error("{}", e); - } - return null; - } - - @SuppressWarnings("unchecked") - static List getClasspath(ASTParser parser) { - try { - return (List) GET_CLASSPATH_METHOD.get().invoke(parser); - } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { - log.error("{}", e); - } - return null; - } - -} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/CompilationUnitCache.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/CompilationUnitCache.java deleted file mode 100644 index f5417627d..000000000 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/CompilationUnitCache.java +++ /dev/null @@ -1,307 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2017, 2021 Pivotal, Inc. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Pivotal, Inc. - initial API and implementation - *******************************************************************************/ -package org.springframework.ide.vscode.boot.java.utils; - -import java.io.File; -import java.net.URI; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.CancellationException; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.function.Function; -import java.util.stream.Stream; - -import org.apache.commons.io.IOUtils; -import org.eclipse.jdt.core.ICompilationUnit; -import org.eclipse.jdt.core.IJavaElement; -import org.eclipse.jdt.core.JavaCore; -import org.eclipse.jdt.core.dom.AST; -import org.eclipse.jdt.core.dom.ASTParser; -import org.eclipse.jdt.core.dom.CompilationUnit; -import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; -import org.eclipse.jdt.internal.compiler.batch.FileSystem.Classpath; -import org.eclipse.jdt.internal.core.BasicCompilationUnit; -import org.eclipse.jdt.internal.core.DefaultWorkingCopyOwner; -import org.eclipse.jdt.internal.core.INameEnvironmentWithProgress; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.ide.vscode.commons.java.IClasspath; -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; -import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; -import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService; -import org.springframework.ide.vscode.commons.util.text.TextDocument; - -import com.google.common.cache.Cache; -import com.google.common.cache.CacheBuilder; - -import reactor.util.function.Tuple2; -import reactor.util.function.Tuples; - -public final class CompilationUnitCache implements DocumentContentProvider { - - private static final Logger logger = LoggerFactory.getLogger(CompilationUnitCache.class); - - private static final long CU_ACCESS_EXPIRATION = 1; - private JavaProjectFinder projectFinder; - private ProjectObserver projectObserver; - - private final ProjectObserver.Listener projectListener; - private final SimpleTextDocumentService documentService; - - private final Cache uriToCu; - private final Cache> projectToDocs; - private final Cache, INameEnvironmentWithProgress>> lookupEnvCache; - - public CompilationUnitCache(JavaProjectFinder projectFinder, SimpleLanguageServer server, ProjectObserver projectObserver) { - this.projectFinder = projectFinder; - this.projectObserver = projectObserver; - - // PT 154618835 - Avoid retaining the CU in the cache as it consumes memory if it hasn't been - // accessed after some time - this.uriToCu = CacheBuilder.newBuilder() - .expireAfterWrite(CU_ACCESS_EXPIRATION, TimeUnit.MINUTES) - .build(); - this.projectToDocs = CacheBuilder.newBuilder().build(); - this.lookupEnvCache = CacheBuilder.newBuilder().build(); - - this.documentService = server == null ? null : server.getTextDocumentService(); - - // IMPORTANT ===> these notifications arrive within the lsp message loop, so reactions to them have to be fast - // and not be blocked by waiting for anything - if (this.documentService != null) { - this.documentService.onDidChangeContent(doc -> invalidateCuForJavaFile(doc.getDocument().getId().getUri())); - this.documentService.onDidClose(doc -> invalidateCuForJavaFile(doc.getId().getUri())); - } - - if (this.projectFinder != null) { - for (IJavaProject project : this.projectFinder.all()) { - logger.info("CU Cache: initial lookup env creation for project <{}>", project.getElementName()); - loadLookupEnvTuple(project); - } - } - - this.projectListener = new ProjectObserver.Listener() { - - @Override - public void deleted(IJavaProject project) { - logger.info("CU Cache: deleted project {}", project.getElementName()); - invalidateProject(project); - } - - @Override - public void created(IJavaProject project) { - logger.info("CU Cache: created project {}", project.getElementName()); - invalidateProject(project); - loadLookupEnvTuple(project); - } - - @Override - public void changed(IJavaProject project) { - logger.info("CU Cache: changed project {}", project.getElementName()); - invalidateProject(project); - // Load the new cache the value right away - loadLookupEnvTuple(project); - } - }; - - if (this.projectObserver != null) { - this.projectObserver.addListener(this.projectListener); - } - - } - - public void dispose() { - if (this.projectObserver != null) { - this.projectObserver.removeListener(this.projectListener); - } - } - - /** - * Never research shows at the AST is thread-safe when used in read-only mode: - * https://bugs.eclipse.org/bugs/show_bug.cgi?id=58314 - * - * This means that the previous implemented synchronization around the requestor - * working on the AST is not necessary as long as the requestor operates in read-only - * mode on the AST nodes. - * - * Warning: Callers should take care to do all AST processing inside of the requestor callback and - * not pass of AST nodes to helper functions that work aynchronously or store AST nodes or ITypeBindings - * for later use. The JDT ASTs are not thread safe! - */ - @Deprecated - public T withCompilationUnit(TextDocument document, Function requestor) { - IJavaProject project = this.projectFinder != null ? projectFinder.find(document.getId()).orElse(null) : null; - - URI uri = URI.create(document.getUri()); - return withCompilationUnit(project, uri, requestor); - } - - /** - * Never research shows at the AST is thread-safe when used in read-only mode: - * https://bugs.eclipse.org/bugs/show_bug.cgi?id=58314 - * - * This means that the previous implemented synchronization around the requestor - * working on the AST is not necessary as long as the requestor operates in read-only - * mode on the AST nodes. - * - * Warning: Callers should take care to do all AST processing inside of the requestor callback and - * not pass of AST nodes to helper functions that work aynchronously or store AST nodes or ITypeBindings - * for later use. The JDT ASTs are not thread safe! - */ - public T withCompilationUnit(IJavaProject project, URI uri, Function requestor) { - logger.info("CU Cache: work item submitted for doc {}", uri.toString()); - - if (project != null) { - - CompilationUnit cu = null; - - try { - cu = uriToCu.get(uri, () -> { - Tuple2, INameEnvironmentWithProgress> lookupEnvTuple = loadLookupEnvTuple(project); - String utiStr = uri.toString(); - String unitName = utiStr.substring(utiStr.lastIndexOf("/")); - CompilationUnit cUnit = parse2(fetchContent(uri).toCharArray(), utiStr, unitName, lookupEnvTuple.getT1(), lookupEnvTuple.getT2()); - - logger.info("CU Cache: created new AST for {}", uri.toString()); - - return cUnit; - }); - - if (cu != null) { - projectToDocs.get(project, () -> new HashSet<>()).add(uri); - } - - } catch (Exception e) { - logger.error("", e); - } - - if (cu != null) { - try { - logger.info("CU Cache: start work on AST for {}", uri.toString()); - return requestor.apply(cu); - } - catch (CancellationException e) { - throw e; - } - catch (Exception e) { - logger.error("", e); - } - finally { - logger.info("CU Cache: end work on AST for {}", uri.toString()); - } - } - } - - return requestor.apply(null); - } - - - public static CompilationUnit parse2(char[] source, String docURI, String unitName, IJavaProject project) throws Exception { - List classpaths = createClasspath(getClasspathEntries(project)); - return parse2(source, docURI, unitName, classpaths, null); - } - - private static CompilationUnit parse2(char[] source, String docURI, String unitName, List classpaths, INameEnvironmentWithProgress environment) throws Exception { - Map options = JavaCore.getOptions(); - String apiLevel = JavaCore.VERSION_16; - JavaCore.setComplianceOptions(apiLevel, options); - if (environment == null) { - environment = CUResolver.createLookupEnvironment(classpaths.toArray(new Classpath[classpaths.size()])); - } - - BasicCompilationUnit sourceUnit = new BasicCompilationUnit(source, null, unitName, (IJavaElement) null); - - int flags = 0; - boolean needToResolveBindings = true; - flags |= ICompilationUnit.ENABLE_STATEMENTS_RECOVERY; - flags |= ICompilationUnit.ENABLE_BINDINGS_RECOVERY; - CompilationUnitDeclaration unit = null; - try { - unit = CUResolver.resolve(sourceUnit, classpaths, options, flags, environment); - } catch (Exception e) { - flags &= ~ICompilationUnit.ENABLE_BINDINGS_RECOVERY; - unit = CUResolver.parse(sourceUnit, options, flags); - needToResolveBindings = false; - } - - CompilationUnit cu = CUResolver.convert(unit, source, AST.JLS16, options, needToResolveBindings, DefaultWorkingCopyOwner.PRIMARY, flags); - - return cu; - } - - private static List createClasspath(String[] classpathEntries) { - ASTParser parser = ASTParser.newParser(AST.JLS16); - String[] sourceEntries = new String[] {}; - parser.setEnvironment(classpathEntries, sourceEntries, null, false); - return CUResolver.getClasspath(parser); - } - - private Tuple2, INameEnvironmentWithProgress> loadLookupEnvTuple(IJavaProject project) { - try { - return lookupEnvCache.get(project, () -> { - List classpaths = createClasspath(getClasspathEntries(project)); - INameEnvironmentWithProgress environment = CUResolver.createLookupEnvironment(classpaths.toArray(new Classpath[classpaths.size()])); - return Tuples.of(classpaths, environment); - }); - } catch (ExecutionException e) { - logger.error("{}", e); - return null; - } - } - - private static String[] getClasspathEntries(IJavaProject project) throws Exception { - if (project == null) { - return new String[0]; - } else { - IClasspath classpath = project.getClasspath(); - Stream classpathEntries = IClasspathUtil.getAllBinaryRoots(classpath).stream(); - return classpathEntries - .filter(file -> file.exists()) - .map(file -> file.getAbsolutePath()).toArray(String[]::new); - } - } - - private void invalidateCuForJavaFile(String uriStr) { - logger.info("CU Cache: invalidate AST for {}", uriStr); - - URI uri = URI.create(uriStr); - uriToCu.invalidate(uri); - } - - private void invalidateProject(IJavaProject project) { - logger.info("CU Cache: invalidate project <{}>", project.getElementName()); - - Set docUris = projectToDocs.getIfPresent(project); - if (docUris != null) { - uriToCu.invalidateAll(docUris); - projectToDocs.invalidate(project); - } - lookupEnvCache.invalidate(project); - } - - @Override - public String fetchContent(URI uri) throws Exception { - if (documentService != null) { - TextDocument document = documentService.getLatestSnapshot(uri.toString()); - if (document != null) { - return document.get(); - } - } - return IOUtils.toString(uri); - } - -} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/NameEnvironmentAwareASTRequestor.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/NameEnvironmentAwareASTRequestor.java deleted file mode 100644 index f6fef51df..000000000 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/NameEnvironmentAwareASTRequestor.java +++ /dev/null @@ -1,63 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2019 Pivotal, Inc. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Pivotal, Inc. - initial API and implementation - *******************************************************************************/ -package org.springframework.ide.vscode.boot.java.utils; - -import java.lang.reflect.Field; - -import org.eclipse.jdt.core.dom.CompilationUnit; -import org.eclipse.jdt.core.dom.FileASTRequestor; -import org.eclipse.jdt.internal.compiler.env.INameEnvironment; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * @author Martin Lippert - */ -public class NameEnvironmentAwareASTRequestor extends FileASTRequestor { - - private static final Logger log = LoggerFactory.getLogger(NameEnvironmentAwareASTRequestor.class); - - private FileASTRequestor realOne; - private INameEnvironment nameEnvironment; - - public NameEnvironmentAwareASTRequestor(FileASTRequestor realOne) { - this.realOne = realOne; - } - - @Override - public void acceptAST(String sourceFilePath, CompilationUnit cu) { - if (nameEnvironment == null) { - extractNameEnvironment(); - } - - realOne.acceptAST(sourceFilePath, cu); - } - - public INameEnvironment getNameEnvironment() { - return nameEnvironment; - } - - private void extractNameEnvironment() { - try { - Field declaredField = FileASTRequestor.class.getDeclaredField("compilationUnitResolver"); - declaredField.setAccessible(true); - - Object compilationUnitResolver = declaredField.get(this); - Object lookupEnvironment = compilationUnitResolver.getClass().getField("lookupEnvironment") - .get(compilationUnitResolver); - nameEnvironment = (INameEnvironment) lookupEnvironment.getClass() - .getField("nameEnvironment").get(lookupEnvironment); - } catch (Exception e) { - log.error(" could not identify name environment when scanning for symbols in Java code - " + e.getMessage()); - } - - } -} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/ORCompilationUnitCache.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/ORCompilationUnitCache.java index f4cb4524a..87fdd963c 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/ORCompilationUnitCache.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/ORCompilationUnitCache.java @@ -17,11 +17,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import org.apache.commons.io.IOUtils; -import org.openrewrite.InMemoryExecutionContext; import org.openrewrite.Parser.Input; -import org.openrewrite.Result; import org.openrewrite.java.JavaParser; -import org.openrewrite.java.UpdateSourcePositions; import org.openrewrite.java.tree.J.CompilationUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -41,7 +38,7 @@ import reactor.core.Disposable; public class ORCompilationUnitCache implements DocumentContentProvider, Disposable { - private static final Logger logger = LoggerFactory.getLogger(CompilationUnitCache.class); + private static final Logger logger = LoggerFactory.getLogger(ORCompilationUnitCache.class); private static final long CU_ACCESS_EXPIRATION = 1; private JavaProjectFinder projectFinder; diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerJavaContext.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerJavaContext.java index ca81e4c16..1eae63304 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerJavaContext.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerJavaContext.java @@ -15,7 +15,6 @@ import java.util.List; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; -import org.eclipse.jdt.core.dom.ITypeBinding; import org.openrewrite.java.tree.J.CompilationUnit; import org.openrewrite.java.tree.JavaType.FullyQualified; import org.springframework.ide.vscode.boot.java.utils.SpringIndexerJava.SCAN_PASS; diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerJavaDependencyTracker.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerJavaDependencyTracker.java index b1f3d38b8..af0871c50 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerJavaDependencyTracker.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerJavaDependencyTracker.java @@ -13,7 +13,7 @@ package org.springframework.ide.vscode.boot.java.utils; import java.util.Collection; import java.util.Set; -import org.eclipse.jdt.core.dom.ITypeBinding; +import org.openrewrite.java.tree.JavaType.FullyQualified; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -26,8 +26,8 @@ public class SpringIndexerJavaDependencyTracker { private Multimap dependencies = MultimapBuilder.hashKeys().hashSetValues().build(); - public void addDependency(String sourceFile, ITypeBinding dependsOn) { - dependencies.put(sourceFile, dependsOn.getKey()); + public void addDependency(String sourceFile, FullyQualified dependsOn) { + dependencies.put(sourceFile, dependsOn.getFullyQualifiedName()); } public void dump() { diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValueCompletionProcessor.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValueCompletionProcessor.java index 7837f35de..fbf7445f3 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValueCompletionProcessor.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValueCompletionProcessor.java @@ -18,15 +18,12 @@ import java.util.List; import java.util.Optional; import java.util.Set; -import org.eclipse.jdt.core.dom.ASTNode; -import org.eclipse.jdt.core.dom.MemberValuePair; -import org.eclipse.jdt.core.dom.SimpleName; -import org.eclipse.jdt.core.dom.StringLiteral; import org.eclipse.lsp4j.TextDocumentIdentifier; import org.openrewrite.java.tree.J; import org.openrewrite.java.tree.J.Annotation; import org.openrewrite.marker.Range; import org.springframework.ide.vscode.boot.java.handlers.CompletionProvider; +import org.springframework.ide.vscode.boot.java.utils.ORAstUtils; import org.springframework.ide.vscode.boot.metadata.ProjectBasedPropertyIndexProvider; import org.springframework.ide.vscode.boot.metadata.PropertyInfo; import org.springframework.ide.vscode.boot.metadata.SpringPropertyIndexProvider; @@ -109,12 +106,13 @@ public class ValueCompletionProcessor implements CompletionProvider { public void provideCompletions(J node, int offset, IDocument doc, Collection completions) { } - private void computeProposalsForSimpleName(ASTNode node, Collection completions, int offset, + private void computeProposalsForSimpleName(J node, Collection completions, int offset, IDocument doc) { - String prefix = identifyPropertyPrefix(node.toString(), offset - node.getStartPosition()); + Range r = ORAstUtils.getRange(node); + int startOffset = r.getStart().getOffset(); + int endOffset = r.getEnd().getOffset(); - int startOffset = node.getStartPosition(); - int endOffset = node.getStartPosition() + node.getLength(); + String prefix = identifyPropertyPrefix(node.toString(), offset - startOffset); String proposalPrefix = "\""; String proposalPostfix = "\""; diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValuePropertyReferencesProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValuePropertyReferencesProvider.java index d71c3222d..e0fbdb2d4 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValuePropertyReferencesProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValuePropertyReferencesProvider.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017, 2021 Pivotal, Inc. + * Copyright (c) 2017, 2022 Pivotal, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -24,17 +24,18 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import org.apache.commons.io.FileUtils; -import org.eclipse.jdt.core.dom.ASTNode; -import org.eclipse.jdt.core.dom.Annotation; -import org.eclipse.jdt.core.dom.ITypeBinding; -import org.eclipse.jdt.core.dom.MemberValuePair; -import org.eclipse.jdt.core.dom.StringLiteral; import org.eclipse.lsp4j.Location; import org.eclipse.lsp4j.Position; import org.eclipse.lsp4j.Range; import org.eclipse.lsp4j.WorkspaceFolder; import org.eclipse.lsp4j.jsonrpc.CancelChecker; +import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.J.Annotation; +import org.openrewrite.java.tree.J.Assignment; +import org.openrewrite.java.tree.J.Literal; +import org.openrewrite.java.tree.JavaType.FullyQualified; import org.springframework.ide.vscode.boot.java.handlers.ReferenceProvider; +import org.springframework.ide.vscode.boot.java.utils.ORAstUtils; import org.springframework.ide.vscode.boot.properties.BootPropertiesLanguageServerComponents; import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; import org.springframework.ide.vscode.commons.util.BadLocationException; @@ -63,23 +64,27 @@ public class ValuePropertyReferencesProvider implements ReferenceProvider { } @Override - public List provideReferences(CancelChecker cancelToken, ASTNode node, Annotation annotation, - ITypeBinding type, int offset, TextDocument doc) { + public List provideReferences(CancelChecker cancelToken, J node, Annotation annotation, + FullyQualified type, int offset, TextDocument doc) { cancelToken.checkCanceled(); try { // case: @Value("prefix<*>") - if (node instanceof StringLiteral && node.getParent() instanceof Annotation) { - if (node.toString().startsWith("\"") && node.toString().endsWith("\"")) { - return provideReferences(node.toString(), offset - node.getStartPosition(), node.getStartPosition(), doc); + if (node instanceof Literal && ORAstUtils.getParent(node) instanceof Annotation) { + String nodeStr = node.printTrimmed(); + if (nodeStr.startsWith("\"") && nodeStr.endsWith("\"")) { + org.openrewrite.marker.Range r = ORAstUtils.getRange(node); + return provideReferences(node.toString(), offset - r.getStart().getOffset(), r.getStart().getOffset(), doc); } } // case: @Value(value="prefix<*>") - else if (node instanceof StringLiteral && node.getParent() instanceof MemberValuePair - && "value".equals(((MemberValuePair)node.getParent()).getName().toString())) { - if (node.toString().startsWith("\"") && node.toString().endsWith("\"")) { - return provideReferences(node.toString(), offset - node.getStartPosition(), node.getStartPosition(), doc); + else if (node instanceof Literal && ORAstUtils.getParent(node) instanceof Assignment + && "value".equals(((Assignment)ORAstUtils.getParent(node)).getVariable().printTrimmed())) { + String nodeStr = node.printTrimmed(); + if (nodeStr.startsWith("\"") && nodeStr.endsWith("\"")) { + org.openrewrite.marker.Range r = ORAstUtils.getRange(node); + return provideReferences(node.toString(), offset - r.getStart().getOffset(), r.getStart().getOffset(), doc); } } } diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/bootiful/PropertyEditorTestConf.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/bootiful/PropertyEditorTestConf.java index 11b524a28..94c0514d9 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/bootiful/PropertyEditorTestConf.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/bootiful/PropertyEditorTestConf.java @@ -20,7 +20,7 @@ import org.springframework.ide.vscode.boot.editor.harness.PropertyIndexHarness; import org.springframework.ide.vscode.boot.java.links.JavaDocumentUriProvider; import org.springframework.ide.vscode.boot.java.links.SourceLinkFactory; import org.springframework.ide.vscode.boot.java.links.SourceLinks; -import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache; +import org.springframework.ide.vscode.boot.java.utils.ORCompilationUnitCache; import org.springframework.ide.vscode.boot.java.utils.SymbolCache; import org.springframework.ide.vscode.boot.java.utils.SymbolCacheVoid; import org.springframework.ide.vscode.boot.metadata.ValueProviderRegistry; @@ -73,11 +73,11 @@ public class PropertyEditorTestConf { return serverParams.projectFinder; } - @Bean SourceLinks sourceLinks(CompilationUnitCache cuCache) { + @Bean SourceLinks sourceLinks(ORCompilationUnitCache cuCache) { return SourceLinkFactory.NO_SOURCE_LINKS; } - @Bean DefinitionLinkAsserts definitionLinkAsserts(JavaDocumentUriProvider javaDocumentUriProvider, CompilationUnitCache cuCache) { + @Bean DefinitionLinkAsserts definitionLinkAsserts(JavaDocumentUriProvider javaDocumentUriProvider, ORCompilationUnitCache cuCache) { return new DefinitionLinkAsserts(javaDocumentUriProvider, cuCache); } diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/bootiful/XmlBeansTestConf.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/bootiful/XmlBeansTestConf.java index f603b792b..05e80dc0c 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/bootiful/XmlBeansTestConf.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/bootiful/XmlBeansTestConf.java @@ -18,7 +18,7 @@ import org.springframework.ide.vscode.boot.editor.harness.PropertyIndexHarness; import org.springframework.ide.vscode.boot.java.links.JavaDocumentUriProvider; import org.springframework.ide.vscode.boot.java.links.SourceLinkFactory; import org.springframework.ide.vscode.boot.java.links.SourceLinks; -import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache; +import org.springframework.ide.vscode.boot.java.utils.ORCompilationUnitCache; import org.springframework.ide.vscode.boot.java.utils.SymbolCache; import org.springframework.ide.vscode.boot.java.utils.SymbolCacheVoid; import org.springframework.ide.vscode.boot.java.utils.test.MockProjectObserver; @@ -64,7 +64,7 @@ public class XmlBeansTestConf { return (DefaultSpringPropertyIndexProvider) serverParams.indexProvider; } - @Bean DefinitionLinkAsserts definitionLinkAsserts(JavaDocumentUriProvider javaDocumentUriProvider, CompilationUnitCache cuCache) { + @Bean DefinitionLinkAsserts definitionLinkAsserts(JavaDocumentUriProvider javaDocumentUriProvider, ORCompilationUnitCache cuCache) { return new DefinitionLinkAsserts(javaDocumentUriProvider, cuCache); } diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/AstParserTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/AstParserTest.java deleted file mode 100644 index 5431b57ad..000000000 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/AstParserTest.java +++ /dev/null @@ -1,123 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2019 Pivotal, Inc. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Pivotal, Inc. - initial API and implementation - *******************************************************************************/ -package org.springframework.ide.vscode.boot.java.utils.test; - -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import java.net.URI; -import java.net.URL; - -import org.apache.commons.io.IOUtils; -import org.eclipse.jdt.core.dom.ASTVisitor; -import org.eclipse.jdt.core.dom.CompilationUnit; -import org.eclipse.jdt.core.dom.FieldDeclaration; -import org.eclipse.jdt.core.dom.IAnnotationBinding; -import org.eclipse.jdt.core.dom.IMethodBinding; -import org.eclipse.jdt.core.dom.ITypeBinding; -import org.eclipse.jdt.core.dom.MarkerAnnotation; -import org.eclipse.jdt.core.dom.MethodDeclaration; -import org.eclipse.jdt.core.dom.NormalAnnotation; -import org.eclipse.jdt.core.dom.SingleMemberAnnotation; -import org.eclipse.jdt.core.dom.TypeDeclaration; -import org.junit.Before; -import org.junit.Test; -import org.springframework.ide.vscode.boot.java.links.SourceLinks; -import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache; -import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject; -import org.springframework.ide.vscode.project.harness.ProjectsHarness; - - -public class AstParserTest { - - private ProjectsHarness projects = ProjectsHarness.INSTANCE; - - private MavenJavaProject jp; - - @Before - public void setup() throws Exception { - jp = projects.mavenProject("empty-boot-15-web-app"); - assertTrue(jp.getIndex().findType("org.springframework.boot.SpringApplication").exists()); - } - - @Test - public void test1() throws Exception { - URL sourceUrl = SourceLinks.source(jp, "org.springframework.boot.SpringApplication").get(); - - URI uri = sourceUrl.toURI(); - - String unitName = "SpringApplication"; - - char[] content = IOUtils.toString(uri).toCharArray(); - - CompilationUnit cu = CompilationUnitCache.parse2(content, uri.toString(), unitName, jp); - - assertNotNull(cu); - - cu.accept(new ASTVisitor() { - - @Override - public boolean visit(TypeDeclaration node) { - ITypeBinding binding = node.resolveBinding(); - assertNotNull(binding); - return super.visit(node); - } - - @Override - public boolean visit(SingleMemberAnnotation node) { - IAnnotationBinding annotationBinding = node.resolveAnnotationBinding(); - assertNotNull(annotationBinding); - ITypeBinding binding = node.resolveTypeBinding(); - assertNotNull(binding); - return super.visit(node); - } - - @Override - public boolean visit(NormalAnnotation node) { - IAnnotationBinding annotationBinding = node.resolveAnnotationBinding(); - assertNotNull(annotationBinding); - ITypeBinding binding = node.resolveTypeBinding(); - assertNotNull(binding); - return super.visit(node); - } - - @Override - public boolean visit(MarkerAnnotation node) { - IAnnotationBinding annotationBinding = node.resolveAnnotationBinding(); - assertNotNull(annotationBinding); - ITypeBinding binding = node.resolveTypeBinding(); - assertNotNull(binding); - return super.visit(node); - } - - @Override - public boolean visit(MethodDeclaration node) { - IMethodBinding binding = node.resolveBinding(); - assertNotNull(binding); - if (node.getReturnType2() != null) { - ITypeBinding returnTypeBinding = node.getReturnType2().resolveBinding(); - assertNotNull(returnTypeBinding); - } - return super.visit(node); - } - - @Override - public boolean visit(FieldDeclaration node) { - ITypeBinding binding = node.getType().resolveBinding(); - assertNotNull(binding); - return super.visit(node); - } - - }); - - } - -} diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/CompilationUnitCacheTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/CompilationUnitCacheTest.java index 915295efc..1cd527fb2 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/CompilationUnitCacheTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/CompilationUnitCacheTest.java @@ -36,7 +36,7 @@ import org.springframework.ide.vscode.boot.editor.harness.PropertyIndexHarness; import org.springframework.ide.vscode.boot.java.BootJavaLanguageServerComponents; import org.springframework.ide.vscode.boot.java.links.SourceLinkFactory; import org.springframework.ide.vscode.boot.java.links.SourceLinks; -import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache; +import org.springframework.ide.vscode.boot.java.utils.ORCompilationUnitCache; import org.springframework.ide.vscode.boot.java.utils.SymbolCache; import org.springframework.ide.vscode.boot.java.utils.SymbolCacheVoid; import org.springframework.ide.vscode.boot.metadata.ValueProviderRegistry; @@ -104,7 +104,7 @@ public class CompilationUnitCacheTest { ); } - @Bean SourceLinks sourceLinks(SimpleTextDocumentService documents, CompilationUnitCache cuCache) { + @Bean SourceLinks sourceLinks(SimpleTextDocumentService documents, ORCompilationUnitCache cuCache) { return SourceLinkFactory.NO_SOURCE_LINKS; } diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/value/test/ValueCompletionTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/value/test/ValueCompletionTest.java index 30dda51f3..1f9ea3cbe 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/value/test/ValueCompletionTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/value/test/ValueCompletionTest.java @@ -37,7 +37,7 @@ import org.springframework.ide.vscode.boot.editor.harness.AdHocPropertyHarness; import org.springframework.ide.vscode.boot.editor.harness.PropertyIndexHarness; import org.springframework.ide.vscode.boot.java.links.SourceLinkFactory; import org.springframework.ide.vscode.boot.java.links.SourceLinks; -import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache; +import org.springframework.ide.vscode.boot.java.utils.ORCompilationUnitCache; import org.springframework.ide.vscode.boot.java.utils.SymbolCache; import org.springframework.ide.vscode.boot.java.utils.SymbolCacheVoid; import org.springframework.ide.vscode.boot.java.value.ValueCompletionProcessor; @@ -121,7 +121,7 @@ public class ValueCompletionTest { return new SymbolCacheVoid(); } - @Bean SourceLinks sourceLinks(SimpleTextDocumentService documents, CompilationUnitCache cuCache) { + @Bean SourceLinks sourceLinks(SimpleTextDocumentService documents, ORCompilationUnitCache cuCache) { return SourceLinkFactory.NO_SOURCE_LINKS; } diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/value/test/ValueSpelExpressionValidationTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/value/test/ValueSpelExpressionValidationTest.java index 5a8e44ab4..e2e4f5af3 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/value/test/ValueSpelExpressionValidationTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/value/test/ValueSpelExpressionValidationTest.java @@ -42,7 +42,6 @@ import org.springframework.ide.vscode.boot.editor.harness.PropertyIndexHarness; import org.springframework.ide.vscode.boot.java.handlers.BootJavaReconcileEngine; import org.springframework.ide.vscode.boot.java.links.SourceLinkFactory; import org.springframework.ide.vscode.boot.java.links.SourceLinks; -import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache; import org.springframework.ide.vscode.boot.java.utils.ORCompilationUnitCache; import org.springframework.ide.vscode.boot.java.utils.SymbolCache; import org.springframework.ide.vscode.boot.java.utils.SymbolCacheVoid; @@ -130,7 +129,7 @@ public class ValueSpelExpressionValidationTest { return new SymbolCacheVoid(); } - @Bean SourceLinks sourceLinks(SimpleTextDocumentService documents, CompilationUnitCache cuCache) { + @Bean SourceLinks sourceLinks(SimpleTextDocumentService documents, ORCompilationUnitCache cuCache) { return SourceLinkFactory.NO_SOURCE_LINKS; } diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/DefinitionLinkAsserts.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/DefinitionLinkAsserts.java index 8e90bb01b..6c5418e96 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/DefinitionLinkAsserts.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/DefinitionLinkAsserts.java @@ -17,22 +17,24 @@ import java.util.HashSet; import java.util.Optional; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; -import java.util.stream.Collectors; -import org.eclipse.jdt.core.dom.ASTVisitor; -import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; -import org.eclipse.jdt.core.dom.EnumConstantDeclaration; -import org.eclipse.jdt.core.dom.EnumDeclaration; -import org.eclipse.jdt.core.dom.MethodDeclaration; -import org.eclipse.jdt.core.dom.SimpleName; -import org.eclipse.jdt.core.dom.SingleVariableDeclaration; -import org.eclipse.jdt.core.dom.TypeDeclaration; import org.eclipse.lsp4j.Location; import org.eclipse.lsp4j.LocationLink; import org.eclipse.lsp4j.Range; +import org.openrewrite.Cursor; +import org.openrewrite.java.JavaIsoVisitor; +import org.openrewrite.java.tree.J.ClassDeclaration; +import org.openrewrite.java.tree.J.EnumValue; +import org.openrewrite.java.tree.J.Identifier; +import org.openrewrite.java.tree.J.MethodDeclaration; +import org.openrewrite.java.tree.JavaType; +import org.openrewrite.java.tree.JavaType.FullyQualified; +import org.openrewrite.java.tree.JavaType.Method; +import org.openrewrite.java.tree.TypeUtils; import org.springframework.ide.vscode.boot.java.links.JavaDocumentUriProvider; import org.springframework.ide.vscode.boot.java.links.SourceLinks; -import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache; +import org.springframework.ide.vscode.boot.java.utils.ORAstUtils; +import org.springframework.ide.vscode.boot.java.utils.ORCompilationUnitCache; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.util.BadLocationException; import org.springframework.ide.vscode.commons.util.text.LanguageId; @@ -44,7 +46,7 @@ import com.google.common.collect.ImmutableSet; public class DefinitionLinkAsserts { private JavaDocumentUriProvider javaDocumentUriProvider; - private CompilationUnitCache cuCache; + private ORCompilationUnitCache cuCache; public static JavaMethod method(String fqClassName, String methodName, String... params) { return new JavaMethod(fqClassName, methodName, params); @@ -84,7 +86,7 @@ public class DefinitionLinkAsserts { } } - public DefinitionLinkAsserts(JavaDocumentUriProvider javaDocumentUriProvider, CompilationUnitCache cuCache) { + public DefinitionLinkAsserts(JavaDocumentUriProvider javaDocumentUriProvider, ORCompilationUnitCache cuCache) { this.javaDocumentUriProvider = javaDocumentUriProvider; this.cuCache = cuCache; @@ -133,33 +135,23 @@ public class DefinitionLinkAsserts { TextDocument doc = new TextDocument(sourceUrl.get().toString(), LanguageId.JAVA); doc.setText(cuCache.fetchContent(sourceUri)); AtomicReference range = new AtomicReference<>(null); - cu.accept(new ASTVisitor() { - - private boolean proceessTypeNode(TextDocument doc, String typeName, - AtomicReference range, AbstractTypeDeclaration node) { - SimpleName nameNode = node.getName(); - if (nameNode.getIdentifier().equals(typeName)) { + + new JavaIsoVisitor>() { + @Override + public ClassDeclaration visitClassDeclaration(ClassDeclaration classDecl, AtomicReference p) { + if (typeName.equals(classDecl.getSimpleName())) { try { - range.set(doc.toRange(nameNode.getStartPosition(), nameNode.getLength())); - return false; + org.openrewrite.marker.Range cuRange = ORAstUtils.getRange(classDecl.getName()); + p.set(doc.toRange(cuRange.getStart().getOffset(), cuRange.length())); + return classDecl; } catch (BadLocationException e) { throw new IllegalStateException(e); } } - return true; + return super.visitClassDeclaration(classDecl, p); } - - @Override - public boolean visit(TypeDeclaration node) { - return proceessTypeNode(doc, typeName, range, node); - } - - @Override - public boolean visit(EnumDeclaration node) { - return proceessTypeNode(doc, typeName, range, node); - } - - }); + }.visitNonNull(cu, range); + return range.get(); } catch (Exception e) { throw new IllegalStateException(e); @@ -187,37 +179,38 @@ public class DefinitionLinkAsserts { AtomicReference range = new AtomicReference<>(null); TextDocument doc = new TextDocument(sourceUrl.get().toString(), LanguageId.JAVA); doc.setText(cuCache.fetchContent(sourceUri)); - cu.accept(new ASTVisitor() { - + + new JavaIsoVisitor>() { @Override - public boolean visit(MethodDeclaration node) { - SimpleName nameNode = node.getName(); - if (nameNode.getIdentifier().equals(method.methodName)) { - if (node.parameters().size() != method.params.length) { - return false; - } - int i = 0; - for (Object _p : node.parameters()) { - if (_p instanceof SingleVariableDeclaration) { - SingleVariableDeclaration p = (SingleVariableDeclaration) _p; - String fqName = p.getType().resolveBinding().getErasure().getQualifiedName(); - if (!fqName.equals(method.params[i++])) { - return false; + public MethodDeclaration visitMethodDeclaration(MethodDeclaration m, + AtomicReference p) { + if (m.getSimpleName().equals(method.methodName) && m.getParameters().size() == method.params.length) { + Method methodType = m.getMethodType(); + if (methodType != null) { + int i = 0; + for (JavaType paramType : methodType.getParameterTypes()) { + FullyQualified fqType = TypeUtils.asFullyQualified(paramType); + if (fqType == null) { + if (!method.params[i++].equals(paramType.toString())) { + return m; + } + } else { + if (!fqType.getFullyQualifiedName().equals(method.params[i++])) { + return m; + } } - } else { - return false; + } + try { + org.openrewrite.marker.Range cuRange = ORAstUtils.getRange(m.getName()); + p.set(doc.toRange(cuRange.getStart().getOffset(), cuRange.length())); + } catch (BadLocationException e) { + throw new IllegalStateException(e); } } - try { - range.set(doc.toRange(nameNode.getStartPosition(), nameNode.getLength())); - } catch (BadLocationException e) { - throw new IllegalStateException(e); - } } - return false; + return m; } - - }); + }.visitNonNull(cu, range); return range.get(); } catch (Exception e) { throw new IllegalStateException(e); @@ -247,36 +240,31 @@ public class DefinitionLinkAsserts { AtomicReference range = new AtomicReference<>(null); TextDocument doc = new TextDocument(sourceUrl.get().toString(), LanguageId.JAVA); doc.setText(cuCache.fetchContent(sourceUri)); - cu.accept(new ASTVisitor() { - - boolean foundType = false; - + + String enumName = field.fqName.substring(field.fqName.lastIndexOf('.') + 1); + new JavaIsoVisitor>() { @Override - public boolean visit(EnumConstantDeclaration node) { - if (foundType) { - SimpleName nameNode = node.getName(); - if (nameNode.getIdentifier().equals(field.fieldName)) { - try { - range.set(doc.toRange(nameNode.getStartPosition(), nameNode.getLength())); - } catch (BadLocationException e) { - throw new IllegalStateException(e); + public EnumValue visitEnumValue(EnumValue _enum, AtomicReference p) { + Cursor delcaringTypeCursor = getCursor().dropParentUntil(ClassDeclaration.class::isInstance); + if (delcaringTypeCursor != null) { + ClassDeclaration declaringType = delcaringTypeCursor.getValue(); + if (enumName.equals(declaringType.getSimpleName())) { + Identifier nameNode = _enum.getName(); + if (field.fieldName.equals(nameNode.printTrimmed())) { + try { + org.openrewrite.marker.Range nameNodeRange = ORAstUtils.getRange(nameNode); + p.set(doc.toRange(nameNodeRange.getStart().getOffset(), nameNodeRange.length())); + } catch (BadLocationException e) { + throw new IllegalStateException(e); + } } } } - return true; + // TODO Auto-generated method stub + return super.visitEnumValue(_enum, p); } - - @Override - public boolean visit(EnumDeclaration node) { - if (node.getName().getIdentifier() - .equals(field.fqName.substring(field.fqName.lastIndexOf('.') + 1))) { - foundType = true; - return true; - } - return false; - } - - }); + }.visitNonNull(cu, range); + return range.get(); } catch (Exception e) { throw new IllegalStateException(e);