Skip to content

Commit 4258612

Browse files
committed
GH-1305: find all references for property keys now supported on ConditionalOnProperty annotation
1 parent 790629e commit 4258612

File tree

6 files changed

+284
-121
lines changed

6 files changed

+284
-121
lines changed

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,9 @@
4141
import org.springframework.ide.vscode.boot.java.handlers.BootJavaReferencesHandler;
4242
import org.springframework.ide.vscode.boot.java.handlers.BootJavaWorkspaceSymbolHandler;
4343
import org.springframework.ide.vscode.boot.java.handlers.CodeLensProvider;
44+
import org.springframework.ide.vscode.boot.java.handlers.CopilotCodeLensProvider;
4445
import org.springframework.ide.vscode.boot.java.handlers.HighlightProvider;
4546
import org.springframework.ide.vscode.boot.java.handlers.HoverProvider;
46-
import org.springframework.ide.vscode.boot.java.handlers.CopilotCodeLensProvider;
4747
import org.springframework.ide.vscode.boot.java.handlers.ReferenceProvider;
4848
import org.springframework.ide.vscode.boot.java.links.SourceLinks;
4949
import org.springframework.ide.vscode.boot.java.livehover.ActiveProfilesProvider;
@@ -319,6 +319,7 @@ protected ReferencesHandler createReferenceHandler(SimpleLanguageServer server,
319319
Map<String, ReferenceProvider> providers = new HashMap<>();
320320

321321
providers.put(Annotations.VALUE, new ValuePropertyReferencesProvider(projectFinder));
322+
providers.put(Annotations.CONDITIONAL_ON_PROPERTY, new ValuePropertyReferencesProvider(projectFinder));
322323
providers.put(Annotations.QUALIFIER, new QualifierReferencesProvider(index, symbolIndex));
323324
providers.put(Annotations.NAMED_JAKARTA, new NamedReferencesProvider(index, symbolIndex));
324325
providers.put(Annotations.NAMED_JAVAX, new NamedReferencesProvider(index, symbolIndex));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
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.value;
12+
13+
import java.util.List;
14+
import java.util.Map;
15+
16+
import org.eclipse.jdt.core.dom.ASTNode;
17+
import org.eclipse.jdt.core.dom.Annotation;
18+
import org.eclipse.jdt.core.dom.CompilationUnit;
19+
import org.eclipse.jdt.core.dom.Expression;
20+
import org.eclipse.jdt.core.dom.IAnnotationBinding;
21+
import org.eclipse.jdt.core.dom.MemberValuePair;
22+
import org.eclipse.jdt.core.dom.NormalAnnotation;
23+
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
24+
import org.eclipse.jdt.core.dom.StringLiteral;
25+
import org.eclipse.lsp4j.LocationLink;
26+
import org.springframework.ide.vscode.boot.java.Annotations;
27+
import org.springframework.ide.vscode.commons.java.IJavaProject;
28+
29+
public class PropertyExtractor {
30+
31+
private static final String PARAM_VALUE = "value";
32+
private static final String PARAM_NAME = "name";
33+
private static final String PARAM_PREFIX = "prefix";
34+
35+
private static interface PropertyKeyExtractor {
36+
String extract(Annotation annotation, MemberValuePair memberValuePair, StringLiteral stringLiteral);
37+
}
38+
39+
private final Map<String, PropertyKeyExtractor> propertyKeyExtractors;
40+
41+
public PropertyExtractor() {
42+
propertyKeyExtractors = Map.of(
43+
44+
Annotations.VALUE, (annotation, memberValuePair, stringLiteral) -> {
45+
if (annotation.isSingleMemberAnnotation()) {
46+
return extractPropertyKey(stringLiteral.getLiteralValue());
47+
} else if (annotation.isNormalAnnotation() && PARAM_VALUE.equals(memberValuePair.getName().getIdentifier())) {
48+
return extractPropertyKey(stringLiteral.getLiteralValue());
49+
}
50+
return null;
51+
},
52+
53+
Annotations.CONDITIONAL_ON_PROPERTY, (annotation, memberValuePair, stringLiteral) -> {
54+
if (annotation.isSingleMemberAnnotation()) {
55+
return stringLiteral.getLiteralValue();
56+
} else if (annotation.isNormalAnnotation()) {
57+
switch (memberValuePair.getName().getIdentifier()) {
58+
case PARAM_VALUE:
59+
return stringLiteral.getLiteralValue();
60+
case PARAM_NAME:
61+
String prefix = extractAnnotationParameter(annotation, PARAM_PREFIX);
62+
String name = stringLiteral.getLiteralValue();
63+
return prefix != null && !prefix.isBlank() ? prefix + "." + name : name;
64+
}
65+
}
66+
return null;
67+
}
68+
);
69+
}
70+
71+
public String extractPropertyKey(StringLiteral valueNode) {
72+
73+
ASTNode parent = valueNode.getParent();
74+
75+
if (parent instanceof Annotation) {
76+
77+
Annotation a = (Annotation) parent;
78+
IAnnotationBinding binding = a.resolveAnnotationBinding();
79+
if (binding != null && binding.getAnnotationType() != null) {
80+
PropertyKeyExtractor propertyExtractor = propertyKeyExtractors.get(binding.getAnnotationType().getQualifiedName());
81+
if (propertyExtractor != null) {
82+
return propertyExtractor.extract(a, null, valueNode);
83+
}
84+
}
85+
86+
} else if (parent instanceof MemberValuePair && parent.getParent() instanceof Annotation) {
87+
88+
MemberValuePair pair = (MemberValuePair) parent;
89+
Annotation a = (Annotation) parent.getParent();
90+
IAnnotationBinding binding = a.resolveAnnotationBinding();
91+
if (binding != null && binding.getAnnotationType() != null) {
92+
PropertyKeyExtractor propertyExtractor = propertyKeyExtractors.get(binding.getAnnotationType().getQualifiedName());
93+
if (propertyExtractor != null) {
94+
return propertyExtractor.extract(a, pair, valueNode);
95+
}
96+
}
97+
}
98+
99+
return null;
100+
}
101+
102+
private String extractPropertyKey(String s) {
103+
if (s.length() > 3 && (s.startsWith("${") || s.startsWith("#{")) && s.endsWith("}")) {
104+
return s.substring(2, s.length() - 1);
105+
}
106+
return null;
107+
}
108+
109+
@SuppressWarnings("unchecked")
110+
private static String extractAnnotationParameter(Annotation a, String param) {
111+
Expression value = null;
112+
if (a.isSingleMemberAnnotation() && PARAM_VALUE.equals(param)) {
113+
value = ((SingleMemberAnnotation) a).getValue();
114+
} else if (a.isNormalAnnotation()) {
115+
for (MemberValuePair pair : (List<MemberValuePair>) ((NormalAnnotation) a).values()) {
116+
if (param.equals(pair.getName().getIdentifier())) {
117+
value = pair.getValue();
118+
break;
119+
}
120+
}
121+
}
122+
if (value instanceof StringLiteral) {
123+
return ((StringLiteral) value).getLiteralValue();
124+
}
125+
return null;
126+
}
127+
128+
}

headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValueDefinitionProvider.java

Lines changed: 5 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,7 @@
1818
import java.util.Optional;
1919

2020
import org.eclipse.jdt.core.dom.ASTNode;
21-
import org.eclipse.jdt.core.dom.Annotation;
2221
import org.eclipse.jdt.core.dom.CompilationUnit;
23-
import org.eclipse.jdt.core.dom.Expression;
24-
import org.eclipse.jdt.core.dom.IAnnotationBinding;
25-
import org.eclipse.jdt.core.dom.MemberValuePair;
26-
import org.eclipse.jdt.core.dom.NormalAnnotation;
27-
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
2822
import org.eclipse.jdt.core.dom.StringLiteral;
2923
import org.eclipse.lsp4j.Location;
3024
import org.eclipse.lsp4j.LocationLink;
@@ -34,7 +28,6 @@
3428
import org.eclipse.lsp4j.jsonrpc.CancelChecker;
3529
import org.slf4j.Logger;
3630
import org.slf4j.LoggerFactory;
37-
import org.springframework.ide.vscode.boot.java.Annotations;
3831
import org.springframework.ide.vscode.boot.java.IJavaDefinitionProvider;
3932
import org.springframework.ide.vscode.boot.properties.BootPropertiesLanguageServerComponents;
4033
import org.springframework.ide.vscode.commons.java.IClasspathUtil;
@@ -47,37 +40,12 @@
4740
public class ValueDefinitionProvider implements IJavaDefinitionProvider {
4841

4942
private static final Logger log = LoggerFactory.getLogger(ValueDefinitionProvider.class);
43+
private final PropertyExtractor propertyExtractor;
5044

51-
private static final String PARAM_VALUE = "value";
52-
private static final String PARAM_NAME = "name";
53-
private static final String PARAM_PREFIX = "prefix";
45+
public ValueDefinitionProvider() {
46+
this.propertyExtractor = new PropertyExtractor();
47+
}
5448

55-
private Map<String, PropertyKeyExtractor> annotationToPropertyKeyExtractor = Map.of(
56-
Annotations.VALUE, (annotation, memberValuePair, stringLiteral) -> {
57-
if (annotation.isSingleMemberAnnotation()) {
58-
return extractPropertyKey(stringLiteral.getLiteralValue());
59-
} else if (annotation.isNormalAnnotation() && PARAM_VALUE.equals(memberValuePair.getName().getIdentifier())) {
60-
return extractPropertyKey(stringLiteral.getLiteralValue());
61-
}
62-
return null;
63-
},
64-
Annotations.CONDITIONAL_ON_PROPERTY, (annotation, memberValuePair, stringLiteral) -> {
65-
if (annotation.isSingleMemberAnnotation()) {
66-
return stringLiteral.getLiteralValue();
67-
} else if (annotation.isNormalAnnotation()) {
68-
switch (memberValuePair.getName().getIdentifier()) {
69-
case PARAM_VALUE:
70-
return stringLiteral.getLiteralValue();
71-
case PARAM_NAME:
72-
String prefix = extractAnnotationParameter(annotation, PARAM_PREFIX);
73-
String name = stringLiteral.getLiteralValue();
74-
return prefix != null && !prefix.isBlank() ? prefix + "." + name : name;
75-
}
76-
}
77-
return null;
78-
}
79-
);
80-
8149
@Override
8250
public List<LocationLink> getDefinitions(CancelChecker cancelToken, IJavaProject project,
8351
TextDocumentIdentifier docId, CompilationUnit cu, ASTNode n, int offset) {
@@ -99,30 +67,7 @@ public List<LocationLink> getDefinitions(CancelChecker cancelToken, IJavaProject
9967
}
10068

10169
private List<LocationLink> getDefinitionForProperty(IJavaProject project, CompilationUnit cu, StringLiteral valueNode) {
102-
String propertyKey = null;
103-
104-
ASTNode parent = valueNode.getParent();
105-
if (parent instanceof Annotation) {
106-
Annotation a = (Annotation) parent;
107-
IAnnotationBinding binding = a.resolveAnnotationBinding();
108-
if (binding != null && binding.getAnnotationType() != null) {
109-
PropertyKeyExtractor propertyExtractor = annotationToPropertyKeyExtractor.get(binding.getAnnotationType().getQualifiedName());
110-
if (propertyExtractor != null) {
111-
propertyKey = propertyExtractor.extract(a, null, valueNode);
112-
}
113-
}
114-
} else if (parent instanceof MemberValuePair
115-
&& parent.getParent() instanceof Annotation) {
116-
MemberValuePair pair = (MemberValuePair) parent;
117-
Annotation a = (Annotation) parent.getParent();
118-
IAnnotationBinding binding = a.resolveAnnotationBinding();
119-
if (binding != null && binding.getAnnotationType() != null) {
120-
PropertyKeyExtractor propertyExtractor = annotationToPropertyKeyExtractor.get(binding.getAnnotationType().getQualifiedName());
121-
if (propertyExtractor != null) {
122-
propertyKey = propertyExtractor.extract(a, pair, valueNode);
123-
}
124-
}
125-
}
70+
String propertyKey = propertyExtractor.extractPropertyKey(valueNode);
12671

12772
if (propertyKey != null) {
12873
Builder<LocationLink> builder = ImmutableList.builder();
@@ -217,36 +162,6 @@ private List<Location> findValueReferences(IJavaProject project, String property
217162
return links.build();
218163
}
219164

220-
@SuppressWarnings("unchecked")
221-
private static String extractAnnotationParameter(Annotation a, String param) {
222-
Expression value = null;
223-
if (a.isSingleMemberAnnotation() && PARAM_VALUE.equals(param)) {
224-
value = ((SingleMemberAnnotation) a).getValue();
225-
} else if (a.isNormalAnnotation()) {
226-
for (MemberValuePair pair : (List<MemberValuePair>) ((NormalAnnotation) a).values()) {
227-
if (param.equals(pair.getName().getIdentifier())) {
228-
value = pair.getValue();
229-
break;
230-
}
231-
}
232-
}
233-
if (value instanceof StringLiteral) {
234-
return ((StringLiteral) value).getLiteralValue();
235-
}
236-
return null;
237-
}
238-
239-
private static String extractPropertyKey(String s) {
240-
if (s.length() > 3 && (s.startsWith("${") || s.startsWith("#{")) && s.endsWith("}")) {
241-
return s.substring(2, s.length() - 1);
242-
}
243-
return null;
244-
}
245-
246-
private interface PropertyKeyExtractor {
247-
String extract(Annotation annotation, MemberValuePair memberValuePair, StringLiteral stringLiteral);
248-
}
249-
250165
private List<LocationLink> getDefinitionForClasspathResource(IJavaProject project, CompilationUnit cu, StringLiteral valueNode, String literalValue) {
251166
literalValue = literalValue.substring("classpath:".length());
252167

headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValuePropertyReferencesProvider.java

Lines changed: 5 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
import org.eclipse.jdt.core.dom.ASTNode;
3232
import org.eclipse.jdt.core.dom.Annotation;
3333
import org.eclipse.jdt.core.dom.ITypeBinding;
34-
import org.eclipse.jdt.core.dom.MemberValuePair;
3534
import org.eclipse.jdt.core.dom.StringLiteral;
3635
import org.eclipse.lsp4j.Location;
3736
import org.eclipse.lsp4j.Position;
@@ -65,9 +64,11 @@ public class ValuePropertyReferencesProvider implements ReferenceProvider {
6564
private static final Logger log = LoggerFactory.getLogger(ValuePropertyReferencesProvider.class);
6665

6766
private final JavaProjectFinder projectFinder;
67+
private final PropertyExtractor propertyExtractor;
6868

6969
public ValuePropertyReferencesProvider(JavaProjectFinder projectFinder) {
7070
this.projectFinder = projectFinder;
71+
this.propertyExtractor = new PropertyExtractor();
7172
}
7273

7374
@Override
@@ -76,34 +77,9 @@ public List<? extends Location> provideReferences(CancelChecker cancelToken, IJa
7677
cancelToken.checkCanceled();
7778

7879
try {
79-
// case: @Value("prefix<*>")
80-
if (node instanceof StringLiteral && node.getParent() instanceof Annotation) {
81-
if (node.toString().startsWith("\"") && node.toString().endsWith("\"")) {
82-
return provideReferences(node.toString(), offset - node.getStartPosition(), node.getStartPosition());
83-
}
84-
}
85-
// case: @Value(value="prefix<*>")
86-
else if (node instanceof StringLiteral && node.getParent() instanceof MemberValuePair
87-
&& "value".equals(((MemberValuePair)node.getParent()).getName().toString())) {
88-
if (node.toString().startsWith("\"") && node.toString().endsWith("\"")) {
89-
return provideReferences(node.toString(), offset - node.getStartPosition(), node.getStartPosition());
90-
}
91-
}
92-
}
93-
catch (Exception e) {
94-
e.printStackTrace();
95-
}
96-
97-
return null;
98-
}
99-
100-
private List<? extends Location> provideReferences(String value, int offset, int nodeStartOffset) {
101-
102-
try {
103-
LocalRange range = getPropertyRange(value, offset);
104-
if (range != null) {
105-
String propertyKey = value.substring(range.getStart(), range.getEnd());
106-
if (propertyKey != null && propertyKey.length() > 0) {
80+
if (node instanceof StringLiteral) {
81+
String propertyKey = this.propertyExtractor.extractPropertyKey((StringLiteral) node);
82+
if (propertyKey != null) {
10783
return findReferencesFromPropertyFiles(propertyKey);
10884
}
10985
}

0 commit comments

Comments
 (0)