Skip to content

Commit dd49b56

Browse files
committed
WIP
1 parent d45e841 commit dd49b56

File tree

11 files changed

+223
-109
lines changed

11 files changed

+223
-109
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2025 Broadcom, Inc.
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, Inc. - initial API and implementation
10+
*******************************************************************************/
11+
package org.springframework.ide.vscode.commons.languageserver.util;
12+
13+
public class OS {
14+
15+
public static boolean isWindows() {
16+
String os = System.getProperty("os.name");
17+
if (os != null) {
18+
return os.toLowerCase().indexOf("win") >= 0;
19+
}
20+
return false;
21+
}
22+
23+
}

headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/ParentProcessWatcher.java

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ private boolean parentProcessStillRunning() {
7171
return true;
7272
}
7373
String command;
74-
if (isWindows()) {
74+
if (OS.isWindows()) {
7575
command = "cmd /c \"tasklist /FI \"PID eq " + pid + "\" | findstr " + pid + "\"";
7676
} else {
7777
command = "kill -0 " + pid;
@@ -85,7 +85,7 @@ private boolean parentProcessStillRunning() {
8585
process.destroy();
8686
finished = process.waitFor(POLL_DELAY_SECS, TimeUnit.SECONDS); // wait for the process to stop
8787
}
88-
if (isWindows() && finished && process.exitValue() > 1) {
88+
if (OS.isWindows() && finished && process.exitValue() > 1) {
8989
// the tasklist command should return 0 (parent process exists) or 1 (parent process doesn't exist)
9090
logger.info("The tasklist command: '{}' returns {}", command, process.exitValue());
9191
return true;
@@ -103,7 +103,7 @@ private boolean parentProcessStillRunning() {
103103
// It is only closed when the Process object is garbage collected (in its finalize() method).
104104
// On Windows, when the Java LS is idle, we need to explicitly request a GC,
105105
// to prevent an accumulation of zombie processes, as finalize() will be called.
106-
if (isWindows()) {
106+
if (OS.isWindows()) {
107107
// Java >= 9 doesn't close the handle when the process is garbage collected
108108
// We need to close the opened streams
109109
if (!isJava1x) {
@@ -134,11 +134,4 @@ public MessageConsumer apply(final MessageConsumer consumer) {
134134
};
135135
}
136136

137-
private static boolean isWindows() {
138-
String os = System.getProperty("os.name");
139-
if (os != null) {
140-
return os.toLowerCase().indexOf("win") >= 0;
141-
}
142-
return false;
143-
}
144137
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -436,8 +436,8 @@ ModulithService modulithService(SimpleLanguageServer server, JavaProjectFinder p
436436
}
437437

438438
@Bean
439-
DataRepositoryAotMetadataService dataAotMetadataService() {
440-
return new DataRepositoryAotMetadataService();
439+
DataRepositoryAotMetadataService dataAotMetadataService(SimpleLanguageServer server, FileObserver fileObserver, JavaProjectFinder projectFinder) {
440+
return new DataRepositoryAotMetadataService(server, fileObserver, projectFinder);
441441
}
442442

443443
@Bean

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

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

13+
import java.util.Optional;
14+
1315
import org.eclipse.jdt.core.dom.IMethodBinding;
1416
import org.eclipse.jdt.core.dom.ITypeBinding;
1517
import org.springframework.ide.vscode.commons.java.parser.JLRMethodParser;
1618
import org.springframework.ide.vscode.commons.java.parser.JLRMethodParser.JLRMethod;
1719

1820
public record DataRepositoryAotMetadata (String name, String type, DataRepositoryModule module, IDataRepositoryAotMethodMetadata[] methods) {
1921

20-
public IDataRepositoryAotMethodMetadata findMethod(IMethodBinding method) {
22+
public Optional<IDataRepositoryAotMethodMetadata> findMethod(IMethodBinding method) {
2123
String name = method.getName();
2224

2325
for (IDataRepositoryAotMethodMetadata methodMetadata : methods()) {
@@ -31,12 +33,12 @@ public IDataRepositoryAotMethodMetadata findMethod(IMethodBinding method) {
3133
&& parsedMethodSignature.getMethodName().equals(method.getName())
3234
&& parsedMethodSignature.getReturnType().equals(method.getReturnType().getQualifiedName())
3335
&& parameterMatches(parsedMethodSignature, method)) {
34-
return methodMetadata;
36+
return Optional.of(methodMetadata);
3537
}
3638
}
3739
}
3840

39-
return null;
41+
return Optional.empty();
4042
}
4143

4244
private boolean parameterMatches(JLRMethod parsedMethodSignature, IMethodBinding method) {

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

Lines changed: 49 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,10 @@
3535
import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchies;
3636
import org.springframework.ide.vscode.boot.java.handlers.CodeLensProvider;
3737
import org.springframework.ide.vscode.boot.java.rewrite.RewriteRefactorings;
38+
import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
39+
import org.springframework.ide.vscode.commons.Version;
3840
import org.springframework.ide.vscode.commons.java.IJavaProject;
41+
import org.springframework.ide.vscode.commons.java.SpringProjectUtil;
3942
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
4043
import org.springframework.ide.vscode.commons.rewrite.config.RecipeScope;
4144
import org.springframework.ide.vscode.commons.rewrite.java.AddAnnotationOverMethod;
@@ -85,9 +88,19 @@ static boolean isValidMethodBinding(IMethodBinding methodBinding) {
8588
|| methodBinding.getDeclaringClass().getBinaryName() == null) {
8689
return false;
8790
}
91+
92+
if (ASTUtils.findInTypeHierarchy(methodBinding.getDeclaringClass(), Set.of(Constants.REPOSITORY_TYPE)) == null) {
93+
return false;
94+
}
95+
8896
return true;
8997
}
9098

99+
static boolean isValidProject(IJavaProject jp) {
100+
Version version = SpringProjectUtil.getDependencyVersion(jp, "spring-data-commons");
101+
return version != null && version.compareTo(new Version(4, 0, 0, null)) >= 0;
102+
}
103+
91104
// static Optional<String> getDataQuery(DataRepositoryAotMetadataService repositoryMetadataService, IJavaProject project, IMethodBinding methodBinding) {
92105
// final String repositoryClass = methodBinding.getDeclaringClass().getBinaryName().trim();
93106
// final IMethodBinding method = methodBinding.getMethodDeclaration();
@@ -104,21 +117,14 @@ static boolean isValidMethodBinding(IMethodBinding methodBinding) {
104117

105118
static Optional<DataRepositoryAotMetadata> getMetadata(DataRepositoryAotMetadataService dataRepositoryAotMetadataService, IJavaProject project, IMethodBinding methodBinding) {
106119
final String repositoryClass = methodBinding.getDeclaringClass().getBinaryName().trim();
107-
108-
return Optional.ofNullable(dataRepositoryAotMetadataService.getRepositoryMetadata(project, repositoryClass));
109-
}
110-
111-
static Optional<IDataRepositoryAotMethodMetadata> getMethodMetadata(DataRepositoryAotMetadataService dataRepositoryAotMetadataService, DataRepositoryAotMetadata metadata, IMethodBinding methodBinding) {
112-
final IMethodBinding method = methodBinding.getMethodDeclaration();
113-
114-
return Optional.ofNullable(metadata.findMethod(method));
120+
return dataRepositoryAotMetadataService.getRepositoryMetadata(project, repositoryClass);
115121
}
116122

117123
protected void provideCodeLens(CancelChecker cancelToken, MethodDeclaration node, TextDocument document, List<CodeLens> resultAccumulator) {
118124
cancelToken.checkCanceled();
119125

120126
IJavaProject project = projectFinder.find(document.getId()).get();
121-
if (project == null) {
127+
if (project == null || !isValidProject(project)) {
122128
return;
123129
}
124130

@@ -127,13 +133,11 @@ protected void provideCodeLens(CancelChecker cancelToken, MethodDeclaration node
127133
if (isValidMethodBinding(methodBinding)) {
128134
cancelToken.checkCanceled();
129135

130-
getMetadata(repositoryMetadataService, project, methodBinding)
131-
.map(metadata -> createCodeLenses(node, document, metadata))
132-
.ifPresent(cls -> cls.forEach(resultAccumulator::add));
136+
resultAccumulator.addAll(createCodeLenses(project, node, document));
133137
}
134138
}
135139

136-
private List<CodeLens> createCodeLenses(MethodDeclaration node, TextDocument document, DataRepositoryAotMetadata metadata) {
140+
private List<CodeLens> createCodeLenses(IJavaProject project, MethodDeclaration node, TextDocument document) {
137141
List<CodeLens> codeLenses = new ArrayList<>(2);
138142

139143
try {
@@ -143,49 +147,52 @@ private List<CodeLens> createCodeLenses(MethodDeclaration node, TextDocument doc
143147
Range range = new Range(startPos, endPos);
144148
AnnotationHierarchies hierarchyAnnot = AnnotationHierarchies.get(node);
145149

146-
Optional<IDataRepositoryAotMethodMetadata> methodMetadata = getMethodMetadata(repositoryMetadataService, metadata, mb);
147-
148-
if (mb != null && hierarchyAnnot != null && methodMetadata.isPresent()) {
149-
150-
boolean isQueryAnnotated = hierarchyAnnot.isAnnotatedWith(mb, Annotations.DATA_JPA_QUERY)
151-
|| hierarchyAnnot.isAnnotatedWith(mb, Annotations.DATA_MONGODB_QUERY);
152-
153-
154-
if (!isQueryAnnotated) {
155-
codeLenses.add(new CodeLens(range, refactorings.createFixCommand(COVERT_TO_QUERY_LABEL, createFixDescriptor(mb, document.getUri(), metadata, methodMetadata.get())), null));
156-
}
157-
158-
Command impl = new Command("Implementation", GenAotQueryMethodImplProvider.CMD_NAVIGATE_TO_IMPL, List.of(new GenAotQueryMethodImplProvider.GoToImplParams(
159-
document.getId(),
160-
mb.getDeclaringClass().getQualifiedName(),
161-
mb.getName(),
162-
Arrays.stream(mb.getParameterTypes()).map(p -> p.getQualifiedName()).toArray(String[]::new),
163-
null
164-
)));
165-
codeLenses.add(new CodeLens(range, impl, null));
150+
if (mb != null && hierarchyAnnot != null) {
166151

167-
if (!isQueryAnnotated) {
168-
String queryStatement = methodMetadata.get().getQueryStatement();
169-
if (queryStatement != null) {
170-
Command queryTitle = new Command();
171-
queryTitle.setTitle(queryStatement);
172-
codeLenses.add(new CodeLens(range, queryTitle, null));
152+
getMetadata(repositoryMetadataService, project, mb).ifPresent(metadata -> metadata.findMethod(mb).ifPresent(methodMetadata -> {
153+
boolean isQueryAnnotated = hierarchyAnnot.isAnnotatedWith(mb, Annotations.DATA_JPA_QUERY)
154+
|| hierarchyAnnot.isAnnotatedWith(mb, Annotations.DATA_MONGODB_QUERY);
155+
156+
if (!isQueryAnnotated) {
157+
codeLenses.add(new CodeLens(range, refactorings.createFixCommand(COVERT_TO_QUERY_LABEL, createFixDescriptor(mb, document.getUri(), metadata.module(), methodMetadata)), null));
173158
}
174-
}
159+
160+
Command impl = new Command("Implementation", GenAotQueryMethodImplProvider.CMD_NAVIGATE_TO_IMPL, List.of(new GenAotQueryMethodImplProvider.GoToImplParams(
161+
document.getId(),
162+
mb.getDeclaringClass().getQualifiedName(),
163+
mb.getName(),
164+
Arrays.stream(mb.getParameterTypes()).map(p -> p.getQualifiedName()).toArray(String[]::new),
165+
null
166+
)));
167+
codeLenses.add(new CodeLens(range, impl, null));
168+
169+
if (!isQueryAnnotated && methodMetadata != null) {
170+
String queryStatement = methodMetadata.getQueryStatement();
171+
if (queryStatement != null) {
172+
Command queryTitle = new Command();
173+
queryTitle.setTitle(queryStatement);
174+
codeLenses.add(new CodeLens(range, queryTitle, null));
175+
}
176+
}
177+
}));
178+
179+
Command refreshCmd = new Command("Refresh", DataRepositoryAotMetadataService.CMD_REFESH_METADATA, List.of(project.getElementName()));
180+
codeLenses.add(new CodeLens(range, refreshCmd, null));
181+
175182
}
176183
} catch (BadLocationException e) {
177184
log.error("bad location while calculating code lens for data repository query method", e);
178185
}
179186
return codeLenses;
180187
}
181188

182-
static FixDescriptor createFixDescriptor(IMethodBinding mb, String docUri, DataRepositoryAotMetadata metadata, IDataRepositoryAotMethodMetadata methodMetadata) {
189+
static FixDescriptor createFixDescriptor(IMethodBinding mb, String docUri, DataRepositoryModule module, IDataRepositoryAotMethodMetadata methodMetadata) {
183190
return new FixDescriptor(AddAnnotationOverMethod.class.getName(), List.of(docUri), "Turn into `@Query`")
184191

185192
.withRecipeScope(RecipeScope.FILE)
186193

187194
.withParameters(Map.of(
188-
"annotationType", metadata.module() == DataRepositoryModule.JPA ? Annotations.DATA_JPA_QUERY : Annotations.DATA_MONGODB_QUERY,
195+
"annotationType", module == DataRepositoryModule.JPA ? Annotations.DATA_JPA_QUERY : Annotations.DATA_MONGODB_QUERY,
189196
"method", "%s %s(%s)".formatted(mb.getDeclaringClass().getQualifiedName(), mb.getName(),
190197
Arrays.stream(mb.getParameterTypes())
191198
.map(pt -> pt.getQualifiedName())
@@ -203,7 +210,5 @@ private static List<AddAnnotationOverMethod.Attribute> createAttributeList(Map<S
203210

204211
return result;
205212
}
206-
207-
208213

209214
}

0 commit comments

Comments
 (0)