GH-1298: added bean name code completion and navigation support for name attribute of resource annotation

This commit is contained in:
Martin Lippert
2024-07-19 14:36:22 +02:00
parent 6b0bd5da5a
commit bc5006ff14
7 changed files with 478 additions and 20 deletions

View File

@@ -28,6 +28,7 @@ import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchie
import org.springframework.ide.vscode.boot.java.beans.DependsOnCompletionProcessor;
import org.springframework.ide.vscode.boot.java.beans.ProfileCompletionProvider;
import org.springframework.ide.vscode.boot.java.beans.QualifierCompletionProvider;
import org.springframework.ide.vscode.boot.java.beans.ResourceCompletionProvider;
import org.springframework.ide.vscode.boot.java.data.DataRepositoryCompletionProcessor;
import org.springframework.ide.vscode.boot.java.handlers.BootJavaCompletionEngine;
import org.springframework.ide.vscode.boot.java.handlers.CompletionProvider;
@@ -121,6 +122,9 @@ public class BootJavaCompletionEngineConfigurer {
providers.put(Annotations.QUALIFIER, new AnnotationAttributeCompletionProcessor(javaProjectFinder, Map.of("value", new QualifierCompletionProvider(springIndex))));
providers.put(Annotations.PROFILE, new AnnotationAttributeCompletionProcessor(javaProjectFinder, Map.of("value", new ProfileCompletionProvider(springIndex))));
providers.put(Annotations.RESOURCE_JAVAX, new AnnotationAttributeCompletionProcessor(javaProjectFinder, Map.of("name", new ResourceCompletionProvider(springIndex))));
providers.put(Annotations.RESOURCE_JAKARTA, new AnnotationAttributeCompletionProcessor(javaProjectFinder, Map.of("name", new ResourceCompletionProvider(springIndex))));
return new BootJavaCompletionEngine(cuCache, providers, snippetManager);
}

View File

@@ -47,6 +47,7 @@ import org.springframework.ide.vscode.boot.index.cache.IndexCacheVoid;
import org.springframework.ide.vscode.boot.java.JavaDefinitionHandler;
import org.springframework.ide.vscode.boot.java.beans.DependsOnDefinitionProvider;
import org.springframework.ide.vscode.boot.java.beans.QualifierDefinitionProvider;
import org.springframework.ide.vscode.boot.java.beans.ResourceDefinitionProvider;
import org.springframework.ide.vscode.boot.java.handlers.BootJavaCodeActionProvider;
import org.springframework.ide.vscode.boot.java.handlers.BootJavaReconcileEngine;
import org.springframework.ide.vscode.boot.java.handlers.JavaCodeActionHandler;
@@ -397,6 +398,7 @@ public class BootLanguageServerBootApp {
return new JavaDefinitionHandler(cuCache, projectFinder, List.of(
new ValueDefinitionProvider(),
new DependsOnDefinitionProvider(springIndex),
new ResourceDefinitionProvider(springIndex),
new QualifierDefinitionProvider(springIndex)));
}

View File

@@ -61,39 +61,41 @@ public class AnnotationAttributeCompletionProcessor implements CompletionProvide
// case: @Qualifier(<*>)
if (node == annotation && doc.get(offset - 1, 2).endsWith("()")) {
createCompletionProposals(project, doc, node, completions, offset, offset, "", (beanName) -> "\"" + beanName + "\"");
createCompletionProposals(project, doc, node, "value", completions, offset, offset, "", (beanName) -> "\"" + beanName + "\"");
}
// case: @Qualifier(prefix<*>)
else if (node instanceof SimpleName && node.getParent() instanceof Annotation) {
computeProposalsForSimpleName(project, node, completions, offset, doc);
computeProposalsForSimpleName(project, node, "value", completions, offset, doc);
}
// case: @Qualifier(value=<*>)
else if (node instanceof SimpleName && node.getParent() instanceof MemberValuePair
&& "value".equals(((MemberValuePair)node.getParent()).getName().toString())) {
computeProposalsForSimpleName(project, node, completions, offset, doc);
&& completionProviders.containsKey(((MemberValuePair)node.getParent()).getName().toString())) {
String attributeName = ((MemberValuePair)node.getParent()).getName().toString();
computeProposalsForSimpleName(project, node, attributeName, completions, offset, doc);
}
// case: @Qualifier("prefix<*>")
else if (node instanceof StringLiteral && node.getParent() instanceof Annotation) {
if (node.toString().startsWith("\"") && node.toString().endsWith("\"")) {
computeProposalsForStringLiteral(project, node, completions, offset, doc);
computeProposalsForStringLiteral(project, node, "value", completions, offset, doc);
}
}
// case: @Qualifier({"prefix<*>"})
else if (node instanceof StringLiteral && node.getParent() instanceof ArrayInitializer) {
if (node.toString().startsWith("\"") && node.toString().endsWith("\"")) {
computeProposalsForInsideArrayInitializer(project, node, completions, offset, doc);
computeProposalsForInsideArrayInitializer(project, node, "value", completions, offset, doc);
}
}
// case: @Qualifier(value="prefix<*>")
else if (node instanceof StringLiteral && node.getParent() instanceof MemberValuePair
&& "value".equals(((MemberValuePair)node.getParent()).getName().toString())) {
&& completionProviders.containsKey(((MemberValuePair)node.getParent()).getName().toString())) {
if (node.toString().startsWith("\"") && node.toString().endsWith("\"")) {
computeProposalsForStringLiteral(project, node, completions, offset, doc);
String attributeName = ((MemberValuePair)node.getParent()).getName().toString();
computeProposalsForStringLiteral(project, node, attributeName, completions, offset, doc);
}
}
// case: @Qualifier({<*>})
else if (node instanceof ArrayInitializer && node.getParent() instanceof Annotation) {
computeProposalsForArrayInitializr(project, (ArrayInitializer) node, completions, offset, doc);
computeProposalsForArrayInitializr(project, (ArrayInitializer) node, "value", completions, offset, doc);
}
}
catch (Exception e) {
@@ -104,12 +106,12 @@ public class AnnotationAttributeCompletionProcessor implements CompletionProvide
/**
* create the concrete completion proposal
*/
private void createCompletionProposals(IJavaProject project, TextDocument doc, ASTNode node, Collection<ICompletionProposal> completions, int startOffset, int endOffset,
private void createCompletionProposals(IJavaProject project, TextDocument doc, ASTNode node, String attributeName, Collection<ICompletionProposal> completions, int startOffset, int endOffset,
String filterPrefix, Function<String, String> createReplacementText) {
Set<String> alreadyMentionedValues = alreadyMentionedValues(node);
AnnotationAttributeCompletionProvider completionProvider = this.completionProviders.get("value");
AnnotationAttributeCompletionProvider completionProvider = this.completionProviders.get(attributeName);
if (completionProvider != null) {
List<String> candidates = completionProvider.getCompletionCandidates(project);
@@ -135,7 +137,7 @@ public class AnnotationAttributeCompletionProcessor implements CompletionProvide
// internal computation of the right positions, prefixes, etc.
//
private void computeProposalsForSimpleName(IJavaProject project, ASTNode node, Collection<ICompletionProposal> completions, int offset, TextDocument doc) {
private void computeProposalsForSimpleName(IJavaProject project, ASTNode node, String attributeName, Collection<ICompletionProposal> completions, int offset, TextDocument doc) {
String prefix = identifyPropertyPrefix(node.toString(), offset - node.getStartPosition());
int startOffset = node.getStartPosition();
@@ -144,30 +146,30 @@ public class AnnotationAttributeCompletionProcessor implements CompletionProvide
String proposalPrefix = "\"";
String proposalPostfix = "\"";
createCompletionProposals(project, doc, node, completions, startOffset, endOffset, prefix, (beanName) -> proposalPrefix + beanName + proposalPostfix);
createCompletionProposals(project, doc, node, attributeName, completions, startOffset, endOffset, prefix, (beanName) -> proposalPrefix + beanName + proposalPostfix);
}
private void computeProposalsForStringLiteral(IJavaProject project, ASTNode node, Collection<ICompletionProposal> completions, int offset, TextDocument doc) throws BadLocationException {
private void computeProposalsForStringLiteral(IJavaProject project, ASTNode node, String attributeName, Collection<ICompletionProposal> completions, int offset, TextDocument doc) throws BadLocationException {
int length = offset - (node.getStartPosition() + 1);
String prefix = identifyPropertyPrefix(doc.get(node.getStartPosition() + 1, length), length);
int startOffset = offset - prefix.length();
int endOffset = node.getStartPosition() + node.getLength() - 1;
createCompletionProposals(project, doc, node, completions, startOffset, endOffset, prefix, (beanName) -> beanName);
createCompletionProposals(project, doc, node, attributeName, completions, startOffset, endOffset, prefix, (beanName) -> beanName);
}
private void computeProposalsForArrayInitializr(IJavaProject project, ArrayInitializer node, Collection<ICompletionProposal> completions, int offset, TextDocument doc) {
createCompletionProposals(project, doc, node, completions, offset, offset, "", (beanName) -> "\"" + beanName + "\"");
private void computeProposalsForArrayInitializr(IJavaProject project, ArrayInitializer node, String attributeName, Collection<ICompletionProposal> completions, int offset, TextDocument doc) {
createCompletionProposals(project, doc, node, attributeName, completions, offset, offset, "", (beanName) -> "\"" + beanName + "\"");
}
private void computeProposalsForInsideArrayInitializer(IJavaProject project, ASTNode node, Collection<ICompletionProposal> completions, int offset, TextDocument doc) throws BadLocationException {
private void computeProposalsForInsideArrayInitializer(IJavaProject project, ASTNode node, String attributeName, Collection<ICompletionProposal> completions, int offset, TextDocument doc) throws BadLocationException {
int length = offset - (node.getStartPosition() + 1);
if (length >= 0) {
computeProposalsForStringLiteral(project, node, completions, offset, doc);
computeProposalsForStringLiteral(project, node, attributeName, completions, offset, doc);
}
else {
createCompletionProposals(project, doc, node, completions, offset, offset, "", (beanName) -> "\"" + beanName + "\",");
createCompletionProposals(project, doc, node, attributeName, completions, offset, offset, "", (beanName) -> "\"" + beanName + "\",");
}
}

View File

@@ -0,0 +1,42 @@
/*******************************************************************************
* Copyright (c) 2024 Broadcom
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Broadcom - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.beans;
import java.util.Arrays;
import java.util.List;
import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex;
import org.springframework.ide.vscode.boot.java.annotations.AnnotationAttributeCompletionProvider;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.protocol.spring.Bean;
/**
* @author Martin Lippert
*/
public class ResourceCompletionProvider implements AnnotationAttributeCompletionProvider {
private final SpringMetamodelIndex springIndex;
public ResourceCompletionProvider(SpringMetamodelIndex springIndex) {
this.springIndex = springIndex;
}
@Override
public List<String> getCompletionCandidates(IJavaProject project) {
Bean[] beans = this.springIndex.getBeansOfProject(project.getElementName());
return Arrays.stream(beans).map(bean -> bean.getName())
.distinct()
.toList();
}
}

View File

@@ -0,0 +1,77 @@
/*******************************************************************************
* Copyright (c) 2024 Broadcom
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Broadcom - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.beans;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
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.LocationLink;
import org.eclipse.lsp4j.jsonrpc.CancelChecker;
import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex;
import org.springframework.ide.vscode.boot.java.Annotations;
import org.springframework.ide.vscode.boot.java.IJavaDefinitionProvider;
import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.protocol.spring.Bean;
/**
* @author Martin Lippert
*/
public class ResourceDefinitionProvider implements IJavaDefinitionProvider {
private final SpringMetamodelIndex springIndex;
public ResourceDefinitionProvider(SpringMetamodelIndex springIndex) {
this.springIndex = springIndex;
}
@Override
public List<LocationLink> getDefinitions(CancelChecker cancelToken, IJavaProject project, CompilationUnit cu, ASTNode n) {
if (n instanceof StringLiteral) {
StringLiteral valueNode = (StringLiteral) n;
ASTNode parent = ASTUtils.getNearestAnnotationParent(valueNode);
if (parent != null && parent instanceof Annotation) {
Annotation a = (Annotation) parent;
IAnnotationBinding binding = a.resolveAnnotationBinding();
if (binding != null && binding.getAnnotationType() != null
&& (Annotations.RESOURCE_JAVAX.equals(binding.getAnnotationType().getQualifiedName())
|| Annotations.RESOURCE_JAKARTA.equals(binding.getAnnotationType().getQualifiedName()))) {
String beanName = valueNode.getLiteralValue();
if (beanName != null && beanName.length() > 0) {
return findBeansWithName(project, beanName);
}
}
}
}
return Collections.emptyList();
}
private List<LocationLink> findBeansWithName(IJavaProject project, String beanName) {
Bean[] beans = this.springIndex.getBeansWithName(project.getElementName(), beanName);
return Arrays.stream(beans)
.map(bean -> {
return new LocationLink(bean.getLocation().getUri(), bean.getLocation().getRange(), bean.getLocation().getRange());
})
.collect(Collectors.toList());
}
}

View File

@@ -0,0 +1,196 @@
/*******************************************************************************
* Copyright (c) 2024 Broadcom
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Broadcom - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.beans.test;
import static org.junit.Assert.assertArrayEquals;
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;
import org.eclipse.lsp4j.CompletionItem;
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.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Import;
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.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
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;
import org.springframework.ide.vscode.languageserver.testharness.Editor;
import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness;
import org.springframework.ide.vscode.project.harness.ProjectsHarness;
import org.springframework.test.context.junit.jupiter.SpringExtension;
/**
* @author Martin Lippert
*/
@ExtendWith(SpringExtension.class)
@BootLanguageServerTest
@Import(SymbolProviderTestConf.class)
public class ResourceCompletionProviderTest {
@Autowired private BootLanguageServerHarness harness;
@Autowired private JavaProjectFinder projectFinder;
@Autowired private SpringMetamodelIndex springIndex;
@Autowired private SpringSymbolIndex indexer;
private File directory;
private IJavaProject project;
private Bean[] indexedBeans;
private String tempJavaDocUri;
private Bean bean1;
private Bean bean2;
@BeforeEach
public void setup() throws Exception {
harness.intialize(null);
directory = new File(ProjectsHarness.class.getResource("/test-projects/test-spring-indexing/").toURI());
String projectDir = directory.toURI().toString();
project = projectFinder.find(new TextDocumentIdentifier(projectDir)).get();
CompletableFuture<Void> initProject = indexer.waitOperation();
initProject.get(5, TimeUnit.SECONDS);
indexedBeans = springIndex.getBeansOfProject(project.getElementName());
tempJavaDocUri = directory.toPath().resolve("src/main/java/org/test/TempClass.java").toUri().toString();
AnnotationMetadata annotationBean1 = new AnnotationMetadata("org.springframework.beans.factory.annotation.Qualifier", false, Map.of("value", new String[] {"quali1"}));
AnnotationMetadata annotationBean2 = new AnnotationMetadata("org.springframework.beans.factory.annotation.Qualifier", false, Map.of("value", new String[] {"quali2"}));
bean1 = new Bean("bean1", "type1", new Location(tempJavaDocUri, new Range(new Position(1,1), new Position(1, 20))), null, null, new AnnotationMetadata[] {annotationBean1});
bean2 = new Bean("bean2", "type2", new Location(tempJavaDocUri, new Range(new Position(1,1), new Position(1, 20))), null, null, new AnnotationMetadata[] {annotationBean2});
springIndex.updateBeans(project.getElementName(), new Bean[] {bean1, bean2});
}
@AfterEach
public void restoreIndexState() {
this.springIndex.updateBeans(project.getElementName(), indexedBeans);
}
@Test
public void testResourceNameCompletionWithoutQuotesWithAttributeName() throws Exception {
assertCompletions("@Resource(name=<*>)", new String[] {"bean1", "bean2"}, 0, "@Resource(name=\"bean1\"<*>)");
}
@Test
public void testResourceCompletionInsideOfQuotesWithoutPrefix() throws Exception {
assertCompletions("@Resource(name=\"<*>\")", new String[] {"bean1", "bean2"}, 0, "@Resource(name=\"bean1<*>\")");
}
@Test
public void testResourceCompletionInsideOfQuotesWithPrefix() throws Exception {
assertCompletions("@Resource(name=\"be<*>\")", new String[] {"bean1", "bean2"}, 0, "@Resource(name=\"bean1<*>\")");
}
@Test
public void testResourceCompletionInsideOfQuotesWithCompletePrefix() throws Exception {
assertCompletions("@Resource(name=\"bean1<*>\")", new String[] {"bean1"}, 0, "@Resource(name=\"bean1<*>\")");
}
@Test
public void testResourceCompletionInsideOfQuotesWithPrefixButWithoutMatches() throws Exception {
assertCompletions("@Resource(name=\"XXX<*>\")", 0, null);
}
@Test
public void testResourceCompletionOutsideOfAnnotation1() throws Exception {
assertCompletions("@Resource(name=\"XXX\")<*>", 0, null);
}
@Test
public void testResourceCompletionOutsideOfAnnotation2() throws Exception {
assertCompletions("@Resource<*>(name=\"XXX\")", 0, null);
}
@Test
public void testQualifierCompletionInsideOfQuotesWithPrefixAndReplacedPostfix() throws Exception {
assertCompletions("@Resource(name=\"be<*>xxx\")", 2, "@Resource(name=\"bean1<*>\")");
}
private void assertCompletions(String completionLine, int noOfExpectedCompletions, String expectedCompletedLine) throws Exception {
assertCompletions(completionLine, noOfExpectedCompletions, null, 0, expectedCompletedLine);
}
private void assertCompletions(String completionLine, String[] expectedCompletions, int chosenCompletion, String expectedCompletedLine) throws Exception {
assertCompletions(completionLine, expectedCompletions.length, expectedCompletions, chosenCompletion, expectedCompletedLine);
}
private void assertCompletions(String completionLine, int noOfExcpectedCompletions, String[] expectedCompletions, int chosenCompletion, String expectedCompletedLine) throws Exception {
String editorContent = """
package org.test;
import org.springframework.stereotype.Component;
import jakarta.annotation.Resource;
@Component
public class TestDependsOnClass {
""" +
completionLine + "\n" +
"""
public void setTestBean(Object testBean) {}
}
""";
Editor editor = harness.newEditor(LanguageId.JAVA, editorContent, tempJavaDocUri);
List<CompletionItem> completions = editor.getCompletions();
assertEquals(noOfExcpectedCompletions, completions.size());
if (expectedCompletions != null) {
String[] completionItems = completions.stream()
.map(item -> item.getLabel())
.toArray(size -> new String[size]);
assertArrayEquals(expectedCompletions, completionItems);
}
if (noOfExcpectedCompletions > 0) {
editor.apply(completions.get(chosenCompletion));
assertEquals("""
package org.test;
import org.springframework.stereotype.Component;
import jakarta.annotation.Resource;
@Component
public class TestDependsOnClass {
""" +
expectedCompletedLine + "\n" +
"""
public void setTestBean(Object testBean) {}
}
""", editor.getText());
}
}
}

View File

@@ -0,0 +1,135 @@
/*******************************************************************************
* Copyright (c) 2024 Broadcom
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Broadcom - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.beans.test;
import static org.junit.Assert.assertEquals;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.LocationLink;
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.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Import;
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.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
import org.springframework.ide.vscode.commons.protocol.spring.Bean;
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;
import org.springframework.ide.vscode.project.harness.ProjectsHarness;
import org.springframework.test.context.junit.jupiter.SpringExtension;
/**
* @author Martin Lippert
*/
@ExtendWith(SpringExtension.class)
@BootLanguageServerTest
@Import(SymbolProviderTestConf.class)
public class ResourceDefinitionProviderTest {
@Autowired private BootLanguageServerHarness harness;
@Autowired private JavaProjectFinder projectFinder;
@Autowired private SpringMetamodelIndex springIndex;
@Autowired private SpringSymbolIndex indexer;
private File directory;
private IJavaProject project;
@BeforeEach
public void setup() throws Exception {
harness.intialize(null);
directory = new File(ProjectsHarness.class.getResource("/test-projects/test-spring-indexing/").toURI());
String projectDir = directory.toURI().toString();
project = projectFinder.find(new TextDocumentIdentifier(projectDir)).get();
CompletableFuture<Void> initProject = indexer.waitOperation();
initProject.get(5, TimeUnit.SECONDS);
}
@Test
public void testSingleDependsOnBeanDefinitionLink() 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.stereotype.Component;
import jakarta.annotation.Resource;
@Component
@Resource(name="bean1")
public class TestDependsOnClass {
}""", 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,
beans[0].getLocation().getRange(), beans[0].getLocation().getRange(),
null);
editor.assertLinkTargets("bean1", List.of(expectedLocation));
}
@Test
public void testDependsOnWithMultipleBeanDefinitionLinks() 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.stereotype.Component;
import jakarta.annotation.Resource;
@Component
@Resource(name="bean1")
public class TestDependsOnClass {
}""", tempJavaDocUri);
String expectedDefinitionUri = directory.toPath().resolve("src/main/java/org/test/MainClass.java").toUri().toString();
List<Bean> beansOfDoc = new ArrayList<>(List.of(springIndex.getBeansOfDocument(expectedDefinitionUri)));
beansOfDoc.add(new Bean("bean1", "type", new Location(expectedDefinitionUri, new Range(new Position(20, 1), new Position(20, 10))), null, null, null));
springIndex.updateBeans(project.getElementName(), expectedDefinitionUri, beansOfDoc.toArray(new Bean[0]));
Bean[] beans = springIndex.getBeansWithName(project.getElementName(), "bean1");
assertEquals(2, beans.length);
LocationLink expectedLocation1 = new LocationLink(expectedDefinitionUri,
beans[0].getLocation().getRange(), beans[0].getLocation().getRange(),
null);
LocationLink expectedLocation2 = new LocationLink(expectedDefinitionUri,
beans[1].getLocation().getRange(), beans[1].getLocation().getRange(),
null);
editor.assertLinkTargets("bean1", List.of(expectedLocation1, expectedLocation2));
}
}