Skip to content

Commit

Permalink
GH-1463: support concatenated strings added to beans symbol provider
Browse files Browse the repository at this point in the history
  • Loading branch information
martinlippert committed Feb 7, 2025
1 parent 0851877 commit ba9a589
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 58 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,14 @@
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"};
Expand All @@ -40,10 +45,10 @@ public static String getBeanNameFromComponentAnnotation(Annotation annotation, T
}

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 {
Expand All @@ -56,14 +61,33 @@ public static Collection<String> getBeanNamesFromBeanAnnotation(Annotation node)
}
}

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) {
Expand All @@ -83,4 +107,14 @@ public static String getBeanName(TypeDeclaration typeDeclaration) {
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();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
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;
Expand All @@ -50,10 +49,7 @@
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
Expand Down Expand Up @@ -95,7 +91,7 @@ public void addSymbols(Annotation node, ITypeBinding typeBinding, Collection<ITy
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()));

Expand Down Expand Up @@ -167,30 +163,6 @@ protected void addSymbolsPass1(TypeDeclaration typeDeclaration, SpringIndexerJav
}
}

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('@');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,29 +106,34 @@ public static DocumentRegion nodeRegion(TextDocument doc, ASTNode node) {
}

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();
}

Expand Down Expand Up @@ -206,13 +211,17 @@ public static String getExpressionValueAsString(Expression exp, Consumer<ITypeBi
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();
Expand Down Expand Up @@ -265,6 +274,15 @@ public static List<StringLiteral> getExpressionValueAsListOfLiterals(Expression
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));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,20 +90,35 @@ void testScanSpecialConfigurationClass() throws Exception {
// @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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,31 @@ public BeanClass specialBean() {
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();
Expand All @@ -31,4 +46,9 @@ public BeanClass specialBean5() {
return new BeanClass();
}

@Bean(name= {"named" + "Bean1" + "Concatenated", "named" + "Bean2" + Constants.SAMPLE_CONSTANT})
public BeanClass specialBean5WithConcatenationAndConstant() {
return new BeanClass();
}

}

0 comments on commit ba9a589

Please sign in to comment.