GH-1572: do not treat functional beans different anymore

Fixes GH-1572
This commit is contained in:
Martin Lippert
2025-06-03 11:23:42 +02:00
parent 28617ed96e
commit 67789b730e
7 changed files with 48 additions and 107 deletions

View File

@@ -20,8 +20,6 @@ import org.eclipse.jdt.core.dom.IAnnotationBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.Modifier;
import org.eclipse.jdt.core.dom.ParameterizedType;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.lsp4j.Location;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -29,7 +27,6 @@ import org.springframework.ide.vscode.boot.java.Annotations;
import org.springframework.ide.vscode.boot.java.reconcilers.RequiredCompleteAstException;
import org.springframework.ide.vscode.boot.java.requestmapping.WebfluxRouterSymbolProvider;
import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
import org.springframework.ide.vscode.boot.java.utils.FunctionUtils;
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;
@@ -65,7 +62,6 @@ public class BeansIndexer {
}
}
boolean isFunction = isFunctionBean(method);
ITypeBinding beanType = getBeanType(method);
String markerString = getAnnotations(method);
@@ -73,7 +69,7 @@ public class BeansIndexer {
try {
Location location = new Location(doc.getUri(), doc.toRange(nameAndRegion.getT2()));
String beanLabel = beanLabel(isFunction, nameAndRegion.getT1(), beanType.getName(), "@Bean" + markerString);
String beanLabel = beanLabel(nameAndRegion.getT1(), beanType.getName(), "@Bean" + markerString);
InjectionPoint[] injectionPoints = ASTUtils.findInjectionPoints(method, doc);
@@ -96,10 +92,10 @@ public class BeansIndexer {
}
}
public static String beanLabel(boolean isFunctionBean, String beanName, String beanType, String markerString) {
public static String beanLabel(String beanName, String beanType, String markerString) {
StringBuilder symbolLabel = new StringBuilder();
symbolLabel.append('@');
symbolLabel.append(isFunctionBean ? '>' : '+');
symbolLabel.append('+');
symbolLabel.append(' ');
symbolLabel.append('\'');
symbolLabel.append(beanName);
@@ -116,23 +112,6 @@ public class BeansIndexer {
return method.getReturnType2().resolveBinding();
}
public static boolean isFunctionBean(MethodDeclaration method) {
String returnType = null;
if (method.getReturnType2().isParameterizedType()) {
ParameterizedType paramType = (ParameterizedType) method.getReturnType2();
Type type = paramType.getType();
ITypeBinding typeBinding = type.resolveBinding();
returnType = typeBinding.getBinaryName();
}
else {
returnType = method.getReturnType2().resolveBinding().getQualifiedName();
}
return FunctionUtils.FUNCTION_FUNCTION_TYPE.equals(returnType) || FunctionUtils.FUNCTION_CONSUMER_TYPE.equals(returnType)
|| FunctionUtils.FUNCTION_SUPPLIER_TYPE.equals(returnType);
}
public static String getAnnotations(MethodDeclaration method) {
StringBuilder result = new StringBuilder();

View File

@@ -11,13 +11,11 @@
package org.springframework.ide.vscode.boot.java.beans;
import java.util.Collection;
import java.util.Set;
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.MethodDeclaration;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.SymbolKind;
import org.eclipse.lsp4j.WorkspaceSymbol;
@@ -25,13 +23,8 @@ import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.boot.java.handlers.SymbolProvider;
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.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.util.BadLocationException;
import org.springframework.ide.vscode.commons.util.text.DocumentRegion;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
@@ -56,8 +49,6 @@ public class BeansSymbolProvider implements SymbolProvider {
MethodDeclaration method = (MethodDeclaration) parent;
if (BeansIndexer.isMethodAbstract(method)) return;
boolean isFunction = BeansIndexer.isFunctionBean(method);
ITypeBinding beanType = BeansIndexer.getBeanType(method);
String markerString = BeansIndexer.getAnnotations(method);
@@ -66,7 +57,7 @@ public class BeansSymbolProvider implements SymbolProvider {
Location location = new Location(doc.getUri(), doc.toRange(nameAndRegion.getT2()));
WorkspaceSymbol symbol = new WorkspaceSymbol(
BeansIndexer.beanLabel(isFunction, nameAndRegion.getT1(), beanType.getName(), "@Bean" + markerString),
BeansIndexer.beanLabel(nameAndRegion.getT1(), beanType.getName(), "@Bean" + markerString),
SymbolKind.Interface,
Either.forLeft(location)
);
@@ -79,41 +70,4 @@ public class BeansSymbolProvider implements SymbolProvider {
}
}
@Override
public void addSymbols(TypeDeclaration typeDeclaration, SpringIndexerJavaContext context, TextDocument doc) {
indexFunctionBeans(typeDeclaration, context, doc);
}
private void indexFunctionBeans(TypeDeclaration typeDeclaration, SpringIndexerJavaContext context, TextDocument doc) {
ITypeBinding functionBean = FunctionUtils.getFunctionBean(typeDeclaration, doc);
if (functionBean != null) {
try {
String beanName = BeanUtils.getBeanName(typeDeclaration);
ITypeBinding beanType = functionBean;
Location beanLocation = new Location(doc.getUri(), doc.toRange(ASTUtils.nodeRegion(doc, typeDeclaration.getName())));
WorkspaceSymbol symbol = new WorkspaceSymbol(
BeansIndexer.beanLabel(true, beanName, beanType.getName(), null),
SymbolKind.Interface,
Either.forLeft(beanLocation));
context.getGeneratedSymbols().add(new CachedSymbol(context.getDocURI(), context.getLastModified(), symbol));
ITypeBinding concreteBeanType = typeDeclaration.resolveBinding();
Set<String> supertypes = ASTUtils.findSupertypes(concreteBeanType);
Collection<Annotation> annotationsOnTypeDeclaration = ASTUtils.getAnnotations(typeDeclaration);
AnnotationMetadata[] annotations = ASTUtils.getAnnotationsMetadata(annotationsOnTypeDeclaration, doc);
InjectionPoint[] injectionPoints = ASTUtils.findInjectionPoints(typeDeclaration, doc);
Bean beanDefinition = new Bean(beanName, concreteBeanType.getQualifiedName(), beanLocation, injectionPoints, supertypes, annotations, false, symbol.getName());
context.getBeans().add(new CachedBean(context.getDocURI(), beanDefinition));
} catch (BadLocationException e) {
log.error("", e);
}
}
}
}

View File

@@ -104,7 +104,7 @@ public class ComponentSymbolProvider implements SymbolProvider {
Location location = new Location(doc.getUri(), doc.toRange(node.getStartPosition(), node.getLength()));
WorkspaceSymbol symbol = new WorkspaceSymbol(
beanLabel("+", annotationTypeName, metaAnnotationNames, beanName, beanType.getName()), SymbolKind.Interface,
beanLabel(annotationTypeName, metaAnnotationNames, beanName, beanType.getName()), SymbolKind.Interface,
Either.forLeft(location));
boolean isConfiguration = Annotations.CONFIGURATION.equals(annotationType.getQualifiedName())
@@ -166,7 +166,7 @@ public class ComponentSymbolProvider implements SymbolProvider {
Location location = new Location(doc.getUri(), doc.toRange(node.getStartPosition(), node.getLength()));
WorkspaceSymbol symbol = new WorkspaceSymbol(
beanLabel("+", annotationTypeName, metaAnnotationNames, beanName, beanType.getName()), SymbolKind.Interface,
beanLabel(annotationTypeName, metaAnnotationNames, beanName, beanType.getName()), SymbolKind.Interface,
Either.forLeft(location));
boolean isConfiguration = Annotations.CONFIGURATION.equals(annotationType.getQualifiedName())
@@ -583,7 +583,7 @@ public class ComponentSymbolProvider implements SymbolProvider {
Location location = new Location(doc.getUri(), doc.toRange(node.getStartPosition(), node.getLength()));
WorkspaceSymbol symbol = new WorkspaceSymbol(
beanLabel("+", null, null, beanName, beanType),
beanLabel(null, null, beanName, beanType),
SymbolKind.Class,
Either.forLeft(location));
context.getGeneratedSymbols().add(new CachedSymbol(context.getDocURI(), context.getLastModified(), symbol));
@@ -597,10 +597,10 @@ public class ComponentSymbolProvider implements SymbolProvider {
parentNode.addChild(bean);
}
public static String beanLabel(String searchPrefix, String annotationTypeName, Collection<String> metaAnnotationNames, String beanName, String beanType) {
public static String beanLabel(String annotationTypeName, Collection<String> metaAnnotationNames, String beanName, String beanType) {
StringBuilder symbolLabel = new StringBuilder();
symbolLabel.append("@");
symbolLabel.append(searchPrefix);
symbolLabel.append('@');
symbolLabel.append('+');
symbolLabel.append(' ');
symbolLabel.append('\'');
symbolLabel.append(beanName);

View File

@@ -88,7 +88,7 @@ public class ConfigurationPropertiesSymbolProvider implements SymbolProvider {
Location location = new Location(doc.getUri(), doc.toRange(node.getStartPosition(), node.getLength()));
WorkspaceSymbol symbol = new WorkspaceSymbol(
ComponentSymbolProvider.beanLabel("+", annotationTypeName, metaAnnotationNames, beanName, typeBinding.getName()), SymbolKind.Interface,
ComponentSymbolProvider.beanLabel(annotationTypeName, metaAnnotationNames, beanName, typeBinding.getName()), SymbolKind.Interface,
Either.forLeft(location));
boolean isConfiguration = false; // otherwise, the ComponentSymbolProvider takes care of the bean definiton for this type

View File

@@ -154,7 +154,7 @@ public class SpringFactoriesIndexer implements SpringIndexer {
Location location = new Location(docURI, range);
WorkspaceSymbol symbol = new WorkspaceSymbol(
BeansIndexer.beanLabel(false, beanId, fqName, Paths.get(URI.create(docURI)).getFileName().toString()),
BeansIndexer.beanLabel(beanId, fqName, Paths.get(URI.create(docURI)).getFileName().toString()),
SymbolKind.Interface,
Either.forLeft(location));

View File

@@ -68,7 +68,7 @@ public class SpringIndexerFunctionBeansTest {
String docUri = directory.toPath().resolve("src/main/java/org/test/FunctionClass.java").toUri().toString();
SpringIndexerHarness.assertDocumentSymbols(indexer, docUri,
SpringIndexerHarness.symbol("@Configuration", "@+ 'functionClass' (@Configuration <: @Component) FunctionClass"),
SpringIndexerHarness.symbol("@Bean", "@> 'uppercase' (@Bean) Function<String,String>")
SpringIndexerHarness.symbol("@Bean", "@+ 'uppercase' (@Bean) Function<String,String>")
);
Bean[] beans = springIndex.getBeansOfDocument(docUri);
@@ -84,64 +84,57 @@ public class SpringIndexerFunctionBeansTest {
@Test
void testScanSimpleFunctionClass() throws Exception {
String docUri = directory.toPath().resolve("src/main/java/org/test/ScannedFunctionClass.java").toUri().toString();
SpringIndexerHarness.assertDocumentSymbols(indexer, docUri);
assertEquals(0, springIndex.getBeansOfDocument(docUri).length);
}
@Test
void testScanSimpleFunctionClassWithComponentAnnotation() throws Exception {
String docUri = directory.toPath().resolve("src/main/java/org/test/ScannedFunctionClassWithAnnotation.java").toUri().toString();
SpringIndexerHarness.assertDocumentSymbols(indexer, docUri,
SpringIndexerHarness.symbol("ScannedFunctionClass", "@> 'scannedFunctionClass' Function<String,String>")
SpringIndexerHarness.symbol("@Component", "@+ 'scannedFunctionClassWithAnnotation' (@Component) ScannedFunctionClassWithAnnotation")
);
Bean[] beans = springIndex.getBeansOfDocument(docUri);
assertEquals(1, beans.length);
Bean functionClassBean = Arrays.stream(beans).filter(bean -> bean.getName().equals("scannedFunctionClass")).findFirst().get();
Bean functionClassBean = Arrays.stream(beans).filter(bean -> bean.getName().equals("scannedFunctionClassWithAnnotation")).findFirst().get();
assertEquals("org.test.ScannedFunctionClass", functionClassBean.getType());
assertEquals("org.test.ScannedFunctionClassWithAnnotation", functionClassBean.getType());
}
@Test
void testScanSpecializedFunctionClass() throws Exception {
String docUri = directory.toPath().resolve("src/main/java/org/test/FunctionFromSpecializedClass.java").toUri().toString();
SpringIndexerHarness.assertDocumentSymbols(indexer, docUri,
SpringIndexerHarness.symbol("FunctionFromSpecializedClass", "@> 'functionFromSpecializedClass' Function<String,String>")
);
Bean[] beans = springIndex.getBeansOfDocument(docUri);
assertEquals(1, beans.length);
Bean functionClassBean = Arrays.stream(beans).filter(bean -> bean.getName().equals("functionFromSpecializedClass")).findFirst().get();
assertEquals("org.test.FunctionFromSpecializedClass", functionClassBean.getType());
SpringIndexerHarness.assertDocumentSymbols(indexer, docUri);
assertEquals(0, springIndex.getBeansOfDocument(docUri).length);
}
@Test
void testScanSpecializedFunctionInterface() throws Exception {
String docUri = directory.toPath().resolve("src/main/java/org/test/FunctionFromSpecializedInterface.java").toUri().toString();
SpringIndexerHarness.assertDocumentSymbols(indexer, docUri,
SpringIndexerHarness.symbol("FunctionFromSpecializedInterface", "@> 'functionFromSpecializedInterface' Function<String,String>")
);
Bean[] beans = springIndex.getBeansOfDocument(docUri);
assertEquals(1, beans.length);
Bean functionClassBean = Arrays.stream(beans).filter(bean -> bean.getName().equals("functionFromSpecializedInterface")).findFirst().get();
assertEquals("org.test.FunctionFromSpecializedInterface", functionClassBean.getType());
SpringIndexerHarness.assertDocumentSymbols(indexer, docUri);
assertEquals(0, springIndex.getBeansOfDocument(docUri).length);
}
@Test
void testNoSymbolForAbstractClasses() throws Exception {
String docUri = directory.toPath().resolve("src/main/java/org/test/SpecializedFunctionClass.java").toUri().toString();
SpringIndexerHarness.assertDocumentSymbols(indexer, docUri);
Bean[] beans = springIndex.getBeansOfDocument(docUri);
assertEquals(0, beans.length);
assertEquals(0, springIndex.getBeansOfDocument(docUri).length);
}
@Test
void testNoSymbolForSubInterfaces() throws Exception {
String docUri = directory.toPath().resolve("src/main/java/org/test/SpecializedFunctionInterface.java").toUri().toString();
SpringIndexerHarness.assertDocumentSymbols(indexer, docUri);
Bean[] beans = springIndex.getBeansOfDocument(docUri);
assertEquals(0, beans.length);
SpringIndexerHarness.assertDocumentSymbols(indexer, docUri);
assertEquals(0, springIndex.getBeansOfDocument(docUri).length);
}
}

View File

@@ -0,0 +1,15 @@
package org.test;
import java.util.function.Function;
import org.springframework.stereotype.Component;
@Component
public class ScannedFunctionClassWithAnnotation implements Function<String, String> {
@Override
public String apply(String t) {
return t.toUpperCase();
}
}