Skip to content

Commit 85103b6

Browse files
committed
Navigate to property value from @ConditionalOnProperty
1 parent 7a689ba commit 85103b6

File tree

2 files changed

+141
-16
lines changed

2 files changed

+141
-16
lines changed

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

Lines changed: 75 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,11 @@
1919
import org.eclipse.jdt.core.dom.ASTNode;
2020
import org.eclipse.jdt.core.dom.Annotation;
2121
import org.eclipse.jdt.core.dom.CompilationUnit;
22-
import org.eclipse.jdt.core.dom.FieldDeclaration;
22+
import org.eclipse.jdt.core.dom.Expression;
2323
import org.eclipse.jdt.core.dom.IAnnotationBinding;
2424
import org.eclipse.jdt.core.dom.MemberValuePair;
25+
import org.eclipse.jdt.core.dom.NormalAnnotation;
26+
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
2527
import org.eclipse.jdt.core.dom.StringLiteral;
2628
import org.eclipse.lsp4j.Location;
2729
import org.eclipse.lsp4j.LocationLink;
@@ -43,6 +45,36 @@
4345
public class PropertyValueAnnotationDefProvider implements IJavaDefinitionProvider {
4446

4547
private static final Logger log = LoggerFactory.getLogger(PropertyValueAnnotationDefProvider.class);
48+
49+
private static final String PARAM_VALUE = "value";
50+
private static final String PARAM_NAME = "name";
51+
private static final String PARAM_PREFIX = "prefix";
52+
53+
private Map<String, PropertyKeyExtractor> annotationToPropertyKeyExtractor = Map.of(
54+
Annotations.VALUE, (a, p, v) -> {
55+
if (a.isSingleMemberAnnotation()) {
56+
return extractPropertyKey(v.getLiteralValue());
57+
} else if (a.isNormalAnnotation() && PARAM_VALUE.equals(p.getName().getIdentifier())) {
58+
return extractPropertyKey(v.getLiteralValue());
59+
}
60+
return null;
61+
},
62+
Annotations.CONDITIONAL_ON_PROPERTY, (a, p, v) -> {
63+
if (a.isSingleMemberAnnotation()) {
64+
return v.getLiteralValue();
65+
} else if (a.isNormalAnnotation()) {
66+
switch (p.getName().getIdentifier()) {
67+
case PARAM_VALUE:
68+
return v.getLiteralValue();
69+
case PARAM_NAME:
70+
String prefix = extractAnnotationParameter(a, PARAM_PREFIX);
71+
String name = v.getLiteralValue();
72+
return prefix != null && !prefix.isBlank() ? prefix + "." + name : name;
73+
}
74+
}
75+
return null;
76+
}
77+
);
4678

4779
@Override
4880
public List<LocationLink> getDefinitions(CancelChecker cancelToken, IJavaProject project, CompilationUnit cu,
@@ -52,13 +84,26 @@ public List<LocationLink> getDefinitions(CancelChecker cancelToken, IJavaProject
5284
String propertyKey = null;
5385

5486
ASTNode parent = valueNode.getParent();
55-
if (parent instanceof Annotation && isApplicableValueAnnotation((Annotation) parent)) {
56-
propertyKey = extractPropertyKey(valueNode.getLiteralValue());
87+
if (parent instanceof Annotation) {
88+
Annotation a = (Annotation) parent;
89+
IAnnotationBinding binding = a.resolveAnnotationBinding();
90+
if (binding != null && binding.getAnnotationType() != null) {
91+
PropertyKeyExtractor propertyExtractor = annotationToPropertyKeyExtractor.get(binding.getAnnotationType().getQualifiedName());
92+
if (propertyExtractor != null) {
93+
propertyKey = propertyExtractor.extract(a, null, valueNode);
94+
}
95+
}
5796
} else if (parent instanceof MemberValuePair
58-
&& "value".equals(((MemberValuePair) parent).getName().getIdentifier())
59-
&& parent.getParent() instanceof Annotation
60-
&& isApplicableValueAnnotation((Annotation) parent.getParent())) {
61-
propertyKey = extractPropertyKey(valueNode.getLiteralValue());
97+
&& parent.getParent() instanceof Annotation) {
98+
MemberValuePair pair = (MemberValuePair) parent;
99+
Annotation a = (Annotation) parent.getParent();
100+
IAnnotationBinding binding = a.resolveAnnotationBinding();
101+
if (binding != null && binding.getAnnotationType() != null) {
102+
PropertyKeyExtractor propertyExtractor = annotationToPropertyKeyExtractor.get(binding.getAnnotationType().getQualifiedName());
103+
if (propertyExtractor != null) {
104+
propertyKey = propertyExtractor.extract(a, pair, valueNode);
105+
}
106+
}
62107
}
63108

64109
if (propertyKey != null) {
@@ -87,12 +132,6 @@ && isApplicableValueAnnotation((Annotation) parent.getParent())) {
87132
return Collections.emptyList();
88133
}
89134

90-
private static boolean isApplicableValueAnnotation(Annotation a) {
91-
IAnnotationBinding binding = a.resolveAnnotationBinding();
92-
return binding != null && Annotations.VALUE.equals(binding.getAnnotationType().getQualifiedName())
93-
&& a.getParent() instanceof FieldDeclaration;
94-
}
95-
96135
private List<Location> findValueReferences(IJavaProject project, String propertyKey, Map<Location, Range> targetRanges) {
97136
Builder<Location> links = ImmutableList.builder();
98137
IClasspathUtil.getClasspathResourcesFullPaths(project.getClasspath()).forEach(path -> {
@@ -159,12 +198,35 @@ private List<Location> findValueReferences(IJavaProject project, String property
159198
});
160199
return links.build();
161200
}
201+
202+
@SuppressWarnings("unchecked")
203+
private static String extractAnnotationParameter(Annotation a, String param) {
204+
Expression value = null;
205+
if (a.isSingleMemberAnnotation() && PARAM_VALUE.equals(param)) {
206+
value = ((SingleMemberAnnotation) a).getValue();
207+
} else if (a.isNormalAnnotation()) {
208+
for (MemberValuePair pair : (List<MemberValuePair>) ((NormalAnnotation) a).values()) {
209+
if (param.equals(pair.getName().getIdentifier())) {
210+
value = pair.getValue();
211+
break;
212+
}
213+
}
214+
}
215+
if (value instanceof StringLiteral) {
216+
return ((StringLiteral) value).getLiteralValue();
217+
}
218+
return null;
219+
}
162220

163221
private static String extractPropertyKey(String s) {
164222
if (s.length() > 3 && (s.startsWith("${") || s.startsWith("#{")) && s.endsWith("}")) {
165223
return s.substring(2, s.length() - 1);
166224
}
167225
return null;
168226
}
227+
228+
private interface PropertyKeyExtractor {
229+
String extract(Annotation a, MemberValuePair pair, StringLiteral v);
230+
}
169231

170232
}

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

Lines changed: 66 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ private Path projectFile(String relativePath, String content) throws IOException
7272
}
7373

7474
@Test
75-
void propertiesCase() throws Exception {
75+
void propertiesCase_ValueAnnotation() throws Exception {
7676
Path propertiesFilePath = projectFile("src/main/resources/application.properties", "some.prop=5");
7777
Editor editor = harness.newEditor(LanguageId.JAVA, """
7878
package org.test;
@@ -93,7 +93,7 @@ public class TestValueCompletion {
9393
}
9494

9595
@Test
96-
void yamlCase() throws Exception {
96+
void yamlCase_ValueAnnotation() throws Exception {
9797
Path yamlFilePath = projectFile("src/main/resources/application.yml", """
9898
some:
9999
prop: 5
@@ -117,7 +117,7 @@ public class TestValueCompletion {
117117
}
118118

119119
@Test
120-
void combinedCase() throws Exception {
120+
void combinedCase_ValueAnnotation() throws Exception {
121121
Path propertiesFilePath = projectFile("src/main/resources/application.properties", "some.prop=5");
122122
Path yamlFilePath = projectFile("src/main/resources/application.yml", """
123123
some:
@@ -144,4 +144,67 @@ public class TestValueCompletion {
144144
editor.assertLinkTargets("some.prop", List.of(expectedYamlLocation, expectedPropsLocation));
145145

146146
}
147+
148+
@Test
149+
void noValueCase_ConditionOnPropertyAnnotation() throws Exception {
150+
Path propertiesFilePath = projectFile("src/main/resources/application.properties", "some.prop=5");
151+
Editor editor = harness.newEditor(LanguageId.JAVA, """
152+
package org.test;
153+
154+
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
155+
156+
@ConditionalOnProperty("some.prop")
157+
public class TestValueCompletion {
158+
159+
private String value1;
160+
}""");
161+
162+
LocationLink expectedLocation = new LocationLink(propertiesFilePath.toUri().toASCIIString(),
163+
new Range(new Position(0, 0), new Position(0, 11)), new Range(new Position(0, 10), new Position(0, 11)),
164+
new Range(new Position(4, 23), new Position(4, 34)));
165+
166+
editor.assertLinkTargets("some.prop", List.of(expectedLocation));
167+
}
168+
169+
@Test
170+
void valueCase_ConditionOnPropertyAnnotation() throws Exception {
171+
Path propertiesFilePath = projectFile("src/main/resources/application.properties", "some.prop=5");
172+
Editor editor = harness.newEditor(LanguageId.JAVA, """
173+
package org.test;
174+
175+
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
176+
177+
@ConditionalOnProperty(value = "some.prop")
178+
public class TestValueCompletion {
179+
180+
private String value1;
181+
}""");
182+
183+
LocationLink expectedLocation = new LocationLink(propertiesFilePath.toUri().toASCIIString(),
184+
new Range(new Position(0, 0), new Position(0, 11)), new Range(new Position(0, 10), new Position(0, 11)),
185+
new Range(new Position(4, 31), new Position(4, 42)));
186+
187+
editor.assertLinkTargets("some.prop", List.of(expectedLocation));
188+
}
189+
190+
@Test
191+
void nameAndPrefixCase_ConditionOnPropertyAnnotation() throws Exception {
192+
Path propertiesFilePath = projectFile("src/main/resources/application.properties", "some.prop=5");
193+
Editor editor = harness.newEditor(LanguageId.JAVA, """
194+
package org.test;
195+
196+
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
197+
198+
@ConditionalOnProperty(prefix = "some", name = "prop")
199+
public class TestValueCompletion {
200+
201+
private String value1;
202+
}""");
203+
204+
LocationLink expectedLocation = new LocationLink(propertiesFilePath.toUri().toASCIIString(),
205+
new Range(new Position(0, 0), new Position(0, 11)), new Range(new Position(0, 10), new Position(0, 11)),
206+
new Range(new Position(4, 47), new Position(4, 53)));
207+
208+
editor.assertLinkTargets("prop", List.of(expectedLocation));
209+
}
147210
}

0 commit comments

Comments
 (0)