From 84ab82651f0672bd4a02dfdbc9e37f9f04ef71c6 Mon Sep 17 00:00:00 2001 From: aboyko Date: Fri, 24 Mar 2023 15:39:26 -0400 Subject: [PATCH] [spring-data-support] Account for type hierarchy of domain types --- .../ide/vscode/boot/java/data/DomainType.java | 69 +++++++++++++------ ...aRepositoryStandardCompletionProvider.java | 3 +- ...toryPrefixSensitiveCompletionProvider.java | 11 +-- .../src/main/java/org/test/Customer.java | 19 ++--- .../src/main/java/org/test/Employee.java | 16 +---- .../src/main/java/org/test/Person.java | 25 +++++++ 6 files changed, 82 insertions(+), 61 deletions(-) create mode 100644 headless-services/spring-boot-language-server/src/test/resources/test-projects/test-spring-data-symbols/src/main/java/org/test/Person.java diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/DomainType.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/DomainType.java index 7c88481a7..c06841832 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/DomainType.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/DomainType.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2018 Pivotal, Inc. + * Copyright (c) 2018, 2023 Pivotal, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -11,7 +11,10 @@ package org.springframework.ide.vscode.boot.java.data; import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import org.eclipse.jdt.core.dom.IMethodBinding; import org.eclipse.jdt.core.dom.ITypeBinding; @@ -45,31 +48,47 @@ public class DomainType { this.simpleName = typeBinding.getName(); this.properties = Suppliers.memoize(() -> { - if (!this.packageName.startsWith("java")) { - IMethodBinding[] methods = typeBinding.getDeclaredMethods(); - if (methods != null && methods.length > 0) { - List properties = new ArrayList<>(); + List domainProps = calculateDomainProperties(typeBinding); + return domainProps.toArray(new DomainProperty[domainProps.size()]); + }); + } + + private List calculateDomainProperties(ITypeBinding typeBinding) { + if (!this.packageName.startsWith("java")) { + IMethodBinding[] methods = typeBinding.getDeclaredMethods(); + if (methods != null && methods.length > 0) { + List properties = new ArrayList<>(); - for (IMethodBinding method : methods) { - String methodName = method.getName(); - if (methodName != null) { - String propertyName = null; - if (methodName.startsWith("get")) { - propertyName = methodName.substring(3); - } - else if (methodName.startsWith("is")) { - propertyName = methodName.substring(2); - } - if (propertyName != null) { - properties.add(new DomainProperty(propertyName, new DomainType(method.getReturnType()))); - } + for (IMethodBinding method : methods) { + String methodName = method.getName(); + if (methodName != null) { + String propertyName = null; + if (methodName.startsWith("get")) { + propertyName = methodName.substring(3); + } + else if (methodName.startsWith("is")) { + propertyName = methodName.substring(2); + } + if (propertyName != null) { + properties.add(new DomainProperty(propertyName, new DomainType(method.getReturnType()))); } } - return (DomainProperty[]) properties.toArray(new DomainProperty[properties.size()]); } + + if (typeBinding.getSuperclass() != null) { + properties.addAll(calculateDomainProperties(typeBinding.getSuperclass())); + } + + if (typeBinding.getInterfaces() != null) { + for (ITypeBinding si : typeBinding.getInterfaces()) { + properties.addAll(calculateDomainProperties(si)); + } + } + + return properties; } - return new DomainProperty[0]; - }); + } + return Collections.emptyList(); } public String getPackageName() { @@ -87,5 +106,13 @@ public class DomainType { public DomainProperty[] getProperties() { return properties.get(); } + + public Map getPropertiesByName() { + Map propertiesByName = new LinkedHashMap<>(); + for(DomainProperty prop : properties.get()){ + propertiesByName.put(prop.getName(), prop); + } + return propertiesByName; + } } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/providers/DataRepositoryStandardCompletionProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/providers/DataRepositoryStandardCompletionProvider.java index a3e6b45e3..26321cc8d 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/providers/DataRepositoryStandardCompletionProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/providers/DataRepositoryStandardCompletionProvider.java @@ -29,8 +29,7 @@ public class DataRepositoryStandardCompletionProvider implements DataRepositoryC public void addProposals(Collection completions, IDocument doc, int offset, String prefix, DataRepositoryDefinition repo) { DomainType domainType = repo.getDomainType(); - DomainProperty[] properties = domainType.getProperties(); - for (DomainProperty property : properties) { + for (DomainProperty property : domainType.getPropertiesByName().values()) { completions.add(generateCompletionProposal(offset, prefix, repo, property)); } } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/providers/prefixsensitive/DataRepositoryPrefixSensitiveCompletionProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/providers/prefixsensitive/DataRepositoryPrefixSensitiveCompletionProvider.java index 335b4f704..7ea0fdec2 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/providers/prefixsensitive/DataRepositoryPrefixSensitiveCompletionProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/providers/prefixsensitive/DataRepositoryPrefixSensitiveCompletionProvider.java @@ -11,7 +11,6 @@ package org.springframework.ide.vscode.boot.java.data.providers.prefixsensitive; import java.util.Collection; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; @@ -41,7 +40,7 @@ public class DataRepositoryPrefixSensitiveCompletionProvider implements DataRepo } DataRepositoryMethodNameParseResult parseResult = new DataRepositoryMethodParser(localPrefix, repoDef).parseLocalPrefixForCompletion(); if(parseResult != null && parseResult.performFullCompletion()){ - Map propertiesByName = getPropertiesByName(repoDef.getDomainType().getProperties()); + Map propertiesByName = repoDef.getDomainType().getPropertiesByName(); if (parseResult.lastWord() != null || !localPrefix.endsWith("By")) { addMethodCompletionProposal(completions, offset, repoDef, localPrefix, prefix, parseResult, propertiesByName); } @@ -133,14 +132,6 @@ public class DataRepositoryPrefixSensitiveCompletionProvider implements DataRepo return replaceStart; } - private Map getPropertiesByName(DomainProperty[] properties) { - Map propertiesByName = new HashMap<>(); - for(DomainProperty prop : properties){ - propertiesByName.put(prop.getName(), prop); - } - return propertiesByName; - } - private String buildSignature(String methodName, Map properties, DataRepositoryMethodNameParseResult parseResult) { StringBuilder signatureBuilder = new StringBuilder(); signatureBuilder.append(methodName); diff --git a/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-spring-data-symbols/src/main/java/org/test/Customer.java b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-spring-data-symbols/src/main/java/org/test/Customer.java index 92d59745d..9a675ac7c 100644 --- a/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-spring-data-symbols/src/main/java/org/test/Customer.java +++ b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-spring-data-symbols/src/main/java/org/test/Customer.java @@ -8,23 +8,22 @@ import javax.persistence.Id; import javax.persistence.ManyToOne; @Entity -public class Customer { +public class Customer extends Person { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; - private String firstName; - private String lastName; private boolean thisCustomerIsSpecial;//contains keyword in name @ManyToOne private Employee responsibleEmployee; - protected Customer() {} + protected Customer() { + super(); + } public Customer(String firstName, String lastName, Employee responsibleEmployee) { - this.firstName = firstName; - this.lastName = lastName; + super(firstName, lastName); this.responsibleEmployee = responsibleEmployee; } @@ -40,14 +39,6 @@ public class Customer { return id; } - public String getFirstName() { - return firstName; - } - - public String getLastName() { - return lastName; - } - public boolean isThisCustomerIsSpecial() { return thisCustomerIsSpecial; } diff --git a/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-spring-data-symbols/src/main/java/org/test/Employee.java b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-spring-data-symbols/src/main/java/org/test/Employee.java index 9e6ceb8af..46376d384 100644 --- a/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-spring-data-symbols/src/main/java/org/test/Employee.java +++ b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-spring-data-symbols/src/main/java/org/test/Employee.java @@ -2,25 +2,20 @@ package org.test; import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; import javax.persistence.Id; @Entity -public class Employee { +public class Employee extends Person { @Id private Long socialSecurityNumber; - private String firstName; - private String lastName; protected Employee() { } public Employee(long socialSecurityNumber, String firstName, String lastName) { + super(firstName, lastName); this.socialSecurityNumber = socialSecurityNumber; - this.firstName = firstName; - this.lastName = lastName; } @Override @@ -37,11 +32,4 @@ public class Employee { return socialSecurityNumber; } - public String getFirstName() { - return firstName; - } - - public String getLastName() { - return lastName; - } } diff --git a/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-spring-data-symbols/src/main/java/org/test/Person.java b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-spring-data-symbols/src/main/java/org/test/Person.java new file mode 100644 index 000000000..9778c4a78 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-spring-data-symbols/src/main/java/org/test/Person.java @@ -0,0 +1,25 @@ +package org.test; + +public class Person { + + protected String firstName; + protected String lastName; + + protected Person() { + + } + + public Person(String firstName, String lastName) { + this.firstName = firstName; + this.lastName = lastName; + } + + public String getFirstName() { + return firstName; + } + + public String getLastName() { + return lastName; + } + +}