GH-1463: support concatenated strings added to beans symbol provider

This commit is contained in:
Martin Lippert
2025-02-07 08:14:18 +01:00
parent 08518771a8
commit ba9a589122
5 changed files with 117 additions and 58 deletions

View File

@@ -21,9 +21,14 @@ import org.eclipse.jdt.core.dom.StringLiteral;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
import org.springframework.ide.vscode.commons.util.StringUtil;
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 reactor.util.function.Tuple2;
import reactor.util.function.Tuples;
public class BeanUtils {
private static final String[] NAME_ATTRIBUTES = {"value", "name"};
@@ -40,10 +45,10 @@ public class BeanUtils {
}
public static Collection<String> getBeanNamesFromBeanAnnotation(Annotation node) {
Collection<StringLiteral> beanNameNodes = getBeanNameLiterals(node);
Collection<Expression> beanNameNodes = getBeanNameExpressions(node);
if (beanNameNodes != null && !beanNameNodes.isEmpty()) {
return beanNameNodes.stream().map(nameNode -> ASTUtils.getLiteralValue(nameNode))
return beanNameNodes.stream().map(nameNode -> ASTUtils.getExpressionValueAsString(nameNode, v -> {}))
.toList();
}
else {
@@ -56,14 +61,33 @@ public class BeanUtils {
}
}
public static Collection<StringLiteral> getBeanNameLiterals(Annotation node) {
ImmutableList.Builder<StringLiteral> literals = ImmutableList.builder();
for (String attrib : NAME_ATTRIBUTES) {
ASTUtils.getAttribute(node, attrib).ifPresent((valueExp) -> {
literals.addAll(ASTUtils.getExpressionValueAsListOfLiterals(valueExp));
});
public static Collection<Tuple2<String, DocumentRegion>> getBeanNamesFromBeanAnnotationWithRegions(Annotation node, TextDocument doc) {
Collection<Expression> beanNameNodes = getBeanNameExpressions(node);
if (beanNameNodes != null && !beanNameNodes.isEmpty()) {
ImmutableList.Builder<Tuple2<String,DocumentRegion>> namesAndRegions = ImmutableList.builder();
for (Expression nameNode : beanNameNodes) {
String name = ASTUtils.getExpressionValueAsString(nameNode, v -> {});
DocumentRegion region = ASTUtils.nodeRegion(doc, nameNode);
if (nameNode instanceof StringLiteral) {
region = new DocumentRegion(region.getDocument(), region.getStart() + 1, region.getEnd() - 1);
}
namesAndRegions.add(Tuples.of(name, region));
}
return namesAndRegions.build();
}
else {
ASTNode parent = node.getParent();
if (parent instanceof MethodDeclaration) {
MethodDeclaration method = (MethodDeclaration) parent;
return ImmutableList.of(Tuples.of(
method.getName().toString(),
ASTUtils.nameRegion(doc, node)
));
}
return ImmutableList.of();
}
return literals.build();
}
public static String getBeanNameFromType(String typeName) {
@@ -83,4 +107,14 @@ public class BeanUtils {
return BeanUtils.getBeanNameFromType(beanName);
}
private static Collection<Expression> getBeanNameExpressions(Annotation node) {
ImmutableList.Builder<Expression> literals = ImmutableList.builder();
for (String attrib : NAME_ATTRIBUTES) {
ASTUtils.getAttribute(node, attrib).ifPresent((valueExp) -> {
literals.addAll(ASTUtils.expandExpressionsFromPotentialArray(valueExp));
});
}
return literals.build();
}
}

View File

@@ -24,7 +24,6 @@ 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.StringLiteral;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.lsp4j.Location;
@@ -50,10 +49,7 @@ 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;
import com.google.common.collect.ImmutableList;
import reactor.util.function.Tuple2;
import reactor.util.function.Tuples;
/**
* @author Martin Lippert
@@ -95,7 +91,7 @@ public class BeansSymbolProvider extends AbstractSymbolProvider {
ITypeBinding beanType = getBeanType(method);
String markerString = getAnnotations(method);
for (Tuple2<String, DocumentRegion> nameAndRegion : getBeanNames(node, doc)) {
for (Tuple2<String, DocumentRegion> nameAndRegion : BeanUtils.getBeanNamesFromBeanAnnotationWithRegions(node, doc)) {
try {
Location location = new Location(doc.getUri(), doc.toRange(nameAndRegion.getT2()));
@@ -167,30 +163,6 @@ public class BeansSymbolProvider extends AbstractSymbolProvider {
}
}
protected Collection<Tuple2<String, DocumentRegion>> getBeanNames(Annotation node, TextDocument doc) {
Collection<StringLiteral> beanNameNodes = BeanUtils.getBeanNameLiterals(node);
if (beanNameNodes != null && !beanNameNodes.isEmpty()) {
ImmutableList.Builder<Tuple2<String,DocumentRegion>> namesAndRegions = ImmutableList.builder();
for (StringLiteral nameNode : beanNameNodes) {
String name = ASTUtils.getLiteralValue(nameNode);
namesAndRegions.add(Tuples.of(name, ASTUtils.stringRegion(doc, nameNode)));
}
return namesAndRegions.build();
}
else {
ASTNode parent = node.getParent();
if (parent instanceof MethodDeclaration) {
MethodDeclaration method = (MethodDeclaration) parent;
return ImmutableList.of(Tuples.of(
method.getName().toString(),
ASTUtils.nameRegion(doc, node)
));
}
return ImmutableList.of();
}
}
public static String beanLabel(boolean isFunctionBean, String beanName, String beanType, String markerString) {
StringBuilder symbolLabel = new StringBuilder();
symbolLabel.append('@');

View File

@@ -106,29 +106,34 @@ public class ASTUtils {
}
public static Optional<Expression> 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());
}
if (annotation == null) {
return Optional.empty();
}
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);
}
} catch (Exception e) {
log.error("", e);
}
return Optional.empty();
}
@@ -206,13 +211,17 @@ public class ASTUtils {
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) {
if (klass != null) {
dependencies.accept(klass);
}
Object constValue = varBinding.getConstantValue();
if (constValue != null) {
return constValue.toString();
@@ -265,6 +274,15 @@ public class ASTUtils {
return ImmutableList.of();
}
@SuppressWarnings("unchecked")
public static List<Expression> expandExpressionsFromPotentialArray(Expression exp) {
if (exp instanceof ArrayInitializer array) {
return ((List<Expression>)array.expressions());
}
else {
return List.of(exp);
}
}
public static Collection<Annotation> getAnnotations(TypeDeclaration typeDeclaration) {
return getAnnotationsFromModifiers(typeDeclaration.getStructuralProperty(TypeDeclaration.MODIFIERS2_PROPERTY));

View File

@@ -90,20 +90,35 @@ public class SpringIndexerBeansTest {
// @Bean("implicitNamedBean")
SpringIndexerHarness.symbol("implicitNamedBean", "@+ 'implicitNamedBean' (@Bean) BeanClass"),
// @Bean("implicit" + "NamedBean")
SpringIndexerHarness.symbol("\"implicit\" + \"NamedBean\" + \"Concatenated\"", "@+ 'implicitNamedBeanConcatenated' (@Bean) BeanClass"),
// @Bean(value="valueBean")
SpringIndexerHarness.symbol("valueBean", "@+ 'valueBean' (@Bean) BeanClass"),
// @Bean(value="value" + "Bean")
SpringIndexerHarness.symbol("\"valueBean\" + \"Concatenated\"", "@+ 'valueBeanConcatenated' (@Bean) BeanClass"),
// @Bean(value= {"valueBean1", "valueBean2"})
SpringIndexerHarness.symbol("valueBean1", "@+ 'valueBean1' (@Bean) BeanClass"),
SpringIndexerHarness.symbol("valueBean2", "@+ 'valueBean2' (@Bean) BeanClass"),
// @Bean(value= {"value" + "Bean1" + "Concatenated", "valueBean2" + "Concatenated"})
SpringIndexerHarness.symbol("\"value\" + \"Bean1\" + \"Concatenated\"", "@+ 'valueBean1Concatenated' (@Bean) BeanClass"),
SpringIndexerHarness.symbol("\"valueBean2\" + \"Concatenated\"", "@+ 'valueBean2Concatenated' (@Bean) BeanClass"),
// @Bean(name="namedBean")
SpringIndexerHarness.symbol("namedBean", "@+ 'namedBean' (@Bean) BeanClass"),
// @Bean(name= {"namedBean1", "namedBean2"})
SpringIndexerHarness.symbol("namedBean1", "@+ 'namedBean1' (@Bean) BeanClass"),
SpringIndexerHarness.symbol("namedBean2", "@+ 'namedBean2' (@Bean) BeanClass")
);
SpringIndexerHarness.symbol("namedBean2", "@+ 'namedBean2' (@Bean) BeanClass"),
// @Bean(name= {"named" + "Bean1" + "Concatenated", "named" + "Bean2" + Constants.SAMPLE_CONSTANT})
SpringIndexerHarness.symbol("\"named\" + \"Bean1\" + \"Concatenated\"", "@+ 'namedBean1Concatenated' (@Bean) BeanClass"),
SpringIndexerHarness.symbol("\"named\" + \"Bean2\" + Constants.SAMPLE_CONSTANT", "@+ 'namedBean2SampleConstant' (@Bean) BeanClass")
);
}
@Test

View File

@@ -11,16 +11,31 @@ public class SpecialConfiguration {
return new BeanClass();
}
@Bean("implicit" + "NamedBean" + "Concatenated")
public BeanClass specialBeanWithStringConcatenation() {
return new BeanClass();
}
@Bean(value="valueBean")
public BeanClass specialBean2() {
return new BeanClass();
}
@Bean(value="valueBean" + "Concatenated")
public BeanClass specialBean2WithStringConcatenation() {
return new BeanClass();
}
@Bean(value= {"valueBean1", "valueBean2"})
public BeanClass specialBean3() {
return new BeanClass();
}
@Bean(value= {"value" + "Bean1" + "Concatenated", "valueBean2" + "Concatenated"})
public BeanClass specialBean3WithStringConcatenation() {
return new BeanClass();
}
@Bean(name="namedBean")
public BeanClass specialBean4() {
return new BeanClass();
@@ -31,4 +46,9 @@ public class SpecialConfiguration {
return new BeanClass();
}
@Bean(name= {"named" + "Bean1" + "Concatenated", "named" + "Bean2" + Constants.SAMPLE_CONSTANT})
public BeanClass specialBean5WithConcatenationAndConstant() {
return new BeanClass();
}
}