GH-1381: support Go To Definition for ConditionalOnMissingBean, focus in name attribute, and support value arrays

This commit is contained in:
Martin Lippert
2024-10-10 15:15:07 +02:00
parent 1d02a04a21
commit 54d7ee49f3
2 changed files with 207 additions and 11 deletions

View File

@@ -19,6 +19,8 @@ import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.IAnnotationBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.MemberValuePair;
import org.eclipse.jdt.core.dom.StringLiteral;
import org.eclipse.lsp4j.LocationLink;
import org.eclipse.lsp4j.TextDocumentIdentifier;
@@ -51,19 +53,62 @@ public class ConditionalOnBeanDefinitionProvider implements IJavaDefinitionProvi
if (parent != null && parent instanceof Annotation) {
Annotation a = (Annotation) parent;
IAnnotationBinding binding = a.resolveAnnotationBinding();
if (binding != null && binding.getAnnotationType() != null && Annotations.CONDITIONAL_ON_BEAN.equals(binding.getAnnotationType().getQualifiedName())) {
String beanName = valueNode.getLiteralValue();
if (beanName != null && beanName.length() > 0) {
return findBeansWithName(project, beanName);
}
if (binding != null) {
ITypeBinding annotationType = binding.getAnnotationType();
if (annotationType != null) {
String annotationTypeQualifiedName = annotationType.getQualifiedName();
if (Annotations.CONDITIONAL_ON_BEAN.equals(annotationTypeQualifiedName)
|| Annotations.CONDITIONAL_ON_MISSING_BEAN.equals(annotationTypeQualifiedName)) {
return getDefinitions(project, valueNode);
}
}
}
}
}
return Collections.emptyList();
}
private List<LocationLink> findBeansWithName(IJavaProject project, String beanName) {
private List<LocationLink> getDefinitions(IJavaProject project, StringLiteral valueNode) {
String value = valueNode.getLiteralValue();
if (value != null && value.length() > 0) {
return getDefinitionsForValue(project, valueNode, value);
}
else {
return Collections.emptyList();
}
}
private List<LocationLink> getDefinitionsForValue(IJavaProject project, StringLiteral valueNode, String value) {
ASTNode parent = valueNode.getParent();
if (parent != null && !(parent instanceof MemberValuePair)) {
parent = parent.getParent();
}
if (parent != null && parent instanceof MemberValuePair) {
MemberValuePair pair = (MemberValuePair) parent;
String name = pair.getName().toString();
if ("name".equals(name)) {
return findBeansWithName(project, value);
}
else if ("type".equals(name) ||"ignoredType".equals(name)) {
return findBeanTypesWithName(project, value);
}
}
return Collections.emptyList();
}
private List<LocationLink> findBeanTypesWithName(IJavaProject project, String value) {
// TODO
return Collections.emptyList();
}
private List<LocationLink> findBeansWithName(IJavaProject project, String beanName) {
Bean[] beans = this.springIndex.getBeansWithName(project.getElementName(), beanName);
return Arrays.stream(beans)

View File

@@ -79,24 +79,109 @@ public class ConditionalOnBeanDefinitionProviderTest {
@Configuration
public class TestConditionalOnBeanCompletion {
@ConditionalOnBean(name="bean1")
@ConditionalOnBean(name = "bean1")
@Bean
public void method() {
}
}""", tempJavaDocUri);
String expectedDefinitionUri = directory.toPath().resolve("src/main/java/org/test/MainClass.java").toUri().toString();
Bean[] beans = springIndex.getBeansWithName(project.getElementName(), "bean1");
assertEquals(1, beans.length);
LocationLink expectedLocation = new LocationLink(expectedDefinitionUri,
LocationLink expectedLocation = new LocationLink(beans[0].getLocation().getUri(),
beans[0].getLocation().getRange(), beans[0].getLocation().getRange(),
null);
editor.assertLinkTargets("bean1", List.of(expectedLocation));
}
@Test
public void testConditionalOnMissingBeanWithNameRefersToBeanDefinitionLink() throws Exception {
String tempJavaDocUri = directory.toPath().resolve("src/main/java/org/test/TempClass.java").toUri().toString();
Editor editor = harness.newEditor(LanguageId.JAVA, """
package org.test;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class TestConditionalOnBeanCompletion {
@ConditionalOnMissingBean(name = "bean1")
@Bean
public void method() {
}
}""", tempJavaDocUri);
Bean[] beans = springIndex.getBeansWithName(project.getElementName(), "bean1");
assertEquals(1, beans.length);
LocationLink expectedLocation = new LocationLink(beans[0].getLocation().getUri(),
beans[0].getLocation().getRange(), beans[0].getLocation().getRange(),
null);
editor.assertLinkTargets("bean1", List.of(expectedLocation));
}
@Test
public void testConditionalOnBeanWithNameArrayRefersToBeanDefinitionLink() throws Exception {
String tempJavaDocUri = directory.toPath().resolve("src/main/java/org/test/TempClass.java").toUri().toString();
Editor editor = harness.newEditor(LanguageId.JAVA, """
package org.test;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class TestConditionalOnBeanCompletion {
@ConditionalOnBean(name = {"bean1", "bean2"})
@Bean
public void method() {
}
}""", tempJavaDocUri);
Bean[] beans = springIndex.getBeansWithName(project.getElementName(), "bean2");
assertEquals(1, beans.length);
LocationLink expectedLocation = new LocationLink(beans[0].getLocation().getUri(),
beans[0].getLocation().getRange(), beans[0].getLocation().getRange(),
null);
editor.assertLinkTargets("bean2", List.of(expectedLocation));
}
@Test
public void testConditionalOnMissingBeanWithNameArrayRefersToBeanDefinitionLink() throws Exception {
String tempJavaDocUri = directory.toPath().resolve("src/main/java/org/test/TempClass.java").toUri().toString();
Editor editor = harness.newEditor(LanguageId.JAVA, """
package org.test;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class TestConditionalOnBeanCompletion {
@ConditionalOnMissingBean(name = {"bean1", "bean2"})
@Bean
public void method() {
}
}""", tempJavaDocUri);
Bean[] beans = springIndex.getBeansWithName(project.getElementName(), "bean2");
assertEquals(1, beans.length);
LocationLink expectedLocation = new LocationLink(beans[0].getLocation().getUri(),
beans[0].getLocation().getRange(), beans[0].getLocation().getRange(),
null);
editor.assertLinkTargets("bean2", List.of(expectedLocation));
}
@Test
public void testConditionalOnBeanRefersToRandomBeanWithoutDefinitionLink() throws Exception {
String tempJavaDocUri = directory.toPath().resolve("src/main/java/org/test/TempClass.java").toUri().toString();
@@ -110,7 +195,7 @@ public class ConditionalOnBeanDefinitionProviderTest {
@Configuration
public class TestConditionalOnBeanCompletion {
@ConditionalOnBean(name="bean5")
@ConditionalOnBean(name = "bean5")
@Bean
public void method() {
}
@@ -119,4 +204,70 @@ public class ConditionalOnBeanDefinitionProviderTest {
editor.assertNoLinkTargets("bean5");
}
@Test
public void testConditionalOnMissingBeanRefersToRandomBeanWithoutDefinitionLink() throws Exception {
String tempJavaDocUri = directory.toPath().resolve("src/main/java/org/test/TempClass.java").toUri().toString();
Editor editor = harness.newEditor(LanguageId.JAVA, """
package org.test;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class TestConditionalOnBeanCompletion {
@ConditionalOnMissingBean(name = "bean5")
@Bean
public void method() {
}
}""", tempJavaDocUri);
editor.assertNoLinkTargets("bean5");
}
@Test
public void testConditionalOnBeanUsesTypeInsteadOfBeanNameSoNoDefinitionLink() throws Exception {
String tempJavaDocUri = directory.toPath().resolve("src/main/java/org/test/TempClass.java").toUri().toString();
Editor editor = harness.newEditor(LanguageId.JAVA, """
package org.test;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class TestConditionalOnBeanCompletion {
@ConditionalOnBean(type = "bean1")
@Bean
public void method() {
}
}""", tempJavaDocUri);
editor.assertNoLinkTargets("bean1");
}
@Test
public void testConditionalOnMissingBeanUsesTypeInsteadOfBeanNameSoNoDefinitionLink() throws Exception {
String tempJavaDocUri = directory.toPath().resolve("src/main/java/org/test/TempClass.java").toUri().toString();
Editor editor = harness.newEditor(LanguageId.JAVA, """
package org.test;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class TestConditionalOnBeanCompletion {
@ConditionalOnMissingBean(type = "bean1")
@Bean
public void method() {
}
}""", tempJavaDocUri);
editor.assertNoLinkTargets("bean1");
}
}