added bean method container to index for bean methods without a configuration class around

This commit is contained in:
Martin Lippert
2025-03-27 11:57:30 +01:00
parent d1319977e0
commit c2b50b6eb6
7 changed files with 137 additions and 21 deletions

View File

@@ -0,0 +1,44 @@
/*******************************************************************************
* 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.Location;
/**
* This index element is meant to capture bean elements from @Bean annotated
* methods where the containing class is not a configuration class (e.g. Feign config
* classes)
*
* This container element is meant for internal use and not to be displayed
* on the UI, therefore this is not a symbol element.
*
* @author Martin Lippert
*/
public class BeanMethodContainerElement extends AbstractSpringIndexElement {
private final Location location;
private final String type;
public BeanMethodContainerElement(Location location, String type) {
super();
this.location = location;
this.type = type;
}
public Location getLocation() {
return location;
}
public String getType() {
return type;
}
}

View File

@@ -35,6 +35,7 @@ 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.InjectionPoint;
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;
@@ -49,7 +50,7 @@ public class BeansIndexer {
private static final Logger log = LoggerFactory.getLogger(BeansIndexer.class);
public static void indexBeanMethod(Bean configBean, Annotation node, SpringIndexerJavaContext context, TextDocument doc) {
public static void indexBeanMethod(SpringIndexElement parentNode, Annotation node, SpringIndexerJavaContext context, TextDocument doc) {
if (node == null) return;
ASTNode parent = node.getParent();
@@ -89,7 +90,7 @@ public class BeansIndexer {
WebfluxRouterSymbolProvider.createWebfluxElements(beanDefinition, method, context, doc);
}
configBean.addChild(beanDefinition);
parentNode.addChild(beanDefinition);
} catch (BadLocationException e) {
log.error("", e);

View File

@@ -52,6 +52,7 @@ 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.BeanMethodContainerElement;
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;
@@ -201,28 +202,49 @@ public class ComponentSymbolProvider implements SymbolProvider {
}
}
private void indexBeanMethods(Bean bean, TypeDeclaration type, ITypeBinding annotationType, Collection<ITypeBinding> metaAnnotations, SpringIndexerJavaContext context, TextDocument doc) {
private void indexBeanMethods(final Bean bean, TypeDeclaration type, ITypeBinding annotationType, Collection<ITypeBinding> metaAnnotations, SpringIndexerJavaContext context, TextDocument doc) {
AnnotationHierarchies annotationHierarchies = AnnotationHierarchies.get(type);
if (bean.isConfiguration()) {
MethodDeclaration[] methods = type.getMethods();
if (methods == null) {
SpringIndexElement parent = bean;
if (bean == null) {
try {
Location location = new Location(doc.getUri(), doc.toRange(type.getName().getStartPosition(), type.getName().getLength()));
String typeName = type.resolveBinding().getQualifiedName();
parent = new BeanMethodContainerElement(location, typeName);
} catch (BadLocationException e) {
log.error("error while looking up position for type: " + type.toString(), e);
return;
}
for (int i = 0; i < methods.length; i++) {
MethodDeclaration methodDecl = methods[i];
Collection<Annotation> annotations = ASTUtils.getAnnotations(methodDecl);
for (Annotation annotation : annotations) {
ITypeBinding typeBinding = annotation.resolveTypeBinding();
boolean isBeanMethod = annotationHierarchies.isAnnotatedWith(typeBinding, Annotations.BEAN);
if (isBeanMethod) {
BeansIndexer.indexBeanMethod(bean, annotation, context, doc);
}
}
else if (!bean.isConfiguration()) {
return;
}
MethodDeclaration[] methods = type.getMethods();
if (methods == null) {
return;
}
for (int i = 0; i < methods.length; i++) {
MethodDeclaration methodDecl = methods[i];
Collection<Annotation> annotations = ASTUtils.getAnnotations(methodDecl);
for (Annotation annotation : annotations) {
ITypeBinding typeBinding = annotation.resolveTypeBinding();
boolean isBeanMethod = annotationHierarchies.isAnnotatedWith(typeBinding, Annotations.BEAN);
if (isBeanMethod) {
BeansIndexer.indexBeanMethod(parent, annotation, context, doc);
}
}
}
if (bean == null && parent.getChildren().size() > 0) {
context.getBeans().add(new CachedBean(context.getDocURI(), parent));
}
}
private void indexEventListeners(Bean bean, TypeDeclaration type, ITypeBinding annotationType, Collection<ITypeBinding> metaAnnotations, SpringIndexerJavaContext context, TextDocument doc) {
@@ -329,6 +351,7 @@ public class ComponentSymbolProvider implements SymbolProvider {
if (!isComponment) {
indexEventListenerInterfaceImplementation(null, typeDeclaration, context, doc);
indexBeanRegistrarImplementation(null, typeDeclaration, context, doc);
indexBeanMethods(null, typeDeclaration, null, null, context, doc);
}
}

View File

@@ -84,7 +84,7 @@ public class SpringIndexViaLSPMethodTest {
List<Bean> beans = result.get(5, TimeUnit.SECONDS);
assertNotNull(beans);
assertEquals(26, beans.size());
assertEquals(SpringMetamodelIndexingTest.NO_OF_EXPECTED_BEANS, beans.size());
}
@Test
@@ -98,7 +98,7 @@ public class SpringIndexViaLSPMethodTest {
List<Bean> beans = result.get(5, TimeUnit.SECONDS);
assertNotNull(beans);
assertEquals(25, beans.size());
assertEquals(SpringMetamodelIndexingTest.NO_OF_EXPECTED_BEANS - 1, beans.size());
}
@Test

View File

@@ -40,9 +40,12 @@ import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFin
import org.springframework.ide.vscode.commons.protocol.spring.AnnotationAttributeValue;
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.BeanMethodContainerElement;
import org.springframework.ide.vscode.commons.protocol.spring.DefaultValues;
import org.springframework.ide.vscode.commons.protocol.spring.DocumentElement;
import org.springframework.ide.vscode.commons.protocol.spring.InjectionPoint;
import org.springframework.ide.vscode.commons.protocol.spring.SpringIndexElement;
import org.springframework.ide.vscode.commons.protocol.spring.SymbolElement;
import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness;
import org.springframework.ide.vscode.project.harness.ProjectsHarness;
import org.springframework.test.context.junit.jupiter.SpringExtension;
@@ -544,6 +547,34 @@ public class SpringMetamodelIndexerBeansTest {
assertEquals("prof2", profileAttributeValues[1].getName());
assertEquals(new Location(beans[0].getLocation().getUri(), new Range(new Position(9, 19), new Position(9, 26))), profileAttributeValues[1].getLocation());
}
@Test
void testBeanMethodsWithoutConfiguration() {
String docUri = directory.toPath().resolve("src/main/java/org/test/BeanMethodsWithoutConfiguration.java").toUri().toString();
DocumentElement document = springIndex.getDocument(docUri);
List<SpringIndexElement> docChildren = document.getChildren();
assertEquals(1, docChildren.size());
assertTrue(docChildren.get(0) instanceof BeanMethodContainerElement);
BeanMethodContainerElement container = (BeanMethodContainerElement) docChildren.get(0);
assertEquals(docUri, container.getLocation().getUri());
assertEquals("org.test.BeanMethodsWithoutConfiguration", container.getType());
assertFalse(container instanceof SymbolElement);
List<SpringIndexElement> beans = container.getChildren();
assertEquals(2, beans.size());
Bean bean1 = (Bean) beans.get(0);
Bean bean2 = (Bean) beans.get(1);
assertEquals("beanWithoutConfig", bean1.getName());
assertEquals("beanWithoutConfig2", bean2.getName());
Bean[] beansFound = springIndex.getBeansWithName("test-spring-indexing", "beanWithoutConfig");
assertEquals(1, beansFound.length);
assertSame(bean1, beansFound[0]);
}

View File

@@ -46,7 +46,7 @@ import org.springframework.test.context.junit.jupiter.SpringExtension;
@Import(SymbolProviderTestConf.class)
public class SpringMetamodelIndexingTest {
private static final int NO_OF_EXPECTED_BEANS = 26;
public static final int NO_OF_EXPECTED_BEANS = 28;
@Autowired private BootLanguageServerHarness harness;
@Autowired private JavaProjectFinder projectFinder;

View File

@@ -0,0 +1,17 @@
package org.test;
import org.springframework.context.annotation.Bean;
public class BeanMethodsWithoutConfiguration {
@Bean
public BeanClass1 beanWithoutConfig() {
return new BeanClass1();
}
@Bean
public BeanClass2 beanWithoutConfig2() {
return new BeanClass2();
}
}