From e586ebd82a8009e99c8b6e3ab09744afed679e18 Mon Sep 17 00:00:00 2001 From: Martin Lippert Date: Wed, 5 Mar 2025 10:03:58 +0100 Subject: [PATCH] GH-1491: minor refactorings and preparing case for query methods from type hierarchy --- .../data/DataRepositorySymbolProvider.java | 36 ++++++++++++++----- .../test/DataRepositoryIndexElementsTest.java | 32 ++++++++++++++++- ...ustomerRepositoryWithParentInterfaces.java | 11 ++++++ 3 files changed, 70 insertions(+), 9 deletions(-) create mode 100644 headless-services/spring-boot-language-server/src/test/resources/test-projects/test-spring-data-symbols/src/main/java/org/test/CustomerRepositoryWithParentInterfaces.java diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/DataRepositorySymbolProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/DataRepositorySymbolProvider.java index abf3317e9..551aa179b 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/DataRepositorySymbolProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/DataRepositorySymbolProvider.java @@ -10,17 +10,21 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.java.data; +import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; +import java.util.List; import java.util.Set; import org.eclipse.jdt.core.dom.Annotation; +import org.eclipse.jdt.core.dom.IMethodBinding; import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.Modifier; import org.eclipse.jdt.core.dom.NormalAnnotation; import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.dom.SingleMemberAnnotation; +import org.eclipse.jdt.core.dom.Type; import org.eclipse.jdt.core.dom.TypeDeclaration; import org.eclipse.lsp4j.Location; import org.eclipse.lsp4j.Range; @@ -99,14 +103,14 @@ public class DataRepositorySymbolProvider implements SymbolProvider { } private void indexQueryMethods(Bean beanDefinition, TypeDeclaration typeDeclaration, SpringIndexerJavaContext context, TextDocument doc) { - MethodDeclaration[] methods = typeDeclaration.getMethods(); - if (methods == null) return; + AnnotationHierarchies annotationHierarchies = AnnotationHierarchies.get(typeDeclaration); + + List methods = identifyQueryMethods(typeDeclaration, annotationHierarchies); for (MethodDeclaration method : methods) { - int modifiers = method.getModifiers(); SimpleName nameNode = method.getName(); - if (nameNode != null && (modifiers & Modifier.DEFAULT) == 0) { + if (nameNode != null) { String methodName = nameNode.getFullyQualifiedName(); DocumentRegion nodeRegion = ASTUtils.nodeRegion(doc, method); @@ -114,7 +118,7 @@ public class DataRepositorySymbolProvider implements SymbolProvider { Range range = doc.toRange(nodeRegion); if (methodName != null) { - String queryString = identifyQueryString(method); + String queryString = identifyQueryString(method, annotationHierarchies); beanDefinition.addChild(new QueryMethodIndexElement(methodName, queryString, range)); } @@ -125,8 +129,24 @@ public class DataRepositorySymbolProvider implements SymbolProvider { } } - private String identifyQueryString(MethodDeclaration method) { - AnnotationHierarchies annotationHierarchies = AnnotationHierarchies.get(method); + private List identifyQueryMethods(TypeDeclaration type, AnnotationHierarchies annotationHierarchies) { + List result = new ArrayList<>(); + + MethodDeclaration[] methods = type.getMethods(); + if (methods == null) return result; + + for (MethodDeclaration method : methods) { + int modifiers = method.getModifiers(); + + if ((modifiers & Modifier.DEFAULT) == 0) { + result.add(method); + } + } + + return result; + } + + private String identifyQueryString(MethodDeclaration method, AnnotationHierarchies annotationHierarchies) { EmbeddedQueryExpression queryExpression = null; @@ -150,7 +170,7 @@ public class DataRepositorySymbolProvider implements SymbolProvider { return null; } - + protected String beanLabel(boolean isFunctionBean, String beanName, String beanType, String markerString) { StringBuilder symbolLabel = new StringBuilder(); symbolLabel.append("@+"); diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/data/test/DataRepositoryIndexElementsTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/data/test/DataRepositoryIndexElementsTest.java index 965a782ad..7e78bbd8e 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/data/test/DataRepositoryIndexElementsTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/data/test/DataRepositoryIndexElementsTest.java @@ -10,6 +10,7 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.java.data.test; +import static org.junit.Assert.assertNull; import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.File; @@ -20,6 +21,7 @@ import java.util.concurrent.TimeUnit; import org.apache.commons.lang3.ArrayUtils; import org.eclipse.lsp4j.TextDocumentIdentifier; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; @@ -82,7 +84,7 @@ public class DataRepositoryIndexElementsTest { assertEquals("org.test.CustomerRepository", repoBean[0].getType()); Bean[] matchingBeans = springIndex.getMatchingBeans("test-spring-data-symbols", "org.springframework.data.repository.CrudRepository"); - assertEquals(3, matchingBeans.length); + assertEquals(4, matchingBeans.length); ArrayUtils.contains(matchingBeans, repoBean[0]); } @@ -115,7 +117,35 @@ public class DataRepositoryIndexElementsTest { QueryMethodIndexElement queryMethod = (QueryMethodIndexElement) queryMethods.get(0); assertEquals("findPetTypes", queryMethod.getMethodName()); assertEquals("SELECT ptype FROM PetType ptype ORDER BY ptype.name", queryMethod.getQueryString()); + } + + @Test + void testNoRepositoryBeanAnnotationResultsInNoBeanIndexElement() throws Exception { + String docUri = directory.toPath().resolve("src/main/java/org/test/CustomerRepositoryParentInterface.java").toUri().toString(); + DocumentElement document = springIndex.getDocument(docUri); + assertNull(document); // nothing in the doc, therefore not even the doc node is around + } + + @Test + @Disabled // query methods from superclasses or interfaces not yet implemented, maybe requires a different way to think about this (separate index elements instead of one element with all query methods) + void testQueryMethodsFromParentInterfaces() throws Exception { + String docUri = directory.toPath().resolve("src/main/java/org/test/CustomerRepositoryWithParentInterfaces.java").toUri().toString(); + + DocumentElement document = springIndex.getDocument(docUri); + List children = document.getChildren(); + Bean repositoryElement = (Bean) children.get(0); + + List queryMethods = repositoryElement.getChildren(); + assertEquals(2, queryMethods.size()); + + QueryMethodIndexElement queryMethod = (QueryMethodIndexElement) queryMethods.get(0); + assertEquals("findConcretePetTypes", queryMethod.getMethodName()); + assertEquals("CONCRETE REPO SELECT STATEMENT", queryMethod.getQueryString()); + + QueryMethodIndexElement parentQueryMethod = (QueryMethodIndexElement) queryMethods.get(1); + assertEquals("findParentPetTypes", parentQueryMethod.getMethodName()); + assertEquals("PARENT REPO INTERFACE QUERY STATEMENT", parentQueryMethod.getQueryString()); } } diff --git a/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-spring-data-symbols/src/main/java/org/test/CustomerRepositoryWithParentInterfaces.java b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-spring-data-symbols/src/main/java/org/test/CustomerRepositoryWithParentInterfaces.java new file mode 100644 index 000000000..a90035911 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-spring-data-symbols/src/main/java/org/test/CustomerRepositoryWithParentInterfaces.java @@ -0,0 +1,11 @@ +package org.test; + +import java.util.List; + +import org.springframework.data.jpa.repository.Query; + +public interface CustomerRepositoryWithParentInterfaces extends CustomerRepositoryParentInterface { + + @Query("CONCRETE REPO SELECT STATEMENT") + List findConcretePetTypes(); +}