adjusted handling of named annotation to not take beans without named annotatin into account

This commit is contained in:
Martin Lippert
2024-11-20 10:19:35 +01:00
parent e1b831ec08
commit 0f854a97ee
6 changed files with 103 additions and 79 deletions

View File

@@ -39,9 +39,7 @@ public class NamedCompletionProvider implements AnnotationAttributeCompletionPro
Bean[] beans = this.springIndex.getBeansOfProject(project.getElementName());
return Stream.concat(
findAllNamedValues(beans),
Arrays.stream(beans).map(bean -> bean.getName()))
return findAllNamedValues(beans)
.distinct()
.map(beanName -> new AnnotationAttributeProposal(beanName))
.collect(Collectors.toList());

View File

@@ -14,12 +14,14 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
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.StringLiteral;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.LocationLink;
import org.eclipse.lsp4j.TextDocumentIdentifier;
import org.eclipse.lsp4j.jsonrpc.CancelChecker;
@@ -64,12 +66,18 @@ public class NamedDefinitionProvider implements IJavaDefinitionProvider {
}
private List<LocationLink> findBeansWithName(IJavaProject project, String beanName) {
Bean[] beans = this.springIndex.getBeansWithName(project.getElementName(), beanName);
Bean[] beans = this.springIndex.getBeansOfProject(project.getElementName());
return Arrays.stream(beans)
.map(bean -> {
return new LocationLink(bean.getLocation().getUri(), bean.getLocation().getRange(), bean.getLocation().getRange());
})
Stream<Location> namedLocationFromBeans = Arrays.stream(beans)
// annotations from beans themselves
.flatMap(bean -> Arrays.stream(bean.getAnnotations()))
.filter(annotation -> Annotations.NAMED_ANNOTATIONS.contains(annotation.getAnnotationType()))
.filter(annotation -> annotation.getAttributes() != null && annotation.getAttributes().containsKey("value") && annotation.getAttributes().get("value").length == 1)
.filter(annotation -> annotation.getAttributes().get("value")[0].getName().equals(beanName))
.map(annotation -> annotation.getAttributes().get("value")[0].getLocation());
return namedLocationFromBeans
.map(location -> new LocationLink(location.getUri(), location.getRange(), location.getRange()))
.collect(Collectors.toList());
}

View File

@@ -71,11 +71,6 @@ public class NamedReferencesProvider implements ReferenceProvider {
private List<? extends Location> provideReferences(IJavaProject project, String value) {
Bean[] beans = this.springIndex.getBeansOfProject(project.getElementName());
// beans with name
Stream<Location> beanLocations = Arrays.stream(beans)
.filter(bean -> bean.getName().equals(value))
.map(bean -> bean.getLocation());
Stream<Location> namedLocationFromBeans = Arrays.stream(beans)
// annotations from beans themselves
.flatMap(bean -> Arrays.stream(bean.getAnnotations()))
@@ -95,7 +90,7 @@ public class NamedReferencesProvider implements ReferenceProvider {
.filter(annotation -> annotation.getAttributes().get("value")[0].getName().equals(value))
.map(annotation -> annotation.getAttributes().get("value")[0].getLocation());
return Stream.concat(beanLocations, Stream.concat(namedLocationFromBeans, namedLocationsFromInjectionPoints)).toList();
return Stream.concat(namedLocationFromBeans, namedLocationsFromInjectionPoints).toList();
}
}

View File

@@ -95,53 +95,53 @@ public class NamedCompletionProviderTest {
}
@Test
public void testQualifierCompletionWithoutQuotesWithoutPrefix() throws Exception {
assertCompletions("@Named(<*>)", new String[] {"named1", "named2", "bean1", "bean2"}, 0, "@Named(\"named1\"<*>)");
public void testNamedCompletionWithoutQuotesWithoutPrefix() throws Exception {
assertCompletions("@Named(<*>)", new String[] {"named1", "named2"}, 0, "@Named(\"named1\"<*>)");
}
@Test
public void testQualifierCompletionWithoutQuotesWithPrefix() throws Exception {
assertCompletions("@Named(be<*>)", 2, "@Named(\"bean1\"<*>)");
public void testNamedCompletionWithoutQuotesWithPrefix() throws Exception {
assertCompletions("@Named(be<*>)", 0, null);
}
@Test
public void testQualifierCompletionWithoutQuotesWithPrefixFromExistingQualifier() throws Exception {
public void testNamedCompletionWithoutQuotesWithPrefixFromExistingQualifier() throws Exception {
assertCompletions("@Named(na<*>)", new String[] {"named1", "named2"}, 0, "@Named(\"named1\"<*>)");
}
@Test
public void testQualifierCompletionWithoutQuotesWithAttributeName() throws Exception {
assertCompletions("@Named(value=<*>)", 4, "@Named(value=\"named1\"<*>)");
public void testNamedCompletionWithoutQuotesWithAttributeName() throws Exception {
assertCompletions("@Named(value=<*>)", 2, "@Named(value=\"named1\"<*>)");
}
@Test
public void testQualifierCompletionInsideOfQuotesWithoutPrefix() throws Exception {
assertCompletions("@Named(\"<*>\")", 4, "@Named(\"named1<*>\")");
public void testNamedCompletionInsideOfQuotesWithoutPrefix() throws Exception {
assertCompletions("@Named(\"<*>\")", 2, "@Named(\"named1<*>\")");
}
@Test
public void testQualifierCompletionInsideOfQuotesWithPrefix() throws Exception {
assertCompletions("@Named(\"be<*>\")", 2, "@Named(\"bean1<*>\")");
public void testNAmedCompletionInsideOfQuotesWithPrefix() throws Exception {
assertCompletions("@Named(\"na<*>\")", 2, "@Named(\"named1<*>\")");
}
@Test
public void testQualifierCompletionInsideOfQuotesWithPrefixButWithoutMatches() throws Exception {
public void testNamedCompletionInsideOfQuotesWithPrefixButWithoutMatches() throws Exception {
assertCompletions("@Named(\"XXX<*>\")", 0, null);
}
@Test
public void testQualifierCompletionOutsideOfAnnotation1() throws Exception {
public void testNamedCompletionOutsideOfAnnotation1() throws Exception {
assertCompletions("@Named(\"XXX\")<*>", 0, null);
}
@Test
public void testQualifierCompletionOutsideOfAnnotation2() throws Exception {
public void testNamedCompletionOutsideOfAnnotation2() throws Exception {
assertCompletions("@Named<*>(\"XXX\")", 0, null);
}
@Test
public void testQualifierCompletionInsideOfQuotesWithPrefixAndReplacedPostfix() throws Exception {
assertCompletions("@Named(\"be<*>xxx\")", 2, "@Named(\"bean1<*>\")");
public void testNamedCompletionInsideOfQuotesWithPrefixAndReplacedPostfix() throws Exception {
assertCompletions("@Named(\"na<*>xxx\")", 2, "@Named(\"named1<*>\")");
}
private void assertCompletions(String completionLine, int noOfExpectedCompletions, String expectedCompletedLine) throws Exception {

View File

@@ -10,10 +10,9 @@
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.beans.test;
import static org.junit.Assert.assertEquals;
import java.io.File;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
@@ -33,6 +32,7 @@ import org.springframework.ide.vscode.boot.bootiful.SymbolProviderTestConf;
import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex;
import org.springframework.ide.vscode.commons.java.IJavaProject;
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;
import org.springframework.ide.vscode.commons.protocol.spring.Bean;
import org.springframework.ide.vscode.commons.util.text.LanguageId;
@@ -62,6 +62,8 @@ public class NamedDefinitionProviderTest {
private String tempJavaDocUri1;
private String tempJavaDocUri2;
private String tempJavaDocUri;
private Location locationNamedAnnotation1;
private Location locationNamedAnnotation2;
@BeforeEach
public void setup() throws Exception {
@@ -79,8 +81,14 @@ public class NamedDefinitionProviderTest {
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[] {});
locationNamedAnnotation1 = new Location(tempJavaDocUri1, new Range(new Position(1, 10), new Position(1, 20)));
locationNamedAnnotation2 = new Location(tempJavaDocUri2, new Range(new Position(2, 10), new Position(2, 20)));
AnnotationMetadata annotationBean1 = new AnnotationMetadata("jakarta.inject.Named", false, null, Map.of("value", new AnnotationAttributeValue[] {new AnnotationAttributeValue("named1", locationNamedAnnotation1)}));
AnnotationMetadata annotationBean2 = new AnnotationMetadata("jakarta.inject.Named", false, null, Map.of("value", new AnnotationAttributeValue[] {new AnnotationAttributeValue("named2", locationNamedAnnotation2)}));
bean1 = new Bean("bean1", "type1", new Location(tempJavaDocUri1, new Range(new Position(1,1), new Position(1, 20))), null, null, new AnnotationMetadata[] {annotationBean1});
bean2 = new Bean("bean2", "type2", new Location(tempJavaDocUri2, new Range(new Position(1,1), new Position(1, 20))), null, null, new AnnotationMetadata[] {annotationBean2});
springIndex.updateBeans(project.getElementName(), new Bean[] {bean1, bean2});
}
@@ -92,18 +100,15 @@ public class NamedDefinitionProviderTest {
import jakarta.inject.Named;
@Named("bean1")
@Named("named1")
public class TestDependsOnClass {
}""", tempJavaDocUri);
Bean[] beans = springIndex.getBeansWithName(project.getElementName(), "bean1");
assertEquals(1, beans.length);
LocationLink expectedLocation = new LocationLink(tempJavaDocUri1,
beans[0].getLocation().getRange(), beans[0].getLocation().getRange(),
LocationLink expectedLocation = new LocationLink(locationNamedAnnotation1.getUri(),
locationNamedAnnotation1.getRange(), locationNamedAnnotation1.getRange(),
null);
editor.assertLinkTargets("bean1", List.of(expectedLocation));
editor.assertLinkTargets("named1", List.of(expectedLocation));
}
@Test
@@ -117,19 +122,16 @@ public class NamedDefinitionProviderTest {
@Component
public class TestDependsOnClass {
public void setDependency(@Named("bean1") Object bean) {
public void setDependency(@Named("named1") Object bean) {
}
}""", tempJavaDocUri);
Bean[] beans = springIndex.getBeansWithName(project.getElementName(), "bean1");
assertEquals(1, beans.length);
LocationLink expectedLocation = new LocationLink(tempJavaDocUri1,
beans[0].getLocation().getRange(), beans[0].getLocation().getRange(),
LocationLink expectedLocation = new LocationLink(locationNamedAnnotation1.getUri(),
locationNamedAnnotation1.getRange(), locationNamedAnnotation1.getRange(),
null);
editor.assertLinkTargets("bean1", List.of(expectedLocation));
editor.assertLinkTargets("named1", List.of(expectedLocation));
}
@Test
@@ -143,12 +145,12 @@ public class NamedDefinitionProviderTest {
@Component
public class TestDependsOnClass {
public void setDependency(@Named("XXX") Object bean1) {
public void setDependency(@Named("bean1") Object bean1) {
}
}""", tempJavaDocUri1);
editor.assertNoLinkTargets("XXX");
editor.assertNoLinkTargets("bean1");
}
}

View File

@@ -11,14 +11,15 @@
package org.springframework.ide.vscode.boot.java.beans.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.util.List;
import java.util.Map;
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;
@@ -34,8 +35,10 @@ import org.springframework.ide.vscode.boot.bootiful.SymbolProviderTestConf;
import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex;
import org.springframework.ide.vscode.commons.java.IJavaProject;
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;
import org.springframework.ide.vscode.commons.protocol.spring.Bean;
import org.springframework.ide.vscode.commons.protocol.spring.InjectionPoint;
import org.springframework.ide.vscode.commons.util.text.LanguageId;
import org.springframework.ide.vscode.languageserver.testharness.Editor;
import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness;
@@ -61,6 +64,11 @@ public class NamedReferencesProviderTest {
private String tempJavaDocUri1;
private String tempJavaDocUri;
private String tempJavaDocUri2;
private Location locationNamedAnnotation1;
private Location locationNamedAnnotation2;
private Bean bean2;
private Location point1NamedAnnotationLocation;
@BeforeEach
public void setup() throws Exception {
@@ -74,17 +82,47 @@ public class NamedReferencesProviderTest {
CompletableFuture<Void> initProject = indexer.waitOperation();
initProject.get(5, TimeUnit.SECONDS);
tempJavaDocUri = directory.toPath().resolve("src/main/java/org/test/TestDependsOnClass.java").toUri().toString();
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[] {});
locationNamedAnnotation1 = new Location(tempJavaDocUri1, new Range(new Position(1, 10), new Position(1, 20)));
locationNamedAnnotation2 = new Location(tempJavaDocUri2, new Range(new Position(2, 10), new Position(2, 20)));
Bean[] beans = ArrayUtils.add(springIndex.getBeansOfProject(project.getElementName()), bean1);
springIndex.updateBeans(project.getElementName(), beans);
AnnotationMetadata annotationBean1 = new AnnotationMetadata("jakarta.inject.Named", false, null, Map.of("value", new AnnotationAttributeValue[] {new AnnotationAttributeValue("named1", locationNamedAnnotation1)}));
AnnotationMetadata annotationBean2 = new AnnotationMetadata("jakarta.inject.Named", false, null, Map.of("value", new AnnotationAttributeValue[] {new AnnotationAttributeValue("named2", locationNamedAnnotation2)}));
point1NamedAnnotationLocation = new Location(tempJavaDocUri1, new Range(new Position(20, 10), new Position(20, 20)));
AnnotationMetadata point1Metadata = new AnnotationMetadata("jakarta.inject.Named", false, null, Map.of("value", new AnnotationAttributeValue[] {new AnnotationAttributeValue("namedAtPoint1", point1NamedAnnotationLocation)}));
InjectionPoint point1 = new InjectionPoint("point1", "type1", null, new AnnotationMetadata[] {point1Metadata});
bean1 = new Bean("bean1", "type1", new Location(tempJavaDocUri1, new Range(new Position(1,1), new Position(1, 20))), new InjectionPoint[] {point1}, null, new AnnotationMetadata[] {annotationBean1});
bean2 = new Bean("bean2", "type2", new Location(tempJavaDocUri2, new Range(new Position(1,1), new Position(1, 20))), null, null, new AnnotationMetadata[] {annotationBean2});
springIndex.updateBeans(project.getElementName(), new Bean[] {bean1, bean2});
}
@Test
public void testNamedRefersToBean() throws Exception {
public void testNamedRefersToNamedBean() throws Exception {
Editor editor = harness.newEditor(LanguageId.JAVA, """
package org.test;
import jakarta.inject.Named;
@Named("na<*>med1")
public class TestDependsOnClass {
}""", tempJavaDocUri);
List<? extends Location> references = editor.getReferences();
assertEquals(1, references.size());
Location foundLocation = references.get(0);
assertEquals(locationNamedAnnotation1, foundLocation);
}
@Test
public void testNamedNotRefersToPureSpringBean() throws Exception {
Editor editor = harness.newEditor(LanguageId.JAVA, """
package org.test;
@@ -94,43 +132,26 @@ public class NamedReferencesProviderTest {
public class TestDependsOnClass {
}""", tempJavaDocUri);
Bean[] beans = springIndex.getBeansWithName(project.getElementName(), "bean1");
assertEquals(1, beans.length);
Location expectedLocation = new Location(tempJavaDocUri1,
beans[0].getLocation().getRange());
List<? extends Location> references = editor.getReferences();
assertEquals(1, references.size());
Location foundLocation = references.get(0);
assertEquals(expectedLocation, foundLocation);
assertNull(references);
}
@Test
public void testNamedRefersToOtherNamedValues() throws Exception {
public void testNamedRefersToNamedInjectionPoints() throws Exception {
Editor editor = harness.newEditor(LanguageId.JAVA, """
package org.test;
import jakarta.inject.Named;
@Named("specificFin<*>der")
@Named("namedAt<*>Point1")
public class TestDependsOnClass {
}""", tempJavaDocUri);
String expectedDefinitionUri1 = directory.toPath().resolve("src/main/java/org/test/jakarta/SimpleMovieLister.java").toUri().toString();
Location expectedLocation1 = new Location(expectedDefinitionUri1,
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(27, 45), new Position(27, 61)));
List<? extends Location> references = editor.getReferences();
assertEquals(2, references.size());
assertEquals(1, references.size());
assertTrue(references.contains(expectedLocation1));
assertTrue(references.contains(expectedLocation2));
Location foundLocation = references.get(0);
assertEquals(point1NamedAnnotationLocation, foundLocation);
}
}