diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/DataRepositoryCompletionProcessor.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/DataRepositoryCompletionProcessor.java index cd8a49d1f..1fc08a33f 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/DataRepositoryCompletionProcessor.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/DataRepositoryCompletionProcessor.java @@ -23,6 +23,7 @@ import org.springframework.ide.vscode.boot.java.utils.ASTUtils; import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits; import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal; import org.springframework.ide.vscode.commons.util.text.IDocument; +import org.springframework.util.StringUtils; /** * @author Martin Lippert @@ -39,18 +40,45 @@ public class DataRepositoryCompletionProcessor implements CompletionProvider { TypeDeclaration type = ASTUtils.findDeclaringType(node); DataRepositoryDefinition repo = getDataRepositoryDefinition(type); if (repo != null) { - DocumentEdits edits = new DocumentEdits(null); - edits.insert(offset, "List findByLastName${1|(String lastName);,And,Or|}"); - - DocumentEdits additionalEdits = new DocumentEdits(null); -// additionalEdits.insert(offset, "(String lastName);"); - - completions.add(new FindByCompletionProposal("findByLastName(String lastName);", CompletionItemKind.Method, edits, null, null, Optional.of(additionalEdits))); - - System.out.println("data completion proposal calculation for: " + node.toString()); + DomainType domainType = repo.getDomainType(); + if (domainType != null) { + DomainProperty[] properties = domainType.getProperties(); + for (DomainProperty property : properties) { + completions.add(generateCompletionProposal(offset, repo, property)); + } + } } } + protected ICompletionProposal generateCompletionProposal(int offset, DataRepositoryDefinition repoDef, DomainProperty domainProperty) { + StringBuilder label = new StringBuilder(); + label.append("findBy"); + label.append(StringUtils.capitalize(domainProperty.getName())); + label.append("("); + label.append(domainProperty.getType().getSimpleName()); + label.append(" "); + label.append(StringUtils.uncapitalize(domainProperty.getName())); + label.append(");"); + + DocumentEdits edits = new DocumentEdits(null); + + StringBuilder completion = new StringBuilder(); + completion.append("List<"); + completion.append(repoDef.getDomainType().getSimpleName()); + completion.append("> findBy"); + completion.append(StringUtils.capitalize(domainProperty.getName())); + completion.append("("); + completion.append(domainProperty.getType().getSimpleName()); + completion.append(" "); + completion.append(StringUtils.uncapitalize(domainProperty.getName())); + completion.append(");"); + edits.insert(offset, completion.toString()); + + DocumentEdits additionalEdits = new DocumentEdits(null); + + return new FindByCompletionProposal(label.toString(), CompletionItemKind.Method, edits, null, null, Optional.of(additionalEdits)); + } + private DataRepositoryDefinition getDataRepositoryDefinition(TypeDeclaration type) { if (type != null) { ITypeBinding resolvedType = type.resolveBinding(); @@ -75,7 +103,14 @@ public class DataRepositoryCompletionProcessor implements CompletionProvider { } if (Constants.REPOSITORY_TYPE.equals(simplifiedType)) { - return new DataRepositoryDefinition(); + DomainType domainType = null; + if (resolvedInterface.isParameterizedType()) { + ITypeBinding[] typeParameters = resolvedInterface.getTypeArguments(); + if (typeParameters != null && typeParameters.length > 0) { + domainType = new DomainType(typeParameters[0]); + } + } + return createDataRepositoryDefinitionFromType(domainType); } else { DataRepositoryDefinition repo = getDataRepositoryDefinition(type, resolvedInterface); @@ -93,4 +128,8 @@ public class DataRepositoryCompletionProcessor implements CompletionProvider { } return null; } + + private DataRepositoryDefinition createDataRepositoryDefinitionFromType(DomainType domainType) { + return new DataRepositoryDefinition(domainType); + } } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/DataRepositoryDefinition.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/DataRepositoryDefinition.java index 8f2a2a442..243518032 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/DataRepositoryDefinition.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/DataRepositoryDefinition.java @@ -10,6 +10,20 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.java.data; +/** + * @author Martin Lippert + */ public class DataRepositoryDefinition { + private final DomainType domainType; + + public DataRepositoryDefinition(DomainType domainType) { + super(); + this.domainType = domainType; + } + + public DomainType getDomainType() { + return domainType; + } + } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/DomainProperty.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/DomainProperty.java new file mode 100644 index 000000000..904ac40c4 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/DomainProperty.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (c) 2018 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 + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.java.data; + +/** + * @author Martin Lippert + */ +public class DomainProperty { + + private final String name; + private final DomainType type; + + public DomainProperty(String name, DomainType type) { + this.name = name; + this.type = type; + } + + public String getName() { + return name; + } + + public DomainType getType() { + return type; + } + +} 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 new file mode 100644 index 000000000..53f99f007 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/DomainType.java @@ -0,0 +1,76 @@ +/******************************************************************************* + * Copyright (c) 2018 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 + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.java.data; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jdt.core.dom.IMethodBinding; +import org.eclipse.jdt.core.dom.ITypeBinding; + +/** + * @author Martin Lippert + */ +public class DomainType { + + private final String packageName; + private final String fullName; + private final String simpleName; + private DomainProperty[] properties; + + public DomainType(String packageName, String fullName, String simpleName) { + this.packageName = packageName; + this.fullName = fullName; + this.simpleName = simpleName; + } + + public DomainType(ITypeBinding typeBinding) { + this.packageName = typeBinding.getPackage().getName(); + this.fullName = typeBinding.getQualifiedName(); + this.simpleName = typeBinding.getName(); + + 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 && methodName.startsWith("get")) { + String propertyName = methodName.substring(3); + properties.add(new DomainProperty(propertyName, new DomainType(method.getReturnType()))); + } + } + this.properties = (DomainProperty[]) properties.toArray(new DomainProperty[properties.size()]); + } + else { + this.properties = new DomainProperty[0]; + } + } + } + + public String getPackageName() { + return packageName; + } + + public String getFullName() { + return fullName; + } + + public String getSimpleName() { + return simpleName; + } + + public DomainProperty[] getProperties() { + return properties; + } + +} diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/data/test/DataRepositoryCompletionProcessorTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/data/test/DataRepositoryCompletionProcessorTest.java index c622133e7..3539fc9e1 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/data/test/DataRepositoryCompletionProcessorTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/data/test/DataRepositoryCompletionProcessorTest.java @@ -54,7 +54,8 @@ public class DataRepositoryCompletionProcessorTest { public void testStandardFindByCompletions() throws Exception { prepareCase("{", "{<*>"); assertContainsAnnotationCompletions( - "List findByLastName${1|(String lastName);,And,Or|}"); + "List findByFirstName(String firstName);", + "List findByLastName(String lastName);"); } private void prepareCase(String selectedAnnotation, String annotationStatementBeforeTest) throws Exception {