Navigate to property value from @ConditionalOnProperty
This commit is contained in:
@@ -19,9 +19,11 @@ import java.util.Optional;
|
||||
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.FieldDeclaration;
|
||||
import org.eclipse.jdt.core.dom.Expression;
|
||||
import org.eclipse.jdt.core.dom.IAnnotationBinding;
|
||||
import org.eclipse.jdt.core.dom.MemberValuePair;
|
||||
import org.eclipse.jdt.core.dom.NormalAnnotation;
|
||||
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
|
||||
import org.eclipse.jdt.core.dom.StringLiteral;
|
||||
import org.eclipse.lsp4j.Location;
|
||||
import org.eclipse.lsp4j.LocationLink;
|
||||
@@ -43,6 +45,36 @@ import com.google.common.collect.ImmutableList.Builder;
|
||||
public class PropertyValueAnnotationDefProvider implements IJavaDefinitionProvider {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(PropertyValueAnnotationDefProvider.class);
|
||||
|
||||
private static final String PARAM_VALUE = "value";
|
||||
private static final String PARAM_NAME = "name";
|
||||
private static final String PARAM_PREFIX = "prefix";
|
||||
|
||||
private Map<String, PropertyKeyExtractor> annotationToPropertyKeyExtractor = Map.of(
|
||||
Annotations.VALUE, (a, p, v) -> {
|
||||
if (a.isSingleMemberAnnotation()) {
|
||||
return extractPropertyKey(v.getLiteralValue());
|
||||
} else if (a.isNormalAnnotation() && PARAM_VALUE.equals(p.getName().getIdentifier())) {
|
||||
return extractPropertyKey(v.getLiteralValue());
|
||||
}
|
||||
return null;
|
||||
},
|
||||
Annotations.CONDITIONAL_ON_PROPERTY, (a, p, v) -> {
|
||||
if (a.isSingleMemberAnnotation()) {
|
||||
return v.getLiteralValue();
|
||||
} else if (a.isNormalAnnotation()) {
|
||||
switch (p.getName().getIdentifier()) {
|
||||
case PARAM_VALUE:
|
||||
return v.getLiteralValue();
|
||||
case PARAM_NAME:
|
||||
String prefix = extractAnnotationParameter(a, PARAM_PREFIX);
|
||||
String name = v.getLiteralValue();
|
||||
return prefix != null && !prefix.isBlank() ? prefix + "." + name : name;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
);
|
||||
|
||||
@Override
|
||||
public List<LocationLink> getDefinitions(CancelChecker cancelToken, IJavaProject project, CompilationUnit cu,
|
||||
@@ -52,13 +84,26 @@ public class PropertyValueAnnotationDefProvider implements IJavaDefinitionProvid
|
||||
String propertyKey = null;
|
||||
|
||||
ASTNode parent = valueNode.getParent();
|
||||
if (parent instanceof Annotation && isApplicableValueAnnotation((Annotation) parent)) {
|
||||
propertyKey = extractPropertyKey(valueNode.getLiteralValue());
|
||||
if (parent instanceof Annotation) {
|
||||
Annotation a = (Annotation) parent;
|
||||
IAnnotationBinding binding = a.resolveAnnotationBinding();
|
||||
if (binding != null && binding.getAnnotationType() != null) {
|
||||
PropertyKeyExtractor propertyExtractor = annotationToPropertyKeyExtractor.get(binding.getAnnotationType().getQualifiedName());
|
||||
if (propertyExtractor != null) {
|
||||
propertyKey = propertyExtractor.extract(a, null, valueNode);
|
||||
}
|
||||
}
|
||||
} else if (parent instanceof MemberValuePair
|
||||
&& "value".equals(((MemberValuePair) parent).getName().getIdentifier())
|
||||
&& parent.getParent() instanceof Annotation
|
||||
&& isApplicableValueAnnotation((Annotation) parent.getParent())) {
|
||||
propertyKey = extractPropertyKey(valueNode.getLiteralValue());
|
||||
&& parent.getParent() instanceof Annotation) {
|
||||
MemberValuePair pair = (MemberValuePair) parent;
|
||||
Annotation a = (Annotation) parent.getParent();
|
||||
IAnnotationBinding binding = a.resolveAnnotationBinding();
|
||||
if (binding != null && binding.getAnnotationType() != null) {
|
||||
PropertyKeyExtractor propertyExtractor = annotationToPropertyKeyExtractor.get(binding.getAnnotationType().getQualifiedName());
|
||||
if (propertyExtractor != null) {
|
||||
propertyKey = propertyExtractor.extract(a, pair, valueNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (propertyKey != null) {
|
||||
@@ -87,12 +132,6 @@ public class PropertyValueAnnotationDefProvider implements IJavaDefinitionProvid
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private static boolean isApplicableValueAnnotation(Annotation a) {
|
||||
IAnnotationBinding binding = a.resolveAnnotationBinding();
|
||||
return binding != null && Annotations.VALUE.equals(binding.getAnnotationType().getQualifiedName())
|
||||
&& a.getParent() instanceof FieldDeclaration;
|
||||
}
|
||||
|
||||
private List<Location> findValueReferences(IJavaProject project, String propertyKey, Map<Location, Range> targetRanges) {
|
||||
Builder<Location> links = ImmutableList.builder();
|
||||
IClasspathUtil.getClasspathResourcesFullPaths(project.getClasspath()).forEach(path -> {
|
||||
@@ -159,6 +198,25 @@ public class PropertyValueAnnotationDefProvider implements IJavaDefinitionProvid
|
||||
});
|
||||
return links.build();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static String extractAnnotationParameter(Annotation a, String param) {
|
||||
Expression value = null;
|
||||
if (a.isSingleMemberAnnotation() && PARAM_VALUE.equals(param)) {
|
||||
value = ((SingleMemberAnnotation) a).getValue();
|
||||
} else if (a.isNormalAnnotation()) {
|
||||
for (MemberValuePair pair : (List<MemberValuePair>) ((NormalAnnotation) a).values()) {
|
||||
if (param.equals(pair.getName().getIdentifier())) {
|
||||
value = pair.getValue();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (value instanceof StringLiteral) {
|
||||
return ((StringLiteral) value).getLiteralValue();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String extractPropertyKey(String s) {
|
||||
if (s.length() > 3 && (s.startsWith("${") || s.startsWith("#{")) && s.endsWith("}")) {
|
||||
@@ -166,5 +224,9 @@ public class PropertyValueAnnotationDefProvider implements IJavaDefinitionProvid
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private interface PropertyKeyExtractor {
|
||||
String extract(Annotation a, MemberValuePair pair, StringLiteral v);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ public class PropertyValueAnnotationDefProviderTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void propertiesCase() throws Exception {
|
||||
void propertiesCase_ValueAnnotation() throws Exception {
|
||||
Path propertiesFilePath = projectFile("src/main/resources/application.properties", "some.prop=5");
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA, """
|
||||
package org.test;
|
||||
@@ -93,7 +93,7 @@ public class PropertyValueAnnotationDefProviderTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void yamlCase() throws Exception {
|
||||
void yamlCase_ValueAnnotation() throws Exception {
|
||||
Path yamlFilePath = projectFile("src/main/resources/application.yml", """
|
||||
some:
|
||||
prop: 5
|
||||
@@ -117,7 +117,7 @@ public class PropertyValueAnnotationDefProviderTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void combinedCase() throws Exception {
|
||||
void combinedCase_ValueAnnotation() throws Exception {
|
||||
Path propertiesFilePath = projectFile("src/main/resources/application.properties", "some.prop=5");
|
||||
Path yamlFilePath = projectFile("src/main/resources/application.yml", """
|
||||
some:
|
||||
@@ -144,4 +144,67 @@ public class PropertyValueAnnotationDefProviderTest {
|
||||
editor.assertLinkTargets("some.prop", List.of(expectedYamlLocation, expectedPropsLocation));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void noValueCase_ConditionOnPropertyAnnotation() throws Exception {
|
||||
Path propertiesFilePath = projectFile("src/main/resources/application.properties", "some.prop=5");
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA, """
|
||||
package org.test;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
|
||||
@ConditionalOnProperty("some.prop")
|
||||
public class TestValueCompletion {
|
||||
|
||||
private String value1;
|
||||
}""");
|
||||
|
||||
LocationLink expectedLocation = new LocationLink(propertiesFilePath.toUri().toASCIIString(),
|
||||
new Range(new Position(0, 0), new Position(0, 11)), new Range(new Position(0, 10), new Position(0, 11)),
|
||||
new Range(new Position(4, 23), new Position(4, 34)));
|
||||
|
||||
editor.assertLinkTargets("some.prop", List.of(expectedLocation));
|
||||
}
|
||||
|
||||
@Test
|
||||
void valueCase_ConditionOnPropertyAnnotation() throws Exception {
|
||||
Path propertiesFilePath = projectFile("src/main/resources/application.properties", "some.prop=5");
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA, """
|
||||
package org.test;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
|
||||
@ConditionalOnProperty(value = "some.prop")
|
||||
public class TestValueCompletion {
|
||||
|
||||
private String value1;
|
||||
}""");
|
||||
|
||||
LocationLink expectedLocation = new LocationLink(propertiesFilePath.toUri().toASCIIString(),
|
||||
new Range(new Position(0, 0), new Position(0, 11)), new Range(new Position(0, 10), new Position(0, 11)),
|
||||
new Range(new Position(4, 31), new Position(4, 42)));
|
||||
|
||||
editor.assertLinkTargets("some.prop", List.of(expectedLocation));
|
||||
}
|
||||
|
||||
@Test
|
||||
void nameAndPrefixCase_ConditionOnPropertyAnnotation() throws Exception {
|
||||
Path propertiesFilePath = projectFile("src/main/resources/application.properties", "some.prop=5");
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA, """
|
||||
package org.test;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
|
||||
@ConditionalOnProperty(prefix = "some", name = "prop")
|
||||
public class TestValueCompletion {
|
||||
|
||||
private String value1;
|
||||
}""");
|
||||
|
||||
LocationLink expectedLocation = new LocationLink(propertiesFilePath.toUri().toASCIIString(),
|
||||
new Range(new Position(0, 0), new Position(0, 11)), new Range(new Position(0, 10), new Position(0, 11)),
|
||||
new Range(new Position(4, 47), new Position(4, 53)));
|
||||
|
||||
editor.assertLinkTargets("prop", List.of(expectedLocation));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user