Skip to content

Commit

Permalink
GH-1405: take setter injection into account while indexing injection …
Browse files Browse the repository at this point in the history
…points
  • Loading branch information
martinlippert committed Nov 13, 2024
1 parent b5fff4e commit 899b737
Show file tree
Hide file tree
Showing 4 changed files with 188 additions and 57 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,63 @@ public static void findSupertypes(ITypeBinding binding, Set<String> supertypesCo

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) {
Expand All @@ -427,70 +484,65 @@ public static InjectionPoint[] findInjectionPoints(MethodDeclaration method, Tex

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

FieldDeclaration[] fields = type.getFields();
for (FieldDeclaration field : fields) {
private static void findInjectionPoints(FieldDeclaration field, TextDocument doc, List<InjectionPoint> result) throws BadLocationException {
boolean autowiredField = false;

List<Annotation> fieldAnnotations = new ArrayList<>();

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;
}
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();
if (!autowiredField) {
return;
}

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);
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();
String fieldType = field.getType().resolveBinding().getQualifiedName();

AnnotationMetadata[] annotationsMetadata = getAnnotationsMetadata(fieldAnnotations, doc);

result.add(new InjectionPoint(fieldName, fieldType, fieldLocation, annotationsMetadata));
}
}
AnnotationMetadata[] annotationsMetadata = getAnnotationsMetadata(fieldAnnotations, doc);

result.add(new InjectionPoint(fieldName, fieldType, fieldLocation, annotationsMetadata));
}
}

return result.size() > 0 ? result.toArray(new InjectionPoint[result.size()]) : DefaultValues.EMPTY_INJECTION_POINTS;
}

public static AnnotationMetadata[] getAnnotationsMetadata(Collection<Annotation> annotations, TextDocument doc) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
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;
Expand Down Expand Up @@ -186,6 +187,53 @@ void testBeanInjectionPointsFromConstructor() {
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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@
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;
Expand Down Expand Up @@ -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
Expand All @@ -78,12 +76,11 @@ public void setup() throws Exception {

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
Expand Down Expand Up @@ -111,7 +108,6 @@ public class TestDependsOnClass {
}

@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;
Expand All @@ -124,11 +120,11 @@ public class TestDependsOnClass {

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());
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}

}

0 comments on commit 899b737

Please sign in to comment.