Skip to content

Commit c860bec

Browse files
committed
Merge branch 'main' of https://github.com/spring-projects/spring-tools into main
2 parents 1b4b2b7 + 7576401 commit c860bec

File tree

2 files changed

+161
-10
lines changed

2 files changed

+161
-10
lines changed

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

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

2020
import org.eclipse.jdt.core.dom.IMethodBinding;
21+
import org.eclipse.jdt.core.dom.ITypeBinding;
2122
import org.slf4j.Logger;
2223
import org.slf4j.LoggerFactory;
2324
import org.springframework.ide.vscode.commons.java.IClasspathUtil;
@@ -122,29 +123,45 @@ private String getJpaQueryStatement(DataRepositoryAotMetadataMethod methodMetada
122123
return methodMetadata.query().query();
123124
}
124125

125-
private DataRepositoryAotMetadataMethod findMethod(DataRepositoryAotMetadata metadata, IMethodBinding method) {
126+
public DataRepositoryAotMetadataMethod findMethod(DataRepositoryAotMetadata metadata, IMethodBinding method) {
126127
String name = method.getName();
127128

128129
for (DataRepositoryAotMetadataMethod methodMetadata : metadata.methods()) {
129130

130-
// TODO: This check needs more exact method signature matching - which is a little more complicated
131-
// due to runtime Method.toGenericString() output needs to be compared to IMethodBinding source level method information
132-
133131
if (methodMetadata.name() != null && methodMetadata.name().equals(name)) {
132+
134133
String signature = methodMetadata.signature();
135134
JLRMethod parsedMethodSignature = JLRMethodParser.parse(signature);
136135

137-
String methodName = parsedMethodSignature.getMethodName();
138-
String[] parameters = parsedMethodSignature.getParameters();
139-
String returnType = parsedMethodSignature.getReturnType();
140-
parsedMethodSignature.getFQClassName();
141-
142-
return methodMetadata;
136+
if (parsedMethodSignature.getFQClassName().equals(metadata.name())
137+
&& parsedMethodSignature.getMethodName().equals(method.getName())
138+
&& parsedMethodSignature.getReturnType().equals(method.getReturnType().getQualifiedName())
139+
&& parameterMatches(parsedMethodSignature, method)) {
140+
return methodMetadata;
141+
}
143142
}
144143
}
145144

146145
return null;
147146
}
148147

148+
private boolean parameterMatches(JLRMethod parsedMethodSignature, IMethodBinding method) {
149+
String[] parsedParameeterTypes = parsedMethodSignature.getParameters();
150+
ITypeBinding[] methodParameters = method.getParameterTypes();
151+
152+
if (parsedParameeterTypes == null || methodParameters == null || parsedParameeterTypes.length != methodParameters.length) {
153+
return false;
154+
}
155+
156+
for (int i = 0; i < parsedParameeterTypes.length; i++) {
157+
String qualifiedName = methodParameters[i].getQualifiedName();
158+
if (qualifiedName != null && !qualifiedName.equals(parsedParameeterTypes[i])) {
159+
return false;
160+
}
161+
}
162+
163+
return true;
164+
}
165+
149166

150167
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
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.boot.java.data.test;
12+
13+
import static org.junit.Assert.assertEquals;
14+
import static org.junit.Assert.assertNotNull;
15+
16+
import java.net.URI;
17+
import java.util.Arrays;
18+
import java.util.concurrent.CompletableFuture;
19+
import java.util.concurrent.TimeUnit;
20+
21+
import org.eclipse.jdt.core.dom.ASTVisitor;
22+
import org.eclipse.jdt.core.dom.IMethodBinding;
23+
import org.eclipse.jdt.core.dom.MethodDeclaration;
24+
import org.eclipse.lsp4j.TextDocumentIdentifier;
25+
import org.junit.jupiter.api.BeforeEach;
26+
import org.junit.jupiter.api.Test;
27+
import org.junit.jupiter.api.extension.ExtendWith;
28+
import org.springframework.beans.factory.annotation.Autowired;
29+
import org.springframework.context.annotation.Import;
30+
import org.springframework.ide.vscode.boot.app.SpringSymbolIndex;
31+
import org.springframework.ide.vscode.boot.bootiful.BootLanguageServerTest;
32+
import org.springframework.ide.vscode.boot.bootiful.SymbolProviderTestConf;
33+
import org.springframework.ide.vscode.boot.java.data.DataRepositoryAotMetadata;
34+
import org.springframework.ide.vscode.boot.java.data.DataRepositoryAotMetadataMethod;
35+
import org.springframework.ide.vscode.boot.java.data.DataRepositoryAotMetadataService;
36+
import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache;
37+
import org.springframework.ide.vscode.commons.java.IJavaProject;
38+
import org.springframework.ide.vscode.commons.java.parser.JLRMethodParser;
39+
import org.springframework.ide.vscode.commons.java.parser.JLRMethodParser.JLRMethod;
40+
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
41+
import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness;
42+
import org.springframework.ide.vscode.project.harness.ProjectsHarness;
43+
import org.springframework.test.context.junit.jupiter.SpringExtension;
44+
45+
@ExtendWith(SpringExtension.class)
46+
@BootLanguageServerTest
47+
@Import(SymbolProviderTestConf.class)
48+
public class DataRepositoryAotMetadataServiceTest {
49+
50+
@Autowired private BootLanguageServerHarness harness;
51+
@Autowired private JavaProjectFinder projectFinder;
52+
@Autowired private SpringSymbolIndex indexer;
53+
@Autowired private CompilationUnitCache cuCache;
54+
55+
private IJavaProject testProject;
56+
57+
@BeforeEach
58+
public void setup() throws Exception {
59+
testProject = ProjectsHarness.INSTANCE.mavenProject("aot-data-repositories-jpa");
60+
61+
harness.useProject(testProject);
62+
harness.intialize(null);
63+
64+
// trigger project creation
65+
projectFinder.find(new TextDocumentIdentifier(testProject.getLocationUri().toASCIIString())).get();
66+
67+
CompletableFuture<Void> initProject = indexer.waitOperation();
68+
initProject.get(5, TimeUnit.SECONDS);
69+
}
70+
71+
@Test
72+
void testBasicRepositoryAotMetadataLookuo() throws Exception {
73+
DataRepositoryAotMetadataService service = new DataRepositoryAotMetadataService();
74+
75+
DataRepositoryAotMetadata metadata = service.getRepositoryMetadata(testProject, "example.springdata.aot.UserRepository");
76+
assertNotNull(metadata);
77+
assertEquals("example.springdata.aot.UserRepository", metadata.name());
78+
assertEquals("JPA", metadata.module());
79+
}
80+
81+
@Test
82+
void testRepositoryMethodsIntMetadata() throws Exception {
83+
DataRepositoryAotMetadataService service = new DataRepositoryAotMetadataService();
84+
85+
DataRepositoryAotMetadata metadata = service.getRepositoryMetadata(testProject, "example.springdata.aot.UserRepository");
86+
DataRepositoryAotMetadataMethod[] methods = metadata.methods();
87+
88+
assertEquals(32, methods.length);
89+
90+
DataRepositoryAotMetadataMethod methodMetadata = Arrays.stream(methods).filter(method -> method.name().equals("countUsersByLastnameLike")).findFirst().get();
91+
assertEquals("countUsersByLastnameLike", methodMetadata.name());
92+
assertEquals("public abstract java.lang.Long example.springdata.aot.UserRepository.countUsersByLastnameLike(java.lang.String)", methodMetadata.signature());
93+
94+
JLRMethod parsedMethodSignature = JLRMethodParser.parse(methodMetadata.signature());
95+
assertEquals("example.springdata.aot.UserRepository", parsedMethodSignature.getFQClassName());
96+
assertEquals("java.lang.Long", parsedMethodSignature.getReturnType());
97+
assertEquals("countUsersByLastnameLike", parsedMethodSignature.getMethodName());
98+
99+
String[] parameters = parsedMethodSignature.getParameters();
100+
assertEquals("java.lang.String", parameters[0]);
101+
assertEquals(1, parameters.length);
102+
}
103+
104+
@Test
105+
void testRepositoryMethodsMatching() throws Exception {
106+
DataRepositoryAotMetadataService service = new DataRepositoryAotMetadataService();
107+
DataRepositoryAotMetadata metadata = service.getRepositoryMetadata(testProject, "example.springdata.aot.UserRepository");
108+
109+
URI docUri = testProject.getLocationUri().resolve("src/main/java/example/springdata/aot/UserRepository.java");
110+
cuCache.withCompilationUnit(testProject, docUri, cu -> {
111+
cu.accept(new ASTVisitor() {
112+
public boolean visit(MethodDeclaration node) {
113+
IMethodBinding binding = node.resolveBinding();
114+
115+
DataRepositoryAotMetadataMethod method = service.findMethod(metadata, binding);
116+
assertNotNull(method);
117+
118+
if (method.name().equals("findUserByLastnameStartingWith") && binding.getParameterTypes().length == 1) {
119+
assertEquals("public abstract java.util.List<example.springdata.aot.User> example.springdata.aot.UserRepository.findUserByLastnameStartingWith(java.lang.String)", method.signature());
120+
}
121+
else if (method.name().equals("findUserByLastnameStartingWith") && binding.getParameterTypes().length == 2) {
122+
assertEquals("public abstract org.springframework.data.domain.Page<example.springdata.aot.User> example.springdata.aot.UserRepository.findUserByLastnameStartingWith(java.lang.String,org.springframework.data.domain.Pageable)", method.signature());
123+
}
124+
125+
return true;
126+
}
127+
});
128+
129+
return null;
130+
});
131+
132+
}
133+
134+
}

0 commit comments

Comments
 (0)