diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java index bc7aad8bc..233bbfe2a 100644 --- a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java @@ -30,6 +30,8 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.stream.Collectors; @@ -137,7 +139,8 @@ public final class SimpleLanguageServer implements Sts4LanguageServer, SpringInd private static final int FORCED_EXIT_DELAY_IN_SECONDS = 3; public final String EXTENSION_ID; - private final String CODE_ACTION_COMMAND_ID; + public final String CODE_ACTION_COMMAND_ID; + public final LazyCompletionResolver completionResolver = createCompletionResolver(); private SimpleTextDocumentService tds; @@ -706,7 +709,7 @@ public final class SimpleLanguageServer implements Sts4LanguageServer, SpringInd testListener.reconcileStarted(docId.getUri(), doc.getVersion()); } - IProblemCollector problems = createProblemCollector(doc); + IProblemCollector problems = createProblemCollector(new AtomicReference(doc), null); engine.reconcile(doc, problems); }) @@ -749,9 +752,10 @@ public final class SimpleLanguageServer implements Sts4LanguageServer, SpringInd } } - public IProblemCollector createProblemCollector(TextDocument doc) { - SimpleTextDocumentService documents = getTextDocumentService(); - TextDocumentIdentifier docId = doc.getId(); + public IProblemCollector createProblemCollector(AtomicReference docRef, BiConsumer diagnosticsCollector) { + + SimpleTextDocumentService documentsService = getTextDocumentService(); + return new IProblemCollector() { private LinkedHashSet diagnostics = new LinkedHashSet<>(); @@ -759,8 +763,8 @@ public final class SimpleLanguageServer implements Sts4LanguageServer, SpringInd @Override public void endCollecting() { - documents.setQuickfixes(docId, quickfixes); - documents.publishDiagnostics(docId, diagnostics); + documentsService.setQuickfixes(docRef.get().getId(), quickfixes); + documentsService.publishDiagnostics(docRef.get().getId(), diagnostics); log.debug("Reconcile done sent {} diagnostics", diagnostics.size()); } @@ -772,8 +776,8 @@ public final class SimpleLanguageServer implements Sts4LanguageServer, SpringInd @Override public void checkPointCollecting() { // publish what has been collected so far - documents.setQuickfixes(docId, quickfixes); - documents.publishDiagnostics(docId, diagnostics); + documentsService.setQuickfixes(docRef.get().getId(), quickfixes); + documentsService.publishDiagnostics(docRef.get().getId(), diagnostics); log.debug("Reconcile checkpoint sent {} diagnostics", diagnostics.size()); } @@ -787,7 +791,7 @@ public final class SimpleLanguageServer implements Sts4LanguageServer, SpringInd Diagnostic d = new Diagnostic(); d.setCode(problem.getCode()); d.setMessage(problem.getMessage()); - Range rng = doc.toRange(problem.getOffset(), problem.getLength()); + Range rng = docRef.get().toRange(problem.getOffset(), problem.getLength()); d.setRange(rng); d.setSeverity(severity); d.setSource(getServer().EXTENSION_ID); @@ -810,9 +814,14 @@ public final class SimpleLanguageServer implements Sts4LanguageServer, SpringInd }).collect(Collectors.toList())); } diagnostics.add(d); + + if (diagnosticsCollector != null) { + diagnosticsCollector.accept(docRef.get().getId().getUri(), d); + } + } } catch (BadLocationException e) { - log.warn("Invalid reconcile problem ignored: " + doc.getUri() + " - problem position: " + problem.getOffset() + "/" + problem.getLength(), e); + log.warn("Invalid reconcile problem ignored: " + docRef.get().getId().getUri() + " - problem position: " + problem.getOffset() + "/" + problem.getLength(), e); } } }; diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/SpringSymbolIndex.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/SpringSymbolIndex.java index 9cdc677d4..bf0a8ee7c 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/SpringSymbolIndex.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/SpringSymbolIndex.java @@ -29,11 +29,15 @@ import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; +import org.eclipse.lsp4j.Diagnostic; import org.eclipse.lsp4j.Location; import org.eclipse.lsp4j.TextDocumentIdentifier; import org.eclipse.lsp4j.WorkspaceSymbol; @@ -50,6 +54,8 @@ import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchyA 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.handlers.SymbolProvider; +import org.springframework.ide.vscode.boot.java.reconcilers.AnnotationReconciler; +import org.springframework.ide.vscode.boot.java.reconcilers.BeanMethodNotPublicReconciler; import org.springframework.ide.vscode.boot.java.utils.DocumentDescriptor; import org.springframework.ide.vscode.boot.java.utils.SpringFactoriesIndexer; import org.springframework.ide.vscode.boot.java.utils.SpringIndexer; @@ -65,6 +71,7 @@ import org.springframework.ide.vscode.commons.languageserver.java.FutureProjectF 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.java.ProjectObserver.Listener; +import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector; import org.springframework.ide.vscode.commons.languageserver.util.ListenerList; import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService; @@ -156,7 +163,9 @@ public class SpringSymbolIndex implements InitializingBean, SpringIndex { SymbolHandler handler = new SymbolHandler() { @Override - public void addSymbols(IJavaProject project, String docURI, EnhancedSymbolInformation[] enhancedSymbols, Bean[] beanDefinitions) { + public void addSymbols(IJavaProject project, String docURI, EnhancedSymbolInformation[] enhancedSymbols, Bean[] beanDefinitions, + List diagnostics) { + if (enhancedSymbols != null) { SpringSymbolIndex.this.addSymbolsByDoc(project, docURI, enhancedSymbols); } @@ -164,11 +173,16 @@ public class SpringSymbolIndex implements InitializingBean, SpringIndex { if (beanDefinitions != null) { springIndex.updateBeans(project.getElementName(), docURI, beanDefinitions); } + + if (diagnostics != null) { + server.getTextDocumentService().publishDiagnostics(new TextDocumentIdentifier(docURI), diagnostics); + // TODO: need to use real TextDocumentIdentifier because of the document version + } } @Override public void addSymbols(IJavaProject project, EnhancedSymbolInformation[] enhancedSymbols, - Bean[] beanDefinitions) { + Bean[] beanDefinitions, Map> diagnosticsPerDoc) { if (enhancedSymbols != null) { @@ -208,12 +222,21 @@ public class SpringSymbolIndex implements InitializingBean, SpringIndex { } } + if (diagnosticsPerDoc != null) { + for (String docURI : diagnosticsPerDoc.keySet()) { + server.getTextDocumentService().publishDiagnostics(new TextDocumentIdentifier(docURI), diagnosticsPerDoc.get(docURI)); + // TODO: need to use real TextDocumentIdentifier because of the document version + } + } } @Override public void removeSymbols(IJavaProject project, String docURI) { SpringSymbolIndex.this.removeSymbolsByDoc(project, docURI); springIndex.removeBeans(project.getElementName(), docURI); + + // TODO remove diagnostics ?!? maybe, maybe not + } }; @@ -221,7 +244,13 @@ public class SpringSymbolIndex implements InitializingBean, SpringIndex { Map namespaceHandler = new HashMap<>(); namespaceHandler.put("http://www.springframework.org/schema/beans", new SpringIndexerXMLNamespaceHandlerBeans()); springIndexerXML = new SpringIndexerXML(handler, namespaceHandler, this.cache, projectFinder()); - springIndexerJava = new SpringIndexerJava(handler, specificProviders, this.cache, projectFinder(), server.getProgressService()); + + List reconcilers = new ArrayList<>(); + reconcilers.add(new BeanMethodNotPublicReconciler()); + + BiFunction, BiConsumer, IProblemCollector> problemCollectorFactory = (docRef, aggregator) -> server.createProblemCollector(docRef, aggregator); + springIndexerJava = new SpringIndexerJava(handler, specificProviders, this.cache, projectFinder(), server.getProgressService(), reconcilers, problemCollectorFactory); + factoriesIndexer = new SpringFactoriesIndexer(handler, cache); this.indexers = new SpringIndexer[] {springIndexerJava, factoriesIndexer}; diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaReconcileEngine.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaReconcileEngine.java index bdaf1ff5a..6deb79358 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaReconcileEngine.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaReconcileEngine.java @@ -18,6 +18,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -133,7 +134,7 @@ public class BootJavaReconcileEngine implements IReconcileEngine, IJavaProjectRe .map(docId -> new LazyTextDocument(docId.getUri(), LanguageId.JAVA)).collect(Collectors.toList()); Map problemCollectors = docs.stream() - .collect(Collectors.toMap(d -> d, d -> server.createProblemCollector(d))); + .collect(Collectors.toMap(doc -> doc, doc -> server.createProblemCollector(new AtomicReference<>(doc), null))); problemCollectors.values().forEach(c -> c.beginCollecting()); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/BeanMethodNotPublicReconciler.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/BeanMethodNotPublicReconciler.java new file mode 100644 index 000000000..2c1f81f2a --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/BeanMethodNotPublicReconciler.java @@ -0,0 +1,82 @@ +/******************************************************************************* + * Copyright (c) 2022, 2023 VMware, 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: + * VMware, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.java.reconcilers; + +import java.lang.reflect.Field; +import java.util.List; + +import org.eclipse.jdt.core.dom.Annotation; +import org.eclipse.jdt.core.dom.IMethodBinding; +import org.eclipse.jdt.core.dom.ITypeBinding; +import org.eclipse.jdt.core.dom.MethodDeclaration; +import org.eclipse.jdt.core.dom.Modifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.ide.vscode.boot.java.Annotations; +import org.springframework.ide.vscode.boot.java.Boot2JavaProblemType; +import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.java.SpringProjectUtil; +import org.springframework.ide.vscode.commons.java.Version; +import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector; +import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblemImpl; +import org.springframework.ide.vscode.commons.util.text.IDocument; + +public class BeanMethodNotPublicReconciler implements AnnotationReconciler { + + private static final Logger log = LoggerFactory.getLogger(BeanMethodNotPublicReconciler.class); + + @Override + public void visit(IJavaProject project, IDocument doc, Annotation node, ITypeBinding typeBinding, + IProblemCollector problemCollector) { + + if (Annotations.BEAN.equals(typeBinding.getQualifiedName()) && node.getParent() instanceof MethodDeclaration) { + MethodDeclaration m = (MethodDeclaration) node.getParent(); + Version version = SpringProjectUtil.getDependencyVersion(project, SpringProjectUtil.SPRING_BOOT); + + if (version.getMajor() >= 2) { + IMethodBinding methodBinding = m.resolveBinding(); + if (isNotOverridingPublicMethod(methodBinding)) { + + ReconcileProblemImpl problem = ((List)m.modifiers()).stream() + .filter(Modifier.class::isInstance) + .map(Modifier.class::cast) + .filter(modifier -> modifier.isPublic()) + .findFirst() + .map(modifier -> new ReconcileProblemImpl( + Boot2JavaProblemType.JAVA_PUBLIC_BEAN_METHOD, "super special DIAGNOSTICS with public @Bean method", + modifier.getStartPosition(), modifier.getLength())) + .orElse(new ReconcileProblemImpl( + Boot2JavaProblemType.JAVA_PUBLIC_BEAN_METHOD, "super special DIAGNOSTICS with public @Bean method", + m.getName().getStartPosition(), m.getName().getLength())); + + problemCollector.accept(problem); + } + } + } + } + + private static final boolean isOverriding(IMethodBinding binding) { + try { + Field f = binding.getClass().getDeclaredField("binding"); + f.setAccessible(true); + org.eclipse.jdt.internal.compiler.lookup.MethodBinding value = (org.eclipse.jdt.internal.compiler.lookup.MethodBinding) f.get(binding); + return value.isOverriding(); + } catch (Exception e) { + log.error("", e); + } + return false; + } + + public static final boolean isNotOverridingPublicMethod(IMethodBinding methodBinding) { + return !isOverriding(methodBinding) && (methodBinding.getModifiers() & Modifier.PUBLIC) != 0; + } + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/CachedDiagnostics.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/CachedDiagnostics.java index 76f512635..a0721deb5 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/CachedDiagnostics.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/CachedDiagnostics.java @@ -10,12 +10,20 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.java.reconcilers; +import org.eclipse.lsp4j.Diagnostic; import org.springframework.ide.vscode.boot.index.cache.AbstractIndexCacheable; public class CachedDiagnostics extends AbstractIndexCacheable { - public CachedDiagnostics(String docURI) { + private final Diagnostic diagnostic; + + public CachedDiagnostics(String docURI, Diagnostic diagnostic) { super(docURI); + this.diagnostic= diagnostic; + } + + public Diagnostic getDiagnostic() { + return this.diagnostic; } } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringFactoriesIndexer.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringFactoriesIndexer.java index 2bb6615e8..3e8feabd1 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringFactoriesIndexer.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringFactoriesIndexer.java @@ -187,7 +187,7 @@ public class SpringFactoriesIndexer implements SpringIndexer { if (symbols != null) { EnhancedSymbolInformation[] enhancedSymbols = Arrays.stream(symbols).map(cachedSymbol -> cachedSymbol.getEnhancedSymbol()).toArray(EnhancedSymbolInformation[]::new); - symbolHandler.addSymbols(project, enhancedSymbols, null); + symbolHandler.addSymbols(project, enhancedSymbols, null, null); } long endTime = System.currentTimeMillis(); @@ -257,7 +257,7 @@ public class SpringFactoriesIndexer implements SpringIndexer { this.cache.update(cacheKey, file, updatedDoc.getLastModified(), generatedSymbols, null, CachedSymbol.class); EnhancedSymbolInformation[] symbols = generatedSymbols.stream().map(cachedSymbol -> cachedSymbol.getEnhancedSymbol()).toArray(EnhancedSymbolInformation[]::new); - symbolHandler.addSymbols(project, docURI, symbols, null); + symbolHandler.addSymbols(project, docURI, symbols, null, null); } } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerJava.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerJava.java index fba7f6664..6e87b3e0c 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerJava.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerJava.java @@ -26,6 +26,8 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -45,6 +47,7 @@ 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.eclipse.lsp4j.Diagnostic; import org.eclipse.lsp4j.WorkspaceSymbol; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -55,6 +58,7 @@ import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchyA import org.springframework.ide.vscode.boot.java.beans.CachedBean; import org.springframework.ide.vscode.boot.java.handlers.EnhancedSymbolInformation; import org.springframework.ide.vscode.boot.java.handlers.SymbolProvider; +import org.springframework.ide.vscode.boot.java.reconcilers.AnnotationReconciler; import org.springframework.ide.vscode.boot.java.reconcilers.CachedDiagnostics; import org.springframework.ide.vscode.commons.java.IClasspath; import org.springframework.ide.vscode.commons.java.IClasspathUtil; @@ -62,6 +66,8 @@ import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.languageserver.PercentageProgressTask; import org.springframework.ide.vscode.commons.languageserver.ProgressService; import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; +import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector; +import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblem; import org.springframework.ide.vscode.commons.protocol.spring.Bean; import org.springframework.ide.vscode.commons.util.UriUtil; import org.springframework.ide.vscode.commons.util.text.TextDocument; @@ -89,9 +95,9 @@ public class SpringIndexerJava implements SpringIndexer { private static final String BEANS_KEY = "beans"; private static final String DIAGNOSTICS_KEY = "diagnostics"; - private final SymbolHandler symbolHandler; private final AnnotationHierarchyAwareLookup symbolProviders; + private final List reconcilers; private final IndexCache cache; private final JavaProjectFinder projectFinder; private final ProgressService progressService; @@ -100,14 +106,19 @@ public class SpringIndexerJava implements SpringIndexer { private FileScanListener fileScanListener = null; //used by test code only private final SpringIndexerJavaDependencyTracker dependencyTracker = new SpringIndexerJavaDependencyTracker(); + private final BiFunction, BiConsumer, IProblemCollector> problemCollectorCreator; public SpringIndexerJava(SymbolHandler symbolHandler, AnnotationHierarchyAwareLookup symbolProviders, IndexCache cache, - JavaProjectFinder projectFimder, ProgressService progressService) { + JavaProjectFinder projectFimder, ProgressService progressService, List reconcilers, + BiFunction, BiConsumer, IProblemCollector> problemCollectorCreator) { this.symbolHandler = symbolHandler; this.symbolProviders = symbolProviders; + this.reconcilers = reconcilers; this.cache = cache; this.projectFinder = projectFimder; this.progressService = progressService; + + this.problemCollectorCreator = problemCollectorCreator; } public SpringIndexerJavaDependencyTracker getDependencyTracker() { @@ -259,8 +270,18 @@ public class SpringIndexerJava implements SpringIndexer { AtomicReference docRef = new AtomicReference<>(); String file = UriUtil.toFileString(docURI); + + BiConsumer diagnosticsAggregator = new BiConsumer<>() { + @Override + public void accept(String docURI, Diagnostic diagnostic) { + generatedDiagnostics.add(new CachedDiagnostics(docURI, diagnostic)); + } + }; + + IProblemCollector problemCollector = problemCollectorCreator.apply(docRef, diagnosticsAggregator); + SpringIndexerJavaContext context = new SpringIndexerJavaContext(project, cu, docURI, file, - lastModified, docRef, content, generatedSymbols, generatedBeans, generatedDiagnostics, SCAN_PASS.ONE, new ArrayList<>()); + lastModified, docRef, content, generatedSymbols, generatedBeans, problemCollector, SCAN_PASS.ONE, new ArrayList<>()); scanAST(context); @@ -275,8 +296,9 @@ public class SpringIndexerJava implements SpringIndexer { EnhancedSymbolInformation[] symbols = generatedSymbols.stream().map(cachedSymbol -> cachedSymbol.getEnhancedSymbol()).toArray(EnhancedSymbolInformation[]::new); Bean[] beans = generatedBeans.stream().filter(cachedBean -> cachedBean.getBean() != null).map(cachedBean -> cachedBean.getBean()).toArray(Bean[]::new); + List diagnostics = generatedDiagnostics.stream().filter(cachedDiagnostics -> cachedDiagnostics.getDiagnostic() != null).map(cachedDiagnostic -> cachedDiagnostic.getDiagnostic()).collect(Collectors.toList()); - symbolHandler.addSymbols(project, docURI, symbols, beans); + symbolHandler.addSymbols(project, docURI, symbols, beans, diagnostics); Set scannedFiles = new HashSet<>(); scannedFiles.add(file); @@ -299,22 +321,33 @@ public class SpringIndexerJava implements SpringIndexer { if (cu != null) { List generatedSymbols = new ArrayList(); List generatedBeans = new ArrayList(); - List generatedDiagnostics = new ArrayList(); + + IProblemCollector voidProblemCollector = new IProblemCollector() { + @Override + public void endCollecting() { + } + + @Override + public void beginCollecting() { + } + + @Override + public void accept(ReconcileProblem problem) { + } + }; AtomicReference docRef = new AtomicReference<>(); String file = UriUtil.toFileString(docURI); SpringIndexerJavaContext context = new SpringIndexerJavaContext(project, cu, docURI, file, - 0, docRef, content, generatedSymbols, generatedBeans, generatedDiagnostics, SCAN_PASS.ONE, new ArrayList<>()); + 0, docRef, content, generatedSymbols, generatedBeans, voidProblemCollector, SCAN_PASS.ONE, new ArrayList<>()); scanAST(context); return generatedSymbols.stream().map(s -> s.getEnhancedSymbol()).collect(Collectors.toList()); } - } return Collections.emptyList(); - } private Set scanFilesInternally(IJavaProject project, DocumentDescriptor[] docs) throws Exception { @@ -341,6 +374,13 @@ public class SpringIndexerJava implements SpringIndexer { List generatedDiagnostics = new ArrayList(); Multimap dependencies = MultimapBuilder.hashKeys().hashSetValues().build(); + + BiConsumer diagnosticsAggregator = new BiConsumer<>() { + @Override + public void accept(String docURI, Diagnostic diagnostic) { + generatedDiagnostics.add(new CachedDiagnostics(docURI, diagnostic)); + } + }; FileASTRequestor requestor = new FileASTRequestor() { @Override @@ -353,8 +393,10 @@ public class SpringIndexerJava implements SpringIndexer { AtomicReference docRef = new AtomicReference<>(); + IProblemCollector problemCollector = problemCollectorCreator.apply(docRef, diagnosticsAggregator); + SpringIndexerJavaContext context = new SpringIndexerJavaContext(project, cu, docURI, sourceFilePath, - lastModified, docRef, null, generatedSymbols, generatedBeans, generatedDiagnostics, SCAN_PASS.ONE, new ArrayList<>()); + lastModified, docRef, null, generatedSymbols, generatedBeans, problemCollector, SCAN_PASS.ONE, new ArrayList<>()); scanAST(context); @@ -369,7 +411,8 @@ public class SpringIndexerJava implements SpringIndexer { EnhancedSymbolInformation[] symbols = generatedSymbols.stream().map(cachedSymbol -> cachedSymbol.getEnhancedSymbol()).toArray(EnhancedSymbolInformation[]::new); Bean[] beans = generatedBeans.stream().filter(cachedBean -> cachedBean.getBean() != null).map(cachedBean -> cachedBean.getBean()).toArray(Bean[]::new); - symbolHandler.addSymbols(project, symbols, beans); + Map> diagnosticsByDoc = generatedDiagnostics.stream().filter(cachedDiagnostic -> cachedDiagnostic.getDiagnostic() != null).collect(Collectors.groupingBy(CachedDiagnostics::getDocURI, Collectors.mapping(CachedDiagnostics::getDiagnostic, Collectors.toList()))); + symbolHandler.addSymbols(project, symbols, beans, diagnosticsByDoc); IndexCacheKey symbolsCacheKey = getCacheKey(project, SYMBOL_KEY); IndexCacheKey beansCacheKey = getCacheKey(project, BEANS_KEY); @@ -433,12 +476,19 @@ public class SpringIndexerJava implements SpringIndexer { log.info("scan java files, AST parse, pass 1 for files: {}", javaFiles.length); - String[] pass2Files = scanFiles(project, javaFiles, generatedSymbols, generatedBeans, generatedDiagnostics, SCAN_PASS.ONE); + BiConsumer diagnosticsAggregator = new BiConsumer<>() { + @Override + public void accept(String docURI, Diagnostic diagnostic) { + generatedDiagnostics.add(new CachedDiagnostics(docURI, diagnostic)); + } + }; + + String[] pass2Files = scanFiles(project, javaFiles, generatedSymbols, generatedBeans, diagnosticsAggregator, SCAN_PASS.ONE); if (pass2Files.length > 0) { log.info("scan java files, AST parse, pass 2 for files: {}", javaFiles.length); - scanFiles(project, pass2Files, generatedSymbols, generatedBeans, generatedDiagnostics, SCAN_PASS.TWO); + scanFiles(project, pass2Files, generatedSymbols, generatedBeans, diagnosticsAggregator, SCAN_PASS.TWO); } log.info("scan java files done, number of symbols created: " + generatedSymbols.size()); @@ -465,12 +515,13 @@ public class SpringIndexerJava implements SpringIndexer { if (symbols != null && beans != null) { EnhancedSymbolInformation[] enhancedSymbols = Arrays.stream(symbols).map(cachedSymbol -> cachedSymbol.getEnhancedSymbol()).toArray(EnhancedSymbolInformation[]::new); Bean[] allBeans = Arrays.stream(beans).filter(cachedBean -> cachedBean.getBean() != null).map(cachedBean -> cachedBean.getBean()).toArray(Bean[]::new); - symbolHandler.addSymbols(project, enhancedSymbols, allBeans); + Map> diagnosticsByDoc = Arrays.stream(diagnostics).filter(cachedDiagnostic -> cachedDiagnostic.getDiagnostic() != null).collect(Collectors.groupingBy(CachedDiagnostics::getDocURI, Collectors.mapping(CachedDiagnostics::getDiagnostic, Collectors.toList()))); + symbolHandler.addSymbols(project, enhancedSymbols, allBeans, diagnosticsByDoc); } } private String[] scanFiles(IJavaProject project, String[] javaFiles, List generatedSymbols, List generatedBeans, - List generatedDiagnostics, SCAN_PASS pass) throws Exception { + BiConsumer diagnosticsAggregator, SCAN_PASS pass) throws Exception { PercentageProgressTask progressTask = this.progressService.createPercentageProgressTask(INDEX_FILES_TASK_ID + project.getElementName(), javaFiles.length, "Spring Tools: Indexing Java Sources for '" + project.getElementName() + "'"); @@ -480,6 +531,7 @@ public class SpringIndexerJava implements SpringIndexer { List nextPassFiles = new ArrayList<>(); FileASTRequestor requestor = new FileASTRequestor() { + @Override public void acceptAST(String sourceFilePath, CompilationUnit cu) { File file = new File(sourceFilePath); @@ -487,8 +539,10 @@ public class SpringIndexerJava implements SpringIndexer { long lastModified = file.lastModified(); AtomicReference docRef = new AtomicReference<>(); + IProblemCollector problemCollector = problemCollectorCreator.apply(docRef, diagnosticsAggregator); + SpringIndexerJavaContext context = new SpringIndexerJavaContext(project, cu, docURI, sourceFilePath, - lastModified, docRef, null, generatedSymbols, generatedBeans, generatedDiagnostics, pass, nextPassFiles); + lastModified, docRef, null, generatedSymbols, generatedBeans, problemCollector, pass, nextPassFiles); scanAST(context); progressTask.increment(); @@ -594,6 +648,8 @@ public class SpringIndexerJava implements SpringIndexer { ITypeBinding typeBinding = node.resolveTypeBinding(); if (typeBinding != null) { + + // symbol and index scanning Collection providers = symbolProviders.get(typeBinding); Collection metaAnnotations = AnnotationHierarchies.getMetaAnnotations(typeBinding, symbolProviders::containsKey); @@ -609,6 +665,12 @@ public class SpringIndexerJava implements SpringIndexer { context.getGeneratedSymbols().add(new CachedSymbol(context.getDocURI(), context.getLastModified(), enhancedSymbol)); } } + + // reconciling + for (AnnotationReconciler reconciler : this.reconcilers) { + reconciler.visit(context.getProject(), context.getDocRef().get(), node, typeBinding, context.getProblemCollector()); + } + } else { log.debug("type binding not around: " + context.getDocURI() + " - " + node.toString()); 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 5058a42dc..10ef6daf5 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 @@ -18,9 +18,9 @@ import java.util.concurrent.atomic.AtomicReference; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.ITypeBinding; import org.springframework.ide.vscode.boot.java.beans.CachedBean; -import org.springframework.ide.vscode.boot.java.reconcilers.CachedDiagnostics; import org.springframework.ide.vscode.boot.java.utils.SpringIndexerJava.SCAN_PASS; import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector; import org.springframework.ide.vscode.commons.util.text.TextDocument; /** @@ -37,13 +37,14 @@ public class SpringIndexerJavaContext { private final String content; private final List generatedSymbols; private final List beans; - private final List diagnostics; + private final IProblemCollector getProblemCollector; private final SCAN_PASS pass; private final List nextPassFiles; private final Set dependencies = new HashSet<>(); private final Set scannedTypes = new HashSet<>(); + public SpringIndexerJavaContext( IJavaProject project, CompilationUnit cu, @@ -54,7 +55,7 @@ public class SpringIndexerJavaContext { String content, List generatedSymbols, List beans, - List diagnostics, + IProblemCollector problemCollector, SCAN_PASS pass, List nextPassFiles ) { @@ -67,8 +68,8 @@ public class SpringIndexerJavaContext { this.docRef = docRef; this.content = content; this.generatedSymbols = generatedSymbols; + this.getProblemCollector = problemCollector; this.beans = beans; - this.diagnostics = diagnostics; this.pass = pass; this.nextPassFiles = nextPassFiles; } @@ -109,10 +110,6 @@ public class SpringIndexerJavaContext { return beans; } - public List getDiagnostics() { - return diagnostics; - } - public SCAN_PASS getPass() { return pass; } @@ -146,4 +143,9 @@ public class SpringIndexerJavaContext { dependencies.remove(type); } } + + public IProblemCollector getProblemCollector() { + return this.getProblemCollector; + } + } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerXML.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerXML.java index 48990f86e..e4e9bbcd6 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerXML.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerXML.java @@ -137,7 +137,7 @@ public class SpringIndexerXML implements SpringIndexer { if (symbols != null && beans != null) { EnhancedSymbolInformation[] enhancedSymbols = Arrays.stream(symbols).map(cachedSymbol -> cachedSymbol.getEnhancedSymbol()).toArray(EnhancedSymbolInformation[]::new); Bean[] allBeans = Arrays.stream(beans).filter(cachedBean -> cachedBean.getBean() != null).map(cachedBean -> cachedBean.getBean()).toArray(Bean[]::new); - symbolHandler.addSymbols(project, enhancedSymbols, allBeans); + symbolHandler.addSymbols(project, enhancedSymbols, allBeans, null); } long endTime = System.currentTimeMillis(); @@ -175,7 +175,7 @@ public class SpringIndexerXML implements SpringIndexer { EnhancedSymbolInformation[] symbols = generatedSymbols.stream().map(cachedSymbol -> cachedSymbol.getEnhancedSymbol()).toArray(EnhancedSymbolInformation[]::new); Bean[] beans = generatedBeans.stream().filter(cachedBean -> cachedBean.getBean() != null).map(cachedBean -> cachedBean.getBean()).toArray(Bean[]::new); - symbolHandler.addSymbols(project, docURI, symbols, beans); + symbolHandler.addSymbols(project, docURI, symbols, beans, null); } @Override @@ -202,7 +202,7 @@ public class SpringIndexerXML implements SpringIndexer { EnhancedSymbolInformation[] symbols = generatedSymbols.stream().map(cachedSymbol -> cachedSymbol.getEnhancedSymbol()).toArray(EnhancedSymbolInformation[]::new); Bean[] beans = generatedBeans.stream().filter(cachedBean -> cachedBean.getBean() != null).map(cachedBean -> cachedBean.getBean()).toArray(Bean[]::new); - symbolHandler.addSymbols(project, docURI, symbols, beans); + symbolHandler.addSymbols(project, docURI, symbols, beans, null); } } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SymbolHandler.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SymbolHandler.java index cb61b3e6f..d2f1fa898 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SymbolHandler.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SymbolHandler.java @@ -10,6 +10,10 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.java.utils; +import java.util.List; +import java.util.Map; + +import org.eclipse.lsp4j.Diagnostic; import org.springframework.ide.vscode.boot.java.handlers.EnhancedSymbolInformation; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.protocol.spring.Bean; @@ -19,8 +23,8 @@ import org.springframework.ide.vscode.commons.protocol.spring.Bean; */ public interface SymbolHandler { - void addSymbols(IJavaProject project, String docURI, EnhancedSymbolInformation[] enhancedSymbols, Bean[] beanDefinitions); - void addSymbols(IJavaProject project, EnhancedSymbolInformation[] enhancedSymbols, Bean[] beanDefinitions); + void addSymbols(IJavaProject project, String docURI, EnhancedSymbolInformation[] enhancedSymbols, Bean[] beanDefinitions, List diagnostics); + void addSymbols(IJavaProject project, EnhancedSymbolInformation[] enhancedSymbols, Bean[] beanDefinitions, Map> diagnosticsByDoc); void removeSymbols(IJavaProject project, String docURI);