Skip to content

Commit

Permalink
adjusted handling of named annotation to not take beans without named…
Browse files Browse the repository at this point in the history
… annotatin into account
  • Loading branch information
martinlippert committed Nov 20, 2024
1 parent e1b831e commit 0f854a9
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 75 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,7 @@ public List<AnnotationAttributeProposal> getCompletionCandidates(IJavaProject pr

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());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@
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;
Expand Down Expand Up @@ -64,12 +66,18 @@ public List<LocationLink> getDefinitions(CancelChecker cancelToken, IJavaProject
}

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,6 @@ else if (node instanceof StringLiteral && node.getParent() instanceof MemberValu
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()))
Expand All @@ -95,7 +90,7 @@ private List<? extends Location> provideReferences(IJavaProject project, String
.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();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -95,53 +95,53 @@ public void restoreIndexState() {
}

@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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -33,6 +32,7 @@
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;
Expand Down Expand Up @@ -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 {
Expand All @@ -79,8 +81,14 @@ public void setup() throws Exception {
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});
}
Expand All @@ -92,18 +100,15 @@ public void testNamedClassRefersToBeanDefinitionLink() throws Exception {
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
Expand All @@ -117,19 +122,16 @@ public void testNamedDependencyRefersToBeanDefinitionLink() throws Exception {
@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
Expand All @@ -143,12 +145,12 @@ public void testNamedDependencyRefersToNonExistingBeanDefinitionLink() throws Ex
@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");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -34,8 +35,10 @@
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;
Expand All @@ -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 {
Expand All @@ -74,63 +82,76 @@ public void setup() throws Exception {
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("be<*>an1")
@Named("na<*>med1")
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);
assertEquals(locationNamedAnnotation1, foundLocation);
}

@Test
public void testNamedRefersToOtherNamedValues() throws Exception {
public void testNamedNotRefersToPureSpringBean() throws Exception {
Editor editor = harness.newEditor(LanguageId.JAVA, """
package org.test;
import jakarta.inject.Named;
@Named("specificFin<*>der")
@Named("be<*>an1")
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)));
List<? extends Location> references = editor.getReferences();
assertNull(references);
}

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)));
@Test
public void testNamedRefersToNamedInjectionPoints() throws Exception {
Editor editor = harness.newEditor(LanguageId.JAVA, """
package org.test;
import jakarta.inject.Named;
@Named("namedAt<*>Point1")
public class TestDependsOnClass {
}""", tempJavaDocUri);

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

}

0 comments on commit 0f854a9

Please sign in to comment.