GH-1294: add find references support for profile annotation values
This commit is contained in:
@@ -27,6 +27,7 @@ import org.springframework.ide.vscode.boot.app.SpringSymbolIndex;
|
||||
import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex;
|
||||
import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchyAwareLookup;
|
||||
import org.springframework.ide.vscode.boot.java.autowired.AutowiredHoverProvider;
|
||||
import org.springframework.ide.vscode.boot.java.beans.ProfileReferencesProvider;
|
||||
import org.springframework.ide.vscode.boot.java.beans.QualifierReferencesProvider;
|
||||
import org.springframework.ide.vscode.boot.java.conditionals.ConditionalsLiveHoverProvider;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.BootJavaCodeActionProvider;
|
||||
@@ -296,6 +297,7 @@ public class BootJavaLanguageServerComponents implements LanguageServerComponent
|
||||
Map<String, ReferenceProvider> providers = new HashMap<>();
|
||||
providers.put(Annotations.VALUE, new ValuePropertyReferencesProvider(server));
|
||||
providers.put(Annotations.QUALIFIER, new QualifierReferencesProvider(index, symbolIndex));
|
||||
providers.put(Annotations.PROFILE, new ProfileReferencesProvider(index, symbolIndex));
|
||||
|
||||
return new BootJavaReferencesHandler(this, cuCache, projectFinder, providers);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
/*******************************************************************************
|
||||
* 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 java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTNode;
|
||||
import org.eclipse.jdt.core.dom.Annotation;
|
||||
import org.eclipse.jdt.core.dom.ArrayInitializer;
|
||||
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.Location;
|
||||
import org.eclipse.lsp4j.WorkspaceSymbol;
|
||||
import org.eclipse.lsp4j.jsonrpc.CancelChecker;
|
||||
import org.springframework.ide.vscode.boot.app.SpringSymbolIndex;
|
||||
import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.ReferenceProvider;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.Bean;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class ProfileReferencesProvider implements ReferenceProvider {
|
||||
|
||||
private final SpringMetamodelIndex springIndex;
|
||||
private final SpringSymbolIndex symbolIndex;
|
||||
|
||||
public ProfileReferencesProvider(SpringMetamodelIndex springIndex, SpringSymbolIndex symbolIndex) {
|
||||
this.springIndex = springIndex;
|
||||
this.symbolIndex = symbolIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<? extends Location> provideReferences(CancelChecker cancelToken, IJavaProject project, ASTNode node, Annotation annotation, ITypeBinding type, int offset) {
|
||||
|
||||
cancelToken.checkCanceled();
|
||||
|
||||
try {
|
||||
// case: @Value("prefix<*>")
|
||||
if (node instanceof StringLiteral && node.getParent() instanceof Annotation) {
|
||||
if (node.toString().startsWith("\"") && node.toString().endsWith("\"")) {
|
||||
return provideReferences(project, ((StringLiteral) node).getLiteralValue());
|
||||
}
|
||||
}
|
||||
// case: @Value(value="prefix<*>")
|
||||
else if (node instanceof StringLiteral && node.getParent() instanceof MemberValuePair
|
||||
&& "value".equals(((MemberValuePair)node.getParent()).getName().toString())) {
|
||||
if (node.toString().startsWith("\"") && node.toString().endsWith("\"")) {
|
||||
return provideReferences(project, ((StringLiteral) node).getLiteralValue());
|
||||
}
|
||||
}
|
||||
// case: @Qualifier({"prefix<*>"})
|
||||
else if (node instanceof StringLiteral && node.getParent() instanceof ArrayInitializer) {
|
||||
if (node.toString().startsWith("\"") && node.toString().endsWith("\"")) {
|
||||
return provideReferences(project, ((StringLiteral) node).getLiteralValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
String profileSymbolsSearch = "@Profile(";
|
||||
List<WorkspaceSymbol> profileSymbols = symbolIndex.getAllSymbols(profileSymbolsSearch);
|
||||
|
||||
String valuePhrase = "\"" + value + "\"";
|
||||
|
||||
Stream<Location> qualifierLocations = profileSymbols.stream()
|
||||
.filter(symbol -> symbol.getName().contains(valuePhrase))
|
||||
.map(symbol -> symbol.getLocation())
|
||||
.filter(location -> location.isLeft())
|
||||
.map(location -> location.getLeft());
|
||||
|
||||
return Stream.concat(qualifierLocations, beanLocations).toList();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/*******************************************************************************
|
||||
* 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 static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
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.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.commons.languageserver.java.JavaProjectFinder;
|
||||
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 ProfileReferencesProviderTest {
|
||||
|
||||
@Autowired private BootLanguageServerHarness harness;
|
||||
@Autowired private JavaProjectFinder projectFinder;
|
||||
@Autowired private SpringSymbolIndex indexer;
|
||||
|
||||
private File directory;
|
||||
|
||||
@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();
|
||||
projectFinder.find(new TextDocumentIdentifier(projectDir)).get();
|
||||
|
||||
CompletableFuture<Void> initProject = indexer.waitOperation();
|
||||
initProject.get(5, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProfileRefersToOtherProfiles() 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 org.springframework.context.annotation.Profile;
|
||||
|
||||
@Component
|
||||
@Profile("pro<*>file1")
|
||||
public class TestDependsOnClass {
|
||||
}""", tempJavaDocUri);
|
||||
|
||||
List<? extends Location> references = editor.getReferences();
|
||||
assertEquals(2, references.size());
|
||||
|
||||
String expectedDefinitionUri1 = directory.toPath().resolve("src/main/java/org/test/profiles/ProfilesClass1.java").toUri().toString();
|
||||
Location expectedLocation1 = new Location(expectedDefinitionUri1,
|
||||
new Range(new Position(6, 0), new Position(6, 20)));
|
||||
|
||||
assertTrue(references.contains(expectedLocation1));
|
||||
|
||||
String expectedDefinitionUri2 = directory.toPath().resolve("src/main/java/org/test/profiles/ProfilesClassWithArray.java").toUri().toString();
|
||||
Location expectedLocation2 = new Location(expectedDefinitionUri2,
|
||||
new Range(new Position(6, 0), new Position(6, 42)));
|
||||
|
||||
assertTrue(references.contains(expectedLocation2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProfileWithinArrayRefersToOtherProfiles() 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 org.springframework.context.annotation.Profile;
|
||||
|
||||
@Component
|
||||
@Profile(value = {"profile2", "pro<*>file1"})
|
||||
public class TestDependsOnClass {
|
||||
}""", tempJavaDocUri);
|
||||
|
||||
List<? extends Location> references = editor.getReferences();
|
||||
assertEquals(2, references.size());
|
||||
|
||||
String expectedDefinitionUri1 = directory.toPath().resolve("src/main/java/org/test/profiles/ProfilesClass1.java").toUri().toString();
|
||||
Location expectedLocation1 = new Location(expectedDefinitionUri1,
|
||||
new Range(new Position(6, 0), new Position(6, 20)));
|
||||
|
||||
assertTrue(references.contains(expectedLocation1));
|
||||
|
||||
String expectedDefinitionUri2 = directory.toPath().resolve("src/main/java/org/test/profiles/ProfilesClassWithArray.java").toUri().toString();
|
||||
Location expectedLocation2 = new Location(expectedDefinitionUri2,
|
||||
new Range(new Position(6, 0), new Position(6, 42)));
|
||||
|
||||
assertTrue(references.contains(expectedLocation2));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package org.test.profiles;
|
||||
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@Profile("profile1")
|
||||
public class ProfilesClass1 {
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package org.test.profiles;
|
||||
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@Profile("profile2")
|
||||
public class ProfilesClass2 {
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package org.test.profiles;
|
||||
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@Profile(value = {"profile1", "profile2"})
|
||||
public class ProfilesClassWithArray {
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user