GH-1463: support concatenated strings added to beans symbol provider
This commit is contained in:
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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('@');
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user