From ab69e8231e4b9798ecb907305d3a91a912c3d330 Mon Sep 17 00:00:00 2001 From: Martin Lippert Date: Tue, 25 Mar 2025 11:18:14 +0100 Subject: [PATCH] GH-1498: bean registrars are not bean index eleements anymore, but have a dedicated index element instead --- .../protocol/spring/BeanRegistrarElement.java | 45 ++++++++++++++++++ .../java/beans/ComponentSymbolProvider.java | 46 +++++++++---------- .../BeanRegistrarDeclarationReconciler.java | 10 ++-- .../boot/java/utils/SpringIndexerJava.java | 2 +- .../test/SpringIndexerBeanRegistrarTest.java | 18 +++++--- .../BeanRegistrarAdvancedReconcilingTest.java | 2 - 6 files changed, 84 insertions(+), 39 deletions(-) create mode 100644 headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/spring/BeanRegistrarElement.java diff --git a/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/spring/BeanRegistrarElement.java b/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/spring/BeanRegistrarElement.java new file mode 100644 index 000000000..516d1d521 --- /dev/null +++ b/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/spring/BeanRegistrarElement.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2025 Broadcom + * 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: + * Broadcom - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.commons.protocol.spring; + +import org.eclipse.lsp4j.DocumentSymbol; +import org.eclipse.lsp4j.Location; +import org.eclipse.lsp4j.SymbolKind; + +public class BeanRegistrarElement extends AbstractSpringIndexElement implements SymbolElement { + + private final String name; + private final String type; + private final Location location; + + public BeanRegistrarElement(String name, String type, Location location) { + this.name = name; + this.type = type; + this.location = location; + } + + public String getType() { + return type; + } + + public Location getLocation() { + return location; + } + + @Override + public DocumentSymbol getDocumentSymbol() { + return new DocumentSymbol( + name + " (Bean Registrar)", + SymbolKind.Class, + location.getRange(), location.getRange()); + } + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/beans/ComponentSymbolProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/beans/ComponentSymbolProvider.java index 622e1673d..0c8a1c3ed 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/beans/ComponentSymbolProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/beans/ComponentSymbolProvider.java @@ -30,6 +30,7 @@ import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.MethodInvocation; import org.eclipse.jdt.core.dom.RecordDeclaration; +import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.dom.TypeDeclaration; import org.eclipse.lsp4j.Location; import org.eclipse.lsp4j.SymbolKind; @@ -51,9 +52,11 @@ import org.springframework.ide.vscode.boot.java.utils.DefaultSymbolProvider; import org.springframework.ide.vscode.boot.java.utils.SpringIndexerJavaContext; import org.springframework.ide.vscode.commons.protocol.spring.AnnotationMetadata; import org.springframework.ide.vscode.commons.protocol.spring.Bean; +import org.springframework.ide.vscode.commons.protocol.spring.BeanRegistrarElement; import org.springframework.ide.vscode.commons.protocol.spring.DefaultValues; import org.springframework.ide.vscode.commons.protocol.spring.InjectionPoint; import org.springframework.ide.vscode.commons.protocol.spring.SimpleSymbolElement; +import org.springframework.ide.vscode.commons.protocol.spring.SpringIndexElement; import org.springframework.ide.vscode.commons.util.BadLocationException; import org.springframework.ide.vscode.commons.util.text.DocumentRegion; import org.springframework.ide.vscode.commons.util.text.TextDocument; @@ -401,7 +404,7 @@ public class ComponentSymbolProvider implements SymbolProvider { return null; } - private void indexBeanRegistrarImplementation(Bean bean, TypeDeclaration typeDeclaration, SpringIndexerJavaContext context, TextDocument doc) { + private void indexBeanRegistrarImplementation(SpringIndexElement parentNode, TypeDeclaration typeDeclaration, SpringIndexerJavaContext context, TextDocument doc) { try { ITypeBinding typeBinding = typeDeclaration.resolveBinding(); if (typeBinding == null) return; @@ -416,39 +419,32 @@ public class ComponentSymbolProvider implements SymbolProvider { throw new RequiredCompleteAstException(); } - if (bean == null) { // need to create and register bean element - String beanType = typeBinding.getQualifiedName(); - String beanName = BeanUtils.getBeanNameFromType(typeBinding.getName()); + if (parentNode == null) { // need to create and register bean element + String name = typeBinding.getName(); + String type = typeBinding.getQualifiedName(); - Location location = new Location(doc.getUri(), doc.toRange(typeDeclaration.getStartPosition(), typeDeclaration.getLength())); + SimpleName typeNameNode = typeDeclaration.getName(); + Location location = new Location(doc.getUri(), doc.toRange(typeNameNode.getStartPosition(), typeNameNode.getLength())); WorkspaceSymbol symbol = new WorkspaceSymbol( - beanLabel("+", null, null, beanName, beanType), - SymbolKind.Class, + name + " (Bean Registrar)", + SymbolKind.Interface, Either.forLeft(location)); - InjectionPoint[] injectionPoints = ASTUtils.findInjectionPoints(typeDeclaration, doc); - - Set supertypes = new HashSet<>(); - ASTUtils.findSupertypes(typeBinding, supertypes); - - Collection annotationsOnMethod = ASTUtils.getAnnotations(typeDeclaration); - AnnotationMetadata[] annotations = ASTUtils.getAnnotationsMetadata(annotationsOnMethod, doc); - - bean = new Bean(beanName, beanType, location, injectionPoints, supertypes, annotations, false, symbol.getName()); + parentNode = new BeanRegistrarElement(name, type, location); context.getGeneratedSymbols().add(new CachedSymbol(context.getDocURI(), context.getLastModified(), symbol)); - context.getBeans().add(new CachedBean(context.getDocURI(), bean)); + context.getBeans().add(new CachedBean(context.getDocURI(), parentNode)); } - scanBeanRegistryInvocations(bean, registerMethod.getBody(), context, doc); + scanBeanRegistryInvocations(parentNode, registerMethod.getBody(), context, doc); } catch (BadLocationException e) { log.error("", e); } } - private void scanBeanRegistryInvocations(Bean component, Block body, SpringIndexerJavaContext context, TextDocument doc) { + private void scanBeanRegistryInvocations(SpringIndexElement parent, Block body, SpringIndexerJavaContext context, TextDocument doc) { if (body == null) { return; } @@ -491,7 +487,7 @@ public class ComponentSymbolProvider implements SymbolProvider { String beanName = BeanUtils.getBeanNameFromType(typeParameters[0].getName()); String beanType = typeParamName; - createBean(component, beanName, beanType, typeParameters[0], methodInvocation, context, doc); + createBean(parent, beanName, beanType, typeParameters[0], methodInvocation, context, doc); } } else if (arguments.size() == 2 && "java.lang.String".equals(types.get(0).getQualifiedName()) && "java.lang.Class".equals(types.get(1).getBinaryName())) { @@ -505,7 +501,7 @@ public class ComponentSymbolProvider implements SymbolProvider { String typeParamName = typeParameters[0].getBinaryName(); String beanType = typeParamName; - createBean(component, beanName, beanType, typeParameters[0], methodInvocation, context, doc); + createBean(parent, beanName, beanType, typeParameters[0], methodInvocation, context, doc); } } else if (arguments.size() == 2 && "java.lang.Class".equals(types.get(0).getBinaryName()) && "java.util.function.Consumer".equals(types.get(1).getBinaryName())) { @@ -519,7 +515,7 @@ public class ComponentSymbolProvider implements SymbolProvider { String beanName = BeanUtils.getBeanNameFromType(typeParameters[0].getName()); String beanType = typeParamName; - createBean(component, beanName, beanType, typeParameters[0], methodInvocation, context, doc); + createBean(parent, beanName, beanType, typeParameters[0], methodInvocation, context, doc); } } else if (arguments.size() == 3 && "java.lang.String".equals(types.get(0).getQualifiedName()) @@ -534,7 +530,7 @@ public class ComponentSymbolProvider implements SymbolProvider { String typeParamName = typeParameters[0].getBinaryName(); String beanType = typeParamName; - createBean(component, beanName, beanType, typeParameters[0], methodInvocation, context, doc); + createBean(parent, beanName, beanType, typeParameters[0], methodInvocation, context, doc); } } } @@ -548,7 +544,7 @@ public class ComponentSymbolProvider implements SymbolProvider { }); } - public void createBean(Bean parentBean, String beanName, String beanType, ITypeBinding beanTypeBinding, ASTNode node, SpringIndexerJavaContext context, TextDocument doc) throws BadLocationException { + public void createBean(SpringIndexElement parentNode, String beanName, String beanType, ITypeBinding beanTypeBinding, ASTNode node, SpringIndexerJavaContext context, TextDocument doc) throws BadLocationException { Location location = new Location(doc.getUri(), doc.toRange(node.getStartPosition(), node.getLength())); WorkspaceSymbol symbol = new WorkspaceSymbol( @@ -564,7 +560,7 @@ public class ComponentSymbolProvider implements SymbolProvider { AnnotationMetadata[] annotations = DefaultValues.EMPTY_ANNOTATIONS; Bean bean = new Bean(beanName, beanType, location, injectionPoints, supertypes, annotations, false, symbol.getName()); - parentBean.addChild(bean); + parentNode.addChild(bean); } public static String beanLabel(String searchPrefix, String annotationTypeName, Collection metaAnnotationNames, String beanName, String beanType) { diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/BeanRegistrarDeclarationReconciler.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/BeanRegistrarDeclarationReconciler.java index 0ef398fcb..9cc959532 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/BeanRegistrarDeclarationReconciler.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/BeanRegistrarDeclarationReconciler.java @@ -37,6 +37,7 @@ import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixRe import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType; import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblemImpl; import org.springframework.ide.vscode.commons.protocol.spring.Bean; +import org.springframework.ide.vscode.commons.protocol.spring.BeanRegistrarElement; import org.springframework.ide.vscode.commons.protocol.spring.SpringIndexElement; import org.springframework.ide.vscode.commons.rewrite.config.RecipeScope; import org.springframework.ide.vscode.commons.rewrite.java.FixDescriptor; @@ -168,10 +169,11 @@ public class BeanRegistrarDeclarationReconciler implements JdtAstReconciler { Set importedTypesDelta = getImportAnnotationTypesDelta(createdIndexElements, previuosIndexElements); - Arrays.stream(springIndex.getBeans()) - .filter(bean -> bean.isTypeCompatibleWith(Annotations.BEAN_REGISTRAR_INTERFACE)) - .filter(bean -> importedTypesDelta.contains(bean.getType())) - .map(bean -> bean.getLocation().getUri()) + List registrarElements = springIndex.getNodesOfType(BeanRegistrarElement.class); + + registrarElements.stream() + .filter(registrar -> importedTypesDelta.contains(registrar.getType())) + .map(registrar -> registrar.getLocation().getUri()) .map(docURI -> UriUtil.toFileString(docURI)) .forEach(file -> context.markForAffetcedFilesIndexing(file)); } 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 dafc5c4a1..6adbfafcb 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 @@ -92,7 +92,7 @@ public class SpringIndexerJava implements SpringIndexer { // whenever the implementation of the indexer changes in a way that the stored data in the cache is no longer valid, // we need to change the generation - this will result in a re-indexing due to no up-to-date cache data being found - private static final String GENERATION = "GEN-17"; + private static final String GENERATION = "GEN-18"; private static final String INDEX_FILES_TASK_ID = "index-java-source-files-task-"; private static final String SYMBOL_KEY = "symbols"; diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/index/test/SpringIndexerBeanRegistrarTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/index/test/SpringIndexerBeanRegistrarTest.java index 20b916ef4..a3e9c58cb 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/index/test/SpringIndexerBeanRegistrarTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/index/test/SpringIndexerBeanRegistrarTest.java @@ -10,6 +10,7 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.index.test; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -31,8 +32,9 @@ import org.springframework.ide.vscode.boot.bootiful.SymbolProviderTestConf; import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex; import org.springframework.ide.vscode.boot.java.Annotations; import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; -import org.springframework.ide.vscode.commons.protocol.spring.AnnotationMetadata; import org.springframework.ide.vscode.commons.protocol.spring.Bean; +import org.springframework.ide.vscode.commons.protocol.spring.BeanRegistrarElement; +import org.springframework.ide.vscode.commons.protocol.spring.DocumentElement; import org.springframework.ide.vscode.commons.protocol.spring.SpringIndexElement; import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness; import org.springframework.ide.vscode.project.harness.ProjectsHarness; @@ -72,15 +74,17 @@ public class SpringIndexerBeanRegistrarTest { void testSimpleBeanRegistration() throws Exception { String docUri = directory.toPath().resolve("src/main/java/com/example/MyBeanRegistrar.java").toUri().toString(); - Bean[] beans = springIndex.getBeansOfDocument(docUri); - assertEquals(5, beans.length); + Bean[] beansOfDoc = springIndex.getBeansOfDocument(docUri); + assertFalse(Arrays.stream(beansOfDoc).anyMatch(bean -> bean.getName().equals("myBeanRegistrar"))); - Bean beanRegistrarBean = Arrays.stream(beans).filter(bean -> bean.getName().equals("myBeanRegistrar")).findFirst().get(); - assertEquals("com.example.MyBeanRegistrar", beanRegistrarBean.getType()); + DocumentElement document = springIndex.getDocument(docUri); + List docChildren = document.getChildren(); + assertEquals(1, docChildren.size()); + assertTrue(docChildren.get(0) instanceof BeanRegistrarElement); - List children = beanRegistrarBean.getChildren(); + List children = docChildren.get(0).getChildren(); assertEquals(4, children.size()); - + Bean fooFoo = (Bean) children.get(0); assertEquals("fooFoo", fooFoo.getName()); assertEquals("com.example.FooFoo", fooFoo.getType()); diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/reconcilers/test/BeanRegistrarAdvancedReconcilingTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/reconcilers/test/BeanRegistrarAdvancedReconcilingTest.java index b2ad2c5de..1089b265c 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/reconcilers/test/BeanRegistrarAdvancedReconcilingTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/reconcilers/test/BeanRegistrarAdvancedReconcilingTest.java @@ -31,7 +31,6 @@ import org.springframework.context.annotation.Import; import org.springframework.ide.vscode.boot.app.SpringSymbolIndex; import org.springframework.ide.vscode.boot.bootiful.BootLanguageServerTest; import org.springframework.ide.vscode.boot.bootiful.SymbolProviderTestConf; -import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex; import org.springframework.ide.vscode.boot.java.Boot4JavaProblemType; import org.springframework.ide.vscode.boot.java.utils.test.TestFileScanListener; import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; @@ -51,7 +50,6 @@ public class BeanRegistrarAdvancedReconcilingTest { @Autowired private BootLanguageServerHarness harness; @Autowired private JavaProjectFinder projectFinder; @Autowired private SpringSymbolIndex indexer; - @Autowired private SpringMetamodelIndex springIndex; private File directory;