Skip to content

Commit 1304d81

Browse files
committed
GH-1294: add find references support for profile annotation values
1 parent 8acbb3d commit 1304d81

File tree

6 files changed

+259
-0
lines changed

6 files changed

+259
-0
lines changed

Diff for: headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServerComponents.java

+2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex;
2828
import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchyAwareLookup;
2929
import org.springframework.ide.vscode.boot.java.autowired.AutowiredHoverProvider;
30+
import org.springframework.ide.vscode.boot.java.beans.ProfileReferencesProvider;
3031
import org.springframework.ide.vscode.boot.java.beans.QualifierReferencesProvider;
3132
import org.springframework.ide.vscode.boot.java.conditionals.ConditionalsLiveHoverProvider;
3233
import org.springframework.ide.vscode.boot.java.handlers.BootJavaCodeActionProvider;
@@ -296,6 +297,7 @@ protected ReferencesHandler createReferenceHandler(SimpleLanguageServer server,
296297
Map<String, ReferenceProvider> providers = new HashMap<>();
297298
providers.put(Annotations.VALUE, new ValuePropertyReferencesProvider(server));
298299
providers.put(Annotations.QUALIFIER, new QualifierReferencesProvider(index, symbolIndex));
300+
providers.put(Annotations.PROFILE, new ProfileReferencesProvider(index, symbolIndex));
299301

300302
return new BootJavaReferencesHandler(this, cuCache, projectFinder, providers);
301303
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2024 Broadcom
3+
* All rights reserved. This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License v1.0
5+
* which accompanies this distribution, and is available at
6+
* https://www.eclipse.org/legal/epl-v10.html
7+
*
8+
* Contributors:
9+
* Broadcom - initial API and implementation
10+
*******************************************************************************/
11+
package org.springframework.ide.vscode.boot.java.beans;
12+
13+
import java.util.Arrays;
14+
import java.util.List;
15+
import java.util.stream.Stream;
16+
17+
import org.eclipse.jdt.core.dom.ASTNode;
18+
import org.eclipse.jdt.core.dom.Annotation;
19+
import org.eclipse.jdt.core.dom.ArrayInitializer;
20+
import org.eclipse.jdt.core.dom.ITypeBinding;
21+
import org.eclipse.jdt.core.dom.MemberValuePair;
22+
import org.eclipse.jdt.core.dom.StringLiteral;
23+
import org.eclipse.lsp4j.Location;
24+
import org.eclipse.lsp4j.WorkspaceSymbol;
25+
import org.eclipse.lsp4j.jsonrpc.CancelChecker;
26+
import org.springframework.ide.vscode.boot.app.SpringSymbolIndex;
27+
import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex;
28+
import org.springframework.ide.vscode.boot.java.handlers.ReferenceProvider;
29+
import org.springframework.ide.vscode.commons.java.IJavaProject;
30+
import org.springframework.ide.vscode.commons.protocol.spring.Bean;
31+
32+
/**
33+
* @author Martin Lippert
34+
*/
35+
public class ProfileReferencesProvider implements ReferenceProvider {
36+
37+
private final SpringMetamodelIndex springIndex;
38+
private final SpringSymbolIndex symbolIndex;
39+
40+
public ProfileReferencesProvider(SpringMetamodelIndex springIndex, SpringSymbolIndex symbolIndex) {
41+
this.springIndex = springIndex;
42+
this.symbolIndex = symbolIndex;
43+
}
44+
45+
@Override
46+
public List<? extends Location> provideReferences(CancelChecker cancelToken, IJavaProject project, ASTNode node, Annotation annotation, ITypeBinding type, int offset) {
47+
48+
cancelToken.checkCanceled();
49+
50+
try {
51+
// case: @Value("prefix<*>")
52+
if (node instanceof StringLiteral && node.getParent() instanceof Annotation) {
53+
if (node.toString().startsWith("\"") && node.toString().endsWith("\"")) {
54+
return provideReferences(project, ((StringLiteral) node).getLiteralValue());
55+
}
56+
}
57+
// case: @Value(value="prefix<*>")
58+
else if (node instanceof StringLiteral && node.getParent() instanceof MemberValuePair
59+
&& "value".equals(((MemberValuePair)node.getParent()).getName().toString())) {
60+
if (node.toString().startsWith("\"") && node.toString().endsWith("\"")) {
61+
return provideReferences(project, ((StringLiteral) node).getLiteralValue());
62+
}
63+
}
64+
// case: @Qualifier({"prefix<*>"})
65+
else if (node instanceof StringLiteral && node.getParent() instanceof ArrayInitializer) {
66+
if (node.toString().startsWith("\"") && node.toString().endsWith("\"")) {
67+
return provideReferences(project, ((StringLiteral) node).getLiteralValue());
68+
}
69+
}
70+
}
71+
catch (Exception e) {
72+
e.printStackTrace();
73+
}
74+
75+
return null;
76+
}
77+
78+
private List<? extends Location> provideReferences(IJavaProject project, String value) {
79+
Bean[] beans = this.springIndex.getBeansOfProject(project.getElementName());
80+
81+
// beans with name
82+
Stream<Location> beanLocations = Arrays.stream(beans)
83+
.filter(bean -> bean.getName().equals(value))
84+
.map(bean -> bean.getLocation());
85+
86+
String profileSymbolsSearch = "@Profile(";
87+
List<WorkspaceSymbol> profileSymbols = symbolIndex.getAllSymbols(profileSymbolsSearch);
88+
89+
String valuePhrase = "\"" + value + "\"";
90+
91+
Stream<Location> qualifierLocations = profileSymbols.stream()
92+
.filter(symbol -> symbol.getName().contains(valuePhrase))
93+
.map(symbol -> symbol.getLocation())
94+
.filter(location -> location.isLeft())
95+
.map(location -> location.getLeft());
96+
97+
return Stream.concat(qualifierLocations, beanLocations).toList();
98+
}
99+
100+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2024 Broadcom
3+
* All rights reserved. This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License v1.0
5+
* which accompanies this distribution, and is available at
6+
* https://www.eclipse.org/legal/epl-v10.html
7+
*
8+
* Contributors:
9+
* Broadcom - initial API and implementation
10+
*******************************************************************************/
11+
package org.springframework.ide.vscode.boot.java.beans.test;
12+
13+
import static org.junit.Assert.assertEquals;
14+
import static org.junit.Assert.assertTrue;
15+
16+
import java.io.File;
17+
import java.util.List;
18+
import java.util.concurrent.CompletableFuture;
19+
import java.util.concurrent.TimeUnit;
20+
21+
import org.eclipse.lsp4j.Location;
22+
import org.eclipse.lsp4j.Position;
23+
import org.eclipse.lsp4j.Range;
24+
import org.eclipse.lsp4j.TextDocumentIdentifier;
25+
import org.junit.jupiter.api.BeforeEach;
26+
import org.junit.jupiter.api.Test;
27+
import org.junit.jupiter.api.extension.ExtendWith;
28+
import org.springframework.beans.factory.annotation.Autowired;
29+
import org.springframework.context.annotation.Import;
30+
import org.springframework.ide.vscode.boot.app.SpringSymbolIndex;
31+
import org.springframework.ide.vscode.boot.bootiful.BootLanguageServerTest;
32+
import org.springframework.ide.vscode.boot.bootiful.SymbolProviderTestConf;
33+
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
34+
import org.springframework.ide.vscode.commons.util.text.LanguageId;
35+
import org.springframework.ide.vscode.languageserver.testharness.Editor;
36+
import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness;
37+
import org.springframework.ide.vscode.project.harness.ProjectsHarness;
38+
import org.springframework.test.context.junit.jupiter.SpringExtension;
39+
40+
/**
41+
* @author Martin Lippert
42+
*/
43+
@ExtendWith(SpringExtension.class)
44+
@BootLanguageServerTest
45+
@Import(SymbolProviderTestConf.class)
46+
public class ProfileReferencesProviderTest {
47+
48+
@Autowired private BootLanguageServerHarness harness;
49+
@Autowired private JavaProjectFinder projectFinder;
50+
@Autowired private SpringSymbolIndex indexer;
51+
52+
private File directory;
53+
54+
@BeforeEach
55+
public void setup() throws Exception {
56+
harness.intialize(null);
57+
58+
directory = new File(ProjectsHarness.class.getResource("/test-projects/test-spring-indexing/").toURI());
59+
60+
String projectDir = directory.toURI().toString();
61+
projectFinder.find(new TextDocumentIdentifier(projectDir)).get();
62+
63+
CompletableFuture<Void> initProject = indexer.waitOperation();
64+
initProject.get(5, TimeUnit.SECONDS);
65+
}
66+
67+
@Test
68+
public void testProfileRefersToOtherProfiles() throws Exception {
69+
String tempJavaDocUri = directory.toPath().resolve("src/main/java/org/test/TempClass.java").toUri().toString();
70+
71+
Editor editor = harness.newEditor(LanguageId.JAVA, """
72+
package org.test;
73+
74+
import org.springframework.stereotype.Component;
75+
import org.springframework.context.annotation.Profile;
76+
77+
@Component
78+
@Profile("pro<*>file1")
79+
public class TestDependsOnClass {
80+
}""", tempJavaDocUri);
81+
82+
List<? extends Location> references = editor.getReferences();
83+
assertEquals(2, references.size());
84+
85+
String expectedDefinitionUri1 = directory.toPath().resolve("src/main/java/org/test/profiles/ProfilesClass1.java").toUri().toString();
86+
Location expectedLocation1 = new Location(expectedDefinitionUri1,
87+
new Range(new Position(6, 0), new Position(6, 20)));
88+
89+
assertTrue(references.contains(expectedLocation1));
90+
91+
String expectedDefinitionUri2 = directory.toPath().resolve("src/main/java/org/test/profiles/ProfilesClassWithArray.java").toUri().toString();
92+
Location expectedLocation2 = new Location(expectedDefinitionUri2,
93+
new Range(new Position(6, 0), new Position(6, 42)));
94+
95+
assertTrue(references.contains(expectedLocation2));
96+
}
97+
98+
@Test
99+
public void testProfileWithinArrayRefersToOtherProfiles() throws Exception {
100+
String tempJavaDocUri = directory.toPath().resolve("src/main/java/org/test/TempClass.java").toUri().toString();
101+
102+
Editor editor = harness.newEditor(LanguageId.JAVA, """
103+
package org.test;
104+
105+
import org.springframework.stereotype.Component;
106+
import org.springframework.context.annotation.Profile;
107+
108+
@Component
109+
@Profile(value = {"profile2", "pro<*>file1"})
110+
public class TestDependsOnClass {
111+
}""", tempJavaDocUri);
112+
113+
List<? extends Location> references = editor.getReferences();
114+
assertEquals(2, references.size());
115+
116+
String expectedDefinitionUri1 = directory.toPath().resolve("src/main/java/org/test/profiles/ProfilesClass1.java").toUri().toString();
117+
Location expectedLocation1 = new Location(expectedDefinitionUri1,
118+
new Range(new Position(6, 0), new Position(6, 20)));
119+
120+
assertTrue(references.contains(expectedLocation1));
121+
122+
String expectedDefinitionUri2 = directory.toPath().resolve("src/main/java/org/test/profiles/ProfilesClassWithArray.java").toUri().toString();
123+
Location expectedLocation2 = new Location(expectedDefinitionUri2,
124+
new Range(new Position(6, 0), new Position(6, 42)));
125+
126+
assertTrue(references.contains(expectedLocation2));
127+
}
128+
129+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package org.test.profiles;
2+
3+
import org.springframework.context.annotation.Profile;
4+
import org.springframework.stereotype.Component;
5+
6+
@Component
7+
@Profile("profile1")
8+
public class ProfilesClass1 {
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package org.test.profiles;
2+
3+
import org.springframework.context.annotation.Profile;
4+
import org.springframework.stereotype.Component;
5+
6+
@Component
7+
@Profile("profile2")
8+
public class ProfilesClass2 {
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package org.test.profiles;
2+
3+
import org.springframework.context.annotation.Profile;
4+
import org.springframework.stereotype.Component;
5+
6+
@Component
7+
@Profile(value = {"profile1", "profile2"})
8+
public class ProfilesClassWithArray {
9+
10+
}

0 commit comments

Comments
 (0)