Skip to content

Commit e04ef5d

Browse files
committed
GH-1545: mongodb query information from aot metadata can now be turned into query annotations in source code
1 parent c860bec commit e04ef5d

File tree

5 files changed

+175
-80
lines changed

5 files changed

+175
-80
lines changed

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,12 @@
1111
package org.springframework.ide.vscode.boot.java.data;
1212

1313
public record DataRepositoryAotMetadata (String name, String type, String module, DataRepositoryAotMetadataMethod[] methods) {
14+
15+
public boolean isJPA() {
16+
return module != null && module.toLowerCase().equals("jpa");
17+
}
18+
19+
public boolean isMongoDb() {
20+
return module != null && module.toLowerCase().equals("mongodb");
21+
}
1422
}

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

Lines changed: 54 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import java.util.List;
1616
import java.util.Map;
1717
import java.util.Optional;
18+
import java.util.Set;
1819
import java.util.stream.Collectors;
1920

2021
import org.apache.commons.text.StringEscapeUtils;
@@ -80,18 +81,30 @@ static boolean isValidMethodBinding(IMethodBinding methodBinding) {
8081
return true;
8182
}
8283

83-
static Optional<String> getDataQuery(DataRepositoryAotMetadataService repositoryMetadataService, IJavaProject project, IMethodBinding methodBinding) {
84+
// static Optional<String> getDataQuery(DataRepositoryAotMetadataService repositoryMetadataService, IJavaProject project, IMethodBinding methodBinding) {
85+
// final String repositoryClass = methodBinding.getDeclaringClass().getBinaryName().trim();
86+
// final IMethodBinding method = methodBinding.getMethodDeclaration();
87+
//
88+
// DataRepositoryAotMetadata metadata = repositoryMetadataService.getRepositoryMetadata(project, repositoryClass);
89+
//
90+
// if (metadata != null) {
91+
// return Optional.ofNullable(repositoryMetadataService.getQueryStatement(metadata, method));
92+
// }
93+
//
94+
// return Optional.empty();
95+
//
96+
// }
97+
98+
static Optional<DataRepositoryAotMetadata> getMetadata(DataRepositoryAotMetadataService dataRepositoryAotMetadataService, IJavaProject project, IMethodBinding methodBinding) {
8499
final String repositoryClass = methodBinding.getDeclaringClass().getBinaryName().trim();
85-
final IMethodBinding method = methodBinding.getMethodDeclaration();
86100

87-
DataRepositoryAotMetadata metadata = repositoryMetadataService.getRepositoryMetadata(project, repositoryClass);
101+
return Optional.ofNullable(dataRepositoryAotMetadataService.getRepositoryMetadata(project, repositoryClass));
102+
}
88103

89-
if (metadata != null) {
90-
return Optional.ofNullable(repositoryMetadataService.getQueryStatement(metadata, method));
91-
}
92-
93-
return Optional.empty();
104+
static Optional<DataRepositoryAotMetadataMethod> getMethodMetadata(DataRepositoryAotMetadataService dataRepositoryAotMetadataService, DataRepositoryAotMetadata metadata, IMethodBinding methodBinding) {
105+
final IMethodBinding method = methodBinding.getMethodDeclaration();
94106

107+
return Optional.ofNullable(dataRepositoryAotMetadataService.findMethod(metadata, method));
95108
}
96109

97110
protected void provideCodeLens(CancelChecker cancelToken, MethodDeclaration node, TextDocument document, List<CodeLens> resultAccumulator) {
@@ -106,13 +119,14 @@ protected void provideCodeLens(CancelChecker cancelToken, MethodDeclaration node
106119

107120
if (isValidMethodBinding(methodBinding)) {
108121
cancelToken.checkCanceled();
109-
getDataQuery(repositoryMetadataService, project, methodBinding)
110-
.map(queryStatement -> createCodeLenses(node, document, queryStatement))
122+
123+
getMetadata(repositoryMetadataService, project, methodBinding)
124+
.map(metadata -> createCodeLenses(node, document, metadata))
111125
.ifPresent(cls -> cls.forEach(resultAccumulator::add));
112126
}
113127
}
114128

115-
private List<CodeLens> createCodeLenses(MethodDeclaration node, TextDocument document, String queryStatement) {
129+
private List<CodeLens> createCodeLenses(MethodDeclaration node, TextDocument document, DataRepositoryAotMetadata metadata) {
116130
List<CodeLens> codeLenses = new ArrayList<>(2);
117131

118132
try {
@@ -122,13 +136,16 @@ private List<CodeLens> createCodeLenses(MethodDeclaration node, TextDocument doc
122136
Range range = new Range(startPos, endPos);
123137
AnnotationHierarchies hierarchyAnnot = AnnotationHierarchies.get(node);
124138

125-
if (mb != null && hierarchyAnnot != null) {
139+
Optional<DataRepositoryAotMetadataMethod> methodMetadata = getMethodMetadata(repositoryMetadataService, metadata, mb);
140+
141+
if (mb != null && hierarchyAnnot != null && methodMetadata.isPresent()) {
126142

127143
boolean isQueryAnnotated = hierarchyAnnot.isAnnotatedWith(mb, Annotations.DATA_JPA_QUERY)
128144
|| hierarchyAnnot.isAnnotatedWith(mb, Annotations.DATA_MONGODB_QUERY);
145+
129146

130147
if (!isQueryAnnotated) {
131-
codeLenses.add(new CodeLens(range, refactorings.createFixCommand(COVERT_TO_QUERY_LABEL, createFixDescriptor(mb, document.getUri(), queryStatement)), null));
148+
codeLenses.add(new CodeLens(range, refactorings.createFixCommand(COVERT_TO_QUERY_LABEL, createFixDescriptor(mb, document.getUri(), metadata, methodMetadata.get())), null));
132149
}
133150

134151
Command impl = new Command("Implementation", GenAotQueryMethodImplProvider.CMD_NAVIGATE_TO_IMPL, List.of(new GenAotQueryMethodImplProvider.GoToImplParams(
@@ -142,7 +159,7 @@ private List<CodeLens> createCodeLenses(MethodDeclaration node, TextDocument doc
142159

143160
if (!isQueryAnnotated) {
144161
Command queryTitle = new Command();
145-
queryTitle.setTitle(queryStatement);
162+
queryTitle.setTitle(methodMetadata.get().getQueryStatement(metadata));
146163
codeLenses.add(new CodeLens(range, queryTitle, null));
147164
}
148165
}
@@ -152,15 +169,31 @@ private List<CodeLens> createCodeLenses(MethodDeclaration node, TextDocument doc
152169
return codeLenses;
153170
}
154171

155-
static FixDescriptor createFixDescriptor(IMethodBinding mb, String docUri, String queryStatement) {
172+
static FixDescriptor createFixDescriptor(IMethodBinding mb, String docUri, DataRepositoryAotMetadata metadata, DataRepositoryAotMetadataMethod methodMetadata) {
156173
return new FixDescriptor(AddAnnotationOverMethod.class.getName(), List.of(docUri), "Turn into `@Query`")
174+
157175
.withRecipeScope(RecipeScope.FILE)
158-
.withParameters(Map.of("annotationType", Annotations.DATA_JPA_QUERY, "method",
159-
"%s %s(%s)".formatted(mb.getDeclaringClass().getQualifiedName(), mb.getName(),
160-
Arrays.stream(mb.getParameterTypes()).map(pt -> pt.getQualifiedName())
161-
.collect(Collectors.joining(", "))),
162-
"attributes", List.of(new AddAnnotationOverMethod.Attribute("value",
163-
"\"%s\"".formatted(StringEscapeUtils.escapeJava(queryStatement))))));
176+
177+
.withParameters(Map.of(
178+
"annotationType", metadata.isJPA() ? Annotations.DATA_JPA_QUERY : Annotations.DATA_MONGODB_QUERY,
179+
"method", "%s %s(%s)".formatted(mb.getDeclaringClass().getQualifiedName(), mb.getName(),
180+
Arrays.stream(mb.getParameterTypes())
181+
.map(pt -> pt.getQualifiedName())
182+
.collect(Collectors.joining(", "))),
183+
"attributes", createAttributeList(methodMetadata.getAttributesMap(metadata))));
164184
}
185+
186+
private static List<AddAnnotationOverMethod.Attribute> createAttributeList(Map<String, String> attributes) {
187+
List<AddAnnotationOverMethod.Attribute> result = new ArrayList<>();
188+
189+
Set<String> keys = attributes.keySet();
190+
for (String key : keys) {
191+
result.add(new AddAnnotationOverMethod.Attribute(key, "\"%s\"".formatted(StringEscapeUtils.escapeJava(attributes.get(key)))));
192+
}
193+
194+
return result;
195+
}
196+
197+
165198

166199
}

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

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,99 @@
1010
*******************************************************************************/
1111
package org.springframework.ide.vscode.boot.java.data;
1212

13+
import java.util.ArrayList;
14+
import java.util.HashMap;
15+
import java.util.List;
16+
import java.util.Map;
17+
18+
import org.springframework.util.StringUtils;
19+
1320
public record DataRepositoryAotMetadataMethod(String name, String signature, DataRepositoryAotMetadataQuery query) {
21+
22+
public String getQueryStatement(DataRepositoryAotMetadata repository) {
23+
if (repository != null && repository.isJPA()) {
24+
return getJpaQueryStatement();
25+
}
26+
else if (repository != null && repository.isMongoDb()) {
27+
return getMongoDbQueryStatement();
28+
}
29+
else {
30+
return null;
31+
}
32+
}
33+
34+
private String getJpaQueryStatement() {
35+
return query() != null ? query.query(): null;
36+
}
37+
38+
private String getMongoDbQueryStatement() {
39+
List<String> parts = new ArrayList<>();
40+
41+
if (query == null) return null;
42+
43+
if (query().filter() != null) {
44+
if (!StringUtils.hasText(query().sort())
45+
&& !StringUtils.hasText(query().fields())
46+
&& !StringUtils.hasText(query().projection())
47+
&& !StringUtils.hasText(query().pipeline())) {
48+
49+
parts.add(query().filter());
50+
}
51+
else {
52+
parts.add("filter = \"" + query().filter() + "\"");
53+
}
54+
}
55+
56+
if (query().fields() != null) {
57+
parts.add("fields = \"" + query().fields() + "\"");
58+
}
59+
60+
if (query().sort() != null) {
61+
parts.add("sort = \"" + query().sort() + "\"");
62+
}
63+
64+
if (query().projection() != null) {
65+
parts.add("projection = \"" + query().projection() + "\"");
66+
}
67+
68+
if (query().pipeline() != null) {
69+
parts.add("pipeline = \"" + query().pipeline() + "\"");
70+
}
71+
72+
return String.join(", ", parts);
73+
}
74+
75+
public Map<String, String> getAttributesMap(DataRepositoryAotMetadata metadata) {
76+
if (metadata != null && metadata.isJPA()) {
77+
return Map.of("value", getJpaQueryStatement());
78+
}
79+
else if (metadata != null && metadata.isMongoDb()) {
80+
if (query != null) {
81+
return createMongoDbQueryAttributes();
82+
}
83+
}
84+
85+
return Map.of();
86+
}
87+
88+
private Map<String, String> createMongoDbQueryAttributes() {
89+
Map<String, String> result = new HashMap<>();
90+
91+
if (query.filter() != null) {
92+
result.put("value", query.filter());
93+
}
94+
95+
if (query().fields() != null) {
96+
result.put("fields", query().fields());
97+
}
98+
99+
if (query().sort() != null) {
100+
result.put("sort", query().sort());
101+
}
102+
103+
// TODO; what about projection and pipeline ?
104+
105+
return result;
106+
}
107+
14108
}

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

Lines changed: 2 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@
1313
import java.io.File;
1414
import java.io.FileReader;
1515
import java.io.IOException;
16-
import java.util.ArrayList;
17-
import java.util.List;
1816
import java.util.Optional;
1917

2018
import org.eclipse.jdt.core.dom.IMethodBinding;
@@ -25,7 +23,6 @@
2523
import org.springframework.ide.vscode.commons.java.IJavaProject;
2624
import org.springframework.ide.vscode.commons.java.parser.JLRMethodParser;
2725
import org.springframework.ide.vscode.commons.java.parser.JLRMethodParser.JLRMethod;
28-
import org.springframework.util.StringUtils;
2926

3027
import com.google.gson.Gson;
3128

@@ -71,58 +68,9 @@ private DataRepositoryAotMetadata readMetadataFile(File file) {
7168

7269
public String getQueryStatement(DataRepositoryAotMetadata metadata, IMethodBinding method) {
7370
DataRepositoryAotMetadataMethod methodMetadata = findMethod(metadata, method);
74-
75-
if (methodMetadata != null) {
76-
if (metadata.module() != null && metadata.module().toUpperCase().equals("JPA")) {
77-
return getJpaQueryStatement(methodMetadata);
78-
}
79-
else if (metadata.module() != null && metadata.module().toUpperCase().equals("MONGODB")) {
80-
return getMongoDbQueryStatement(methodMetadata);
81-
}
82-
}
83-
84-
return null;
85-
}
86-
87-
private String getMongoDbQueryStatement(DataRepositoryAotMetadataMethod methodMetadata) {
88-
List<String> parts = new ArrayList<>();
89-
90-
if (methodMetadata.query().filter() != null) {
91-
if (!StringUtils.hasText(methodMetadata.query().sort())
92-
&& !StringUtils.hasText(methodMetadata.query().fields())
93-
&& !StringUtils.hasText(methodMetadata.query().projection())
94-
&& !StringUtils.hasText(methodMetadata.query().pipeline())) {
95-
96-
parts.add(methodMetadata.query().filter());
97-
}
98-
else {
99-
parts.add("filter = \"" + methodMetadata.query().filter() + "\"");
100-
}
101-
}
102-
103-
if (methodMetadata.query().fields() != null) {
104-
parts.add("fields = \"" + methodMetadata.query().fields() + "\"");
105-
}
106-
107-
if (methodMetadata.query().sort() != null) {
108-
parts.add("sort = \"" + methodMetadata.query().sort() + "\"");
109-
}
110-
111-
if (methodMetadata.query().projection() != null) {
112-
parts.add("projection = \"" + methodMetadata.query().projection() + "\"");
113-
}
114-
115-
if (methodMetadata.query().pipeline() != null) {
116-
parts.add("pipeline = \"" + methodMetadata.query().pipeline() + "\"");
117-
}
118-
119-
return String.join(", ", parts);
120-
}
121-
122-
private String getJpaQueryStatement(DataRepositoryAotMetadataMethod methodMetadata) {
123-
return methodMetadata.query().query();
71+
return methodMetadata.getQueryStatement(metadata);
12472
}
125-
73+
12674
public DataRepositoryAotMetadataMethod findMethod(DataRepositoryAotMetadata metadata, IMethodBinding method) {
12775
String name = method.getName();
12876

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

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
package org.springframework.ide.vscode.boot.java.data;
1212

1313
import java.net.URI;
14+
import java.util.Optional;
1415

1516
import org.eclipse.jdt.core.dom.ASTVisitor;
1617
import org.eclipse.jdt.core.dom.CompilationUnit;
@@ -56,15 +57,26 @@ public ASTVisitor createVisitor(CancelChecker cancelToken, IJavaProject project,
5657
@Override
5758
public boolean visit(MethodDeclaration node) {
5859
cancelToken.checkCanceled();
60+
5961
if (node.getStartPosition() <= region.getStart() && node.getStartPosition() + node.getLength() >= region.getEnd()) {
6062
int start = node.getStartPosition();
6163
int end = node.getName().getStartPosition() + node.getName().getLength();
64+
6265
if (start <= region.getStart() && end >= region.getEnd()) {
66+
6367
IMethodBinding binding = node.resolveBinding();
6468
AnnotationHierarchies hierarchyAnnot = AnnotationHierarchies.get(node);
65-
if (hierarchyAnnot != null && !hierarchyAnnot.isAnnotatedWith(binding, Annotations.DATA_JPA_QUERY)) {
66-
DataRepositoryAotMetadataCodeLensProvider.getDataQuery(repositoryMetadataService, project, binding)
67-
.map(query -> createCodeAction(binding, docURI, query)).ifPresent(collector::accept);
69+
if (hierarchyAnnot != null
70+
&& !hierarchyAnnot.isAnnotatedWith(binding, Annotations.DATA_JPA_QUERY)
71+
&& !hierarchyAnnot.isAnnotatedWith(binding, Annotations.DATA_MONGODB_QUERY)) {
72+
73+
Optional<DataRepositoryAotMetadata> metadata = DataRepositoryAotMetadataCodeLensProvider.getMetadata(repositoryMetadataService, project, binding);
74+
if (metadata.isPresent()) {
75+
Optional<DataRepositoryAotMetadataMethod> methodMetadata = DataRepositoryAotMetadataCodeLensProvider.getMethodMetadata(repositoryMetadataService, metadata.get(), binding);
76+
methodMetadata
77+
.map(method -> createCodeAction(binding, docURI, metadata.get(), method))
78+
.ifPresent(collector::accept);
79+
}
6880
}
6981
}
7082
return super.visit(node);
@@ -75,9 +87,9 @@ public boolean visit(MethodDeclaration node) {
7587
};
7688
}
7789

78-
private CodeAction createCodeAction(IMethodBinding mb, URI docUri, String query) {
90+
private CodeAction createCodeAction(IMethodBinding mb, URI docUri, DataRepositoryAotMetadata metadata, DataRepositoryAotMetadataMethod method) {
7991
CodeAction ca = new CodeAction();
80-
ca.setCommand(refactorings.createFixCommand(TITLE, DataRepositoryAotMetadataCodeLensProvider.createFixDescriptor(mb, docUri.toASCIIString(), query)));
92+
ca.setCommand(refactorings.createFixCommand(TITLE, DataRepositoryAotMetadataCodeLensProvider.createFixDescriptor(mb, docUri.toASCIIString(), metadata, method)));
8193
ca.setTitle(TITLE);
8294
ca.setKind(CodeActionKind.Refactor);
8395
return ca;

0 commit comments

Comments
 (0)