GH-1405: take setter injection into account while indexing injection points
This commit is contained in:
@@ -412,6 +412,63 @@ public class ASTUtils {
|
||||
|
||||
public static InjectionPoint[] findInjectionPoints(MethodDeclaration method, TextDocument doc) throws BadLocationException {
|
||||
List<InjectionPoint> result = new ArrayList<>();
|
||||
findInjectionPoints(method, doc, result, false);
|
||||
|
||||
return result.size() > 0 ? result.toArray(new InjectionPoint[result.size()]) : DefaultValues.EMPTY_INJECTION_POINTS;
|
||||
}
|
||||
|
||||
public static InjectionPoint[] findInjectionPoints(TypeDeclaration type, TextDocument doc) throws BadLocationException {
|
||||
List<InjectionPoint> result = new ArrayList<>();
|
||||
|
||||
findInjectionPoints(type.getMethods(), doc, result);
|
||||
findInjectionPoints(type.getFields(), doc, result);
|
||||
|
||||
return result.size() > 0 ? result.toArray(new InjectionPoint[result.size()]) : DefaultValues.EMPTY_INJECTION_POINTS;
|
||||
}
|
||||
|
||||
private static void findInjectionPoints(MethodDeclaration[] methods, TextDocument doc, List<InjectionPoint> result) throws BadLocationException {
|
||||
int constructorCount = 0;
|
||||
|
||||
// special rule that if there is a single constructor, it doesn't have to have an autowired or inject annotation on it
|
||||
MethodDeclaration singleConstructor = null;
|
||||
|
||||
for (MethodDeclaration method : methods) {
|
||||
if (method.isConstructor()) {
|
||||
constructorCount++;
|
||||
singleConstructor = method;
|
||||
}
|
||||
}
|
||||
|
||||
if (constructorCount == 1) {
|
||||
findInjectionPoints(singleConstructor, doc, result, false);
|
||||
}
|
||||
|
||||
// look for all methods with annotations (whether constructors or regular methods)
|
||||
for (MethodDeclaration method : methods) {
|
||||
findInjectionPoints(method, doc, result, true);
|
||||
}
|
||||
}
|
||||
|
||||
public static void findInjectionPoints(MethodDeclaration method, TextDocument doc, List<InjectionPoint> result, boolean checkForAnnotation) throws BadLocationException {
|
||||
|
||||
Collection<Annotation> annotationsOnMethod = getAnnotations(method);
|
||||
|
||||
if (checkForAnnotation) {
|
||||
boolean isAutowired = false;
|
||||
|
||||
for (Annotation annotation : annotationsOnMethod) {
|
||||
String qualifiedName = annotation.resolveTypeBinding().getQualifiedName();
|
||||
if (Annotations.AUTOWIRED.equals(qualifiedName)
|
||||
|| Annotations.INJECT_JAVAX.equals(qualifiedName)
|
||||
|| Annotations.INJECT_JAKARTA.equals(qualifiedName)) {
|
||||
isAutowired = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isAutowired) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
List<?> parameters = method.parameters();
|
||||
for (Object object : parameters) {
|
||||
@@ -427,70 +484,65 @@ public class ASTUtils {
|
||||
|
||||
Location location = new Location(doc.getUri(), range);
|
||||
|
||||
AnnotationMetadata[] annotations = getAnnotationsMetadata(getAnnotations(variable), doc);
|
||||
List<Annotation> allAnnotations = new ArrayList<>();
|
||||
allAnnotations.addAll(annotationsOnMethod);
|
||||
allAnnotations.addAll(getAnnotations(variable));
|
||||
|
||||
AnnotationMetadata[] annotations = getAnnotationsMetadata(allAnnotations, doc);
|
||||
|
||||
result.add(new InjectionPoint(name, type, location, annotations));
|
||||
}
|
||||
}
|
||||
|
||||
return result.size() > 0 ? result.toArray(new InjectionPoint[result.size()]) : DefaultValues.EMPTY_INJECTION_POINTS;
|
||||
}
|
||||
|
||||
public static InjectionPoint[] findInjectionPoints(TypeDeclaration type, TextDocument doc) throws BadLocationException {
|
||||
List<InjectionPoint> result = new ArrayList<>();
|
||||
|
||||
MethodDeclaration[] methods = type.getMethods();
|
||||
for (MethodDeclaration method : methods) {
|
||||
if (method.isConstructor()) {
|
||||
result.addAll(Arrays.asList(ASTUtils.findInjectionPoints(method, doc)));
|
||||
private static void findInjectionPoints(FieldDeclaration[] fields, TextDocument doc, List<InjectionPoint> result) throws BadLocationException {
|
||||
for (FieldDeclaration field : fields) {
|
||||
findInjectionPoints(field, doc, result);
|
||||
}
|
||||
}
|
||||
|
||||
private static void findInjectionPoints(FieldDeclaration field, TextDocument doc, List<InjectionPoint> result) throws BadLocationException {
|
||||
boolean autowiredField = false;
|
||||
|
||||
List<Annotation> fieldAnnotations = new ArrayList<>();
|
||||
|
||||
List<?> modifiers = field.modifiers();
|
||||
for (Object modifier : modifiers) {
|
||||
if (modifier instanceof Annotation) {
|
||||
Annotation annotation = (Annotation) modifier;
|
||||
fieldAnnotations.add(annotation);
|
||||
|
||||
String qualifiedName = annotation.resolveTypeBinding().getQualifiedName();
|
||||
if (Annotations.AUTOWIRED.equals(qualifiedName)
|
||||
|| Annotations.INJECT_JAVAX.equals(qualifiedName)
|
||||
|| Annotations.INJECT_JAKARTA.equals(qualifiedName)
|
||||
|| Annotations.VALUE.equals(qualifiedName)) {
|
||||
autowiredField = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FieldDeclaration[] fields = type.getFields();
|
||||
for (FieldDeclaration field : fields) {
|
||||
|
||||
boolean autowiredField = false;
|
||||
|
||||
List<Annotation> fieldAnnotations = new ArrayList<>();
|
||||
|
||||
List<?> modifiers = field.modifiers();
|
||||
for (Object modifier : modifiers) {
|
||||
if (modifier instanceof Annotation) {
|
||||
Annotation annotation = (Annotation) modifier;
|
||||
fieldAnnotations.add(annotation);
|
||||
|
||||
String qualifiedName = annotation.resolveTypeBinding().getQualifiedName();
|
||||
if (Annotations.AUTOWIRED.equals(qualifiedName)
|
||||
|| Annotations.INJECT_JAVAX.equals(qualifiedName)
|
||||
|| Annotations.INJECT_JAKARTA.equals(qualifiedName)
|
||||
|| Annotations.VALUE.equals(qualifiedName)) {
|
||||
autowiredField = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (autowiredField) {
|
||||
List<?> fragments = field.fragments();
|
||||
for (Object fragment : fragments) {
|
||||
if (fragment instanceof VariableDeclarationFragment) {
|
||||
VariableDeclarationFragment varFragment = (VariableDeclarationFragment) fragment;
|
||||
String fieldName = varFragment.getName().toString();
|
||||
|
||||
DocumentRegion region = ASTUtils.nodeRegion(doc, varFragment.getName());
|
||||
Range range = doc.toRange(region);
|
||||
Location fieldLocation = new Location(doc.getUri(), range);
|
||||
|
||||
String fieldType = field.getType().resolveBinding().getQualifiedName();
|
||||
|
||||
AnnotationMetadata[] annotationsMetadata = getAnnotationsMetadata(fieldAnnotations, doc);
|
||||
|
||||
result.add(new InjectionPoint(fieldName, fieldType, fieldLocation, annotationsMetadata));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!autowiredField) {
|
||||
return;
|
||||
}
|
||||
|
||||
return result.size() > 0 ? result.toArray(new InjectionPoint[result.size()]) : DefaultValues.EMPTY_INJECTION_POINTS;
|
||||
List<?> fragments = field.fragments();
|
||||
for (Object fragment : fragments) {
|
||||
if (fragment instanceof VariableDeclarationFragment) {
|
||||
VariableDeclarationFragment varFragment = (VariableDeclarationFragment) fragment;
|
||||
String fieldName = varFragment.getName().toString();
|
||||
|
||||
DocumentRegion region = ASTUtils.nodeRegion(doc, varFragment.getName());
|
||||
Range range = doc.toRange(region);
|
||||
Location fieldLocation = new Location(doc.getUri(), range);
|
||||
|
||||
String fieldType = field.getType().resolveBinding().getQualifiedName();
|
||||
|
||||
AnnotationMetadata[] annotationsMetadata = getAnnotationsMetadata(fieldAnnotations, doc);
|
||||
|
||||
result.add(new InjectionPoint(fieldName, fieldType, fieldLocation, annotationsMetadata));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static AnnotationMetadata[] getAnnotationsMetadata(Collection<Annotation> annotations, TextDocument doc) {
|
||||
|
||||
@@ -35,6 +35,7 @@ import org.springframework.ide.vscode.boot.app.SpringSymbolIndex;
|
||||
import org.springframework.ide.vscode.boot.bootiful.BootLanguageServerTest;
|
||||
import org.springframework.ide.vscode.boot.bootiful.SymbolProviderTestConf;
|
||||
import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex;
|
||||
import org.springframework.ide.vscode.boot.java.Annotations;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.AnnotationAttributeValue;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.AnnotationMetadata;
|
||||
@@ -186,6 +187,53 @@ public class SpringMetamodelIndexerBeansTest {
|
||||
assertEquals(ip2Location, injectionPoints[1].getLocation());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetterInjectionPointsFromConstructor() {
|
||||
Bean[] beans = springIndex.getBeansWithName("test-spring-indexing", "setterInjectionService");
|
||||
assertEquals(1, beans.length);
|
||||
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/injections/SetterInjectionService.java").toUri().toString();
|
||||
|
||||
InjectionPoint[] injectionPoints = beans[0].getInjectionPoints();
|
||||
assertEquals(2, injectionPoints.length);
|
||||
|
||||
assertEquals("bean1", injectionPoints[0].getName());
|
||||
assertEquals("org.test.BeanClass1", injectionPoints[0].getType());
|
||||
assertEquals(new Location(docUri, new Range(new Position(21, 33), new Position(21, 38))), injectionPoints[0].getLocation());
|
||||
|
||||
AnnotationMetadata[] point1Annotations = injectionPoints[0].getAnnotations();
|
||||
assertEquals(2, point1Annotations.length);
|
||||
|
||||
assertEquals(Annotations.AUTOWIRED, point1Annotations[0].getAnnotationType());
|
||||
assertEquals(new Location(docUri, new Range(new Position(19, 1), new Position(19, 11))), point1Annotations[0].getLocation());
|
||||
assertEquals(0, point1Annotations[0].getAttributes().size());
|
||||
|
||||
assertEquals(Annotations.QUALIFIER, point1Annotations[1].getAnnotationType());
|
||||
assertEquals(new Location(docUri, new Range(new Position(20, 1), new Position(20, 41))), point1Annotations[1].getLocation());
|
||||
assertEquals(1, point1Annotations[1].getAttributes().size());
|
||||
assertEquals(1, point1Annotations[1].getAttributes().get("value").length);
|
||||
assertEquals("setter-injection-qualifier", point1Annotations[1].getAttributes().get("value")[0].getName());
|
||||
assertEquals(new Location(docUri, new Range(new Position(20, 12), new Position(20, 40))), point1Annotations[1].getAttributes().get("value")[0].getLocation());
|
||||
|
||||
assertEquals("bean2", injectionPoints[1].getName());
|
||||
assertEquals("org.test.BeanClass2", injectionPoints[1].getType());
|
||||
assertEquals(new Location(docUri, new Range(new Position(26, 83), new Position(26, 88))), injectionPoints[1].getLocation());
|
||||
|
||||
AnnotationMetadata[] point2Annotations = injectionPoints[1].getAnnotations();
|
||||
assertEquals(2, point2Annotations.length);
|
||||
assertEquals(Annotations.AUTOWIRED, point2Annotations[0].getAnnotationType());
|
||||
assertEquals(new Location(docUri, new Range(new Position(25, 1), new Position(25, 11))), point2Annotations[0].getLocation());
|
||||
assertEquals(0, point2Annotations[0].getAttributes().size());
|
||||
|
||||
assertEquals(Annotations.QUALIFIER, point2Annotations[1].getAnnotationType());
|
||||
assertEquals(new Location(docUri, new Range(new Position(26, 22), new Position(26, 71))), point2Annotations[1].getLocation());
|
||||
assertEquals(1, point2Annotations[1].getAttributes().size());
|
||||
assertEquals(1, point2Annotations[1].getAttributes().get("value").length);
|
||||
assertEquals("setter-injection-qualifier-on-param", point2Annotations[1].getAttributes().get("value")[0].getName());
|
||||
assertEquals(new Location(docUri, new Range(new Position(26, 33), new Position(26, 70))), point2Annotations[1].getAttributes().get("value")[0].getLocation());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBeanInjectionPointsFromAutowiredFields() {
|
||||
Bean[] beans = springIndex.getBeansWithName("test-spring-indexing", "autowiredInjectionService");
|
||||
|
||||
@@ -18,12 +18,12 @@ import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.eclipse.lsp4j.Location;
|
||||
import org.eclipse.lsp4j.Position;
|
||||
import org.eclipse.lsp4j.Range;
|
||||
import org.eclipse.lsp4j.TextDocumentIdentifier;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -58,10 +58,8 @@ public class NamedReferencesProviderTest {
|
||||
private File directory;
|
||||
private IJavaProject project;
|
||||
private Bean bean1;
|
||||
private Bean bean2;
|
||||
|
||||
private String tempJavaDocUri1;
|
||||
private String tempJavaDocUri2;
|
||||
private String tempJavaDocUri;
|
||||
|
||||
@BeforeEach
|
||||
@@ -78,12 +76,11 @@ public class NamedReferencesProviderTest {
|
||||
|
||||
tempJavaDocUri = directory.toPath().resolve("src/main/java/org/test/TestDependsOnClass.java").toUri().toString();
|
||||
tempJavaDocUri1 = directory.toPath().resolve("src/main/java/org/test/TempClass1.java").toUri().toString();
|
||||
tempJavaDocUri2 = directory.toPath().resolve("src/main/java/org/test/TempClass2.java").toUri().toString();
|
||||
|
||||
bean1 = new Bean("bean1", "type1", new Location(tempJavaDocUri1, new Range(new Position(1,1), new Position(1, 20))), null, null, new AnnotationMetadata[] {});
|
||||
bean2 = new Bean("bean2", "type2", new Location(tempJavaDocUri2, new Range(new Position(1,1), new Position(1, 20))), null, null, new AnnotationMetadata[] {});
|
||||
|
||||
springIndex.updateBeans(project.getElementName(), new Bean[] {bean1, bean2});
|
||||
|
||||
Bean[] beans = ArrayUtils.add(springIndex.getBeansOfProject(project.getElementName()), bean1);
|
||||
springIndex.updateBeans(project.getElementName(), beans);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -111,7 +108,6 @@ public class NamedReferencesProviderTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled // TODO: need to include setter injection in spring index as a first step, then resurrect this test case
|
||||
public void testNamedRefersToOtherNamedValues() throws Exception {
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA, """
|
||||
package org.test;
|
||||
@@ -124,11 +120,11 @@ public class NamedReferencesProviderTest {
|
||||
|
||||
String expectedDefinitionUri1 = directory.toPath().resolve("src/main/java/org/test/jakarta/SimpleMovieLister.java").toUri().toString();
|
||||
Location expectedLocation1 = new Location(expectedDefinitionUri1,
|
||||
new Range(new Position(24, 38), new Position(24, 62)));
|
||||
new Range(new Position(27, 45), new Position(27, 61)));
|
||||
|
||||
String expectedDefinitionUri2 = directory.toPath().resolve("src/main/java/org/test/javax/SimpleMovieLister.java").toUri().toString();
|
||||
Location expectedLocation2 = new Location(expectedDefinitionUri2,
|
||||
new Range(new Position(24, 38), new Position(24, 62)));
|
||||
new Range(new Position(27, 45), new Position(27, 61)));
|
||||
|
||||
List<? extends Location> references = editor.getReferences();
|
||||
assertEquals(2, references.size());
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package org.test.injections;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.test.BeanClass1;
|
||||
import org.test.BeanClass2;
|
||||
|
||||
@Service
|
||||
public class SetterInjectionService {
|
||||
|
||||
private BeanClass1 bean1;
|
||||
private BeanClass2 bean2;
|
||||
|
||||
private BeanClass1 somethingElse;
|
||||
|
||||
public SetterInjectionService() {
|
||||
}
|
||||
|
||||
@Autowired
|
||||
@Qualifier("setter-injection-qualifier")
|
||||
public void setBean1(BeanClass1 bean1) {
|
||||
this.bean1 = bean1;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public void setBean2(@Qualifier("setter-injection-qualifier-on-param") BeanClass2 bean2) {
|
||||
this.bean2 = bean2;
|
||||
}
|
||||
|
||||
public void setSomethingElse(BeanClass1 somethingElseNotInjected) {
|
||||
this.somethingElse = somethingElseNotInjected;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user