From 61354e097a1b33dea752c3b64889d0f50b3d3b15 Mon Sep 17 00:00:00 2001 From: aboyko Date: Wed, 12 Mar 2025 10:59:55 -0400 Subject: [PATCH] Constructor generated --- .../ls/eclipse/commons/InjectBean.java | 159 ++++++++++++++---- .../commons/STS4LanguageClientImpl.java | 2 +- .../protocol/java/InjectBeanParams.java | 2 +- .../java/beans/BeanCompletionProposal.java | 3 +- 4 files changed, 133 insertions(+), 33 deletions(-) diff --git a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/InjectBean.java b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/InjectBean.java index 25662823c..b82842ada 100644 --- a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/InjectBean.java +++ b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/InjectBean.java @@ -1,39 +1,46 @@ package org.springframework.tooling.ls.eclipse.commons; import java.net.URI; +import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.stream.Stream; import org.eclipse.core.runtime.Assert; import org.eclipse.jdt.core.ICompilationUnit; -import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; +import org.eclipse.jdt.core.dom.Assignment; +import org.eclipse.jdt.core.dom.Block; +import org.eclipse.jdt.core.dom.ChildListPropertyDescriptor; import org.eclipse.jdt.core.dom.CompilationUnit; +import org.eclipse.jdt.core.dom.FieldAccess; import org.eclipse.jdt.core.dom.FieldDeclaration; +import org.eclipse.jdt.core.dom.IMethodBinding; +import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.Modifier; +import org.eclipse.jdt.core.dom.SingleVariableDeclaration; +import org.eclipse.jdt.core.dom.TypeDeclaration; import org.eclipse.jdt.core.dom.VariableDeclarationFragment; import org.eclipse.jdt.core.dom.rewrite.ImportRewrite; -import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.ImportRewriteContext; import org.eclipse.jdt.core.dom.rewrite.ListRewrite; import org.eclipse.jdt.core.refactoring.CompilationUnitChange; -import org.eclipse.jdt.internal.corext.codemanipulation.ContextSensitiveImportRewriteContext; import org.eclipse.jdt.internal.corext.dom.IASTSharedValues; import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages; import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite; import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringASTParser; import org.eclipse.jdt.internal.ui.JavaPlugin; +import org.eclipse.jdt.internal.ui.text.java.MethodDeclarationCompletionProposal; import org.eclipse.lsp4e.LSPEclipseUtils; import org.eclipse.lsp4j.Position; import org.eclipse.lsp4j.Range; -import org.eclipse.lsp4j.ResourceOperation; import org.eclipse.lsp4j.TextDocumentEdit; import org.eclipse.lsp4j.VersionedTextDocumentIdentifier; -import org.eclipse.lsp4j.jsonrpc.messages.Either; import org.eclipse.text.edits.DeleteEdit; import org.eclipse.text.edits.InsertEdit; import org.eclipse.text.edits.ReplaceEdit; @@ -44,15 +51,129 @@ import org.eclipse.ui.PartInitException; import org.springframework.tooling.jdt.ls.commons.Logger; import org.springframework.tooling.jdt.ls.commons.resources.ResourceUtils; +@SuppressWarnings({ "restriction", "unchecked" }) public final class InjectBean { private final Logger logger; + private String fieldTypeDeclarationName = null; + public InjectBean(Logger logger) { this.logger = logger; } - public TextDocumentEdit computeEdits(String docUri, String fieldType, String fieldName) { + private void createFieldDeclaration(IJavaProject project, CompilationUnitRewrite cuRewrite, CompilationUnit domCu, + String typeName, String fieldType, String fieldName) throws JavaModelException { + AST ast= cuRewrite.getAST(); + VariableDeclarationFragment variableDeclarationFragment= ast.newVariableDeclarationFragment(); + variableDeclarationFragment.setName(ast.newSimpleName(fieldName)); + + FieldDeclaration fieldDeclaration= ast.newFieldDeclaration(variableDeclarationFragment); + + fieldDeclaration.modifiers().add(ast.newModifier(Modifier.ModifierKeyword.PRIVATE_KEYWORD)); + fieldDeclaration.modifiers().add(ast.newModifier(Modifier.ModifierKeyword.FINAL_KEYWORD)); + + IType itype = project.findType(fieldType); + String fqn = itype.getFullyQualifiedName(); + ImportRewrite importRewrite= cuRewrite.getImportRewrite(); + fieldTypeDeclarationName = importRewrite.addImport(fqn); + fieldDeclaration.setType(ast.newSimpleType(ast.newName(fieldTypeDeclarationName))); + + AbstractTypeDeclaration parent = ((Stream) domCu.types().stream() + .filter(AbstractTypeDeclaration.class::isInstance) + .map(AbstractTypeDeclaration.class::cast)) + .filter(td -> typeName.equals(td.getName().getIdentifier())) + .findFirst() + .orElse(null); + Assert.isNotNull(parent); + ListRewrite listRewrite= cuRewrite.getASTRewrite().getListRewrite(parent, parent.getBodyDeclarationsProperty()); + TextEditGroup msg= cuRewrite.createGroupDescription(RefactoringCoreMessages.ExtractConstantRefactoring_declare_constant); + listRewrite.insertFirst(fieldDeclaration, msg); + } + + private void maybeAddConstructor(IJavaProject project, CompilationUnitRewrite cuRewrite, CompilationUnit domCu, + String typeName, String fieldType, String fieldName) { + TypeDeclaration typeDom = (TypeDeclaration) ((Stream) domCu.types().stream() + .filter(TypeDeclaration.class::isInstance) + .map(TypeDeclaration.class::cast)) + .filter(td -> typeName.equals(td.getName().getIdentifier())) + .findFirst() + .orElse(null); + + MethodDeclaration constructor = null; + boolean parameterAdded = false; + for (MethodDeclaration m : typeDom.getMethods()) { + if (m.isConstructor()) { + IMethodBinding methodBinding = m.resolveBinding(); + if (methodBinding != null) { + boolean autowired = Arrays.stream(methodBinding.getAnnotations()).anyMatch(a -> "org.springframework.beans.factory.annotation.Autowired".equals(a.getAnnotationType().getQualifiedName())); + boolean hasParameter = Arrays.stream(methodBinding.getParameterTypes()).anyMatch(t -> fieldType.equals(t.getQualifiedName())); + if (autowired) { + constructor = m; + parameterAdded = hasParameter; + break; + } else { + if (constructor == null && !parameterAdded) { + constructor = m; + parameterAdded = hasParameter; + } + } + } + } + } + + if (constructor == null) { + AST ast = domCu.getAST(); + + MethodDeclaration newConstructor = ast.newMethodDeclaration(); + newConstructor.setConstructor(true); + newConstructor.setName(ast.newSimpleName(typeName)); + newConstructor.parameters().add(createVariableDeclaration(ast, fieldName)); + newConstructor.modifiers().add(ast.newModifier(Modifier.ModifierKeyword.PUBLIC_KEYWORD)); + Block block = ast.newBlock(); + block.statements().add(ast.newExpressionStatement(createAssignment(ast, fieldName))); + newConstructor.setBody(block); + + ListRewrite listRewrite= cuRewrite.getASTRewrite().getListRewrite(typeDom, TypeDeclaration.BODY_DECLARATIONS_PROPERTY); + TextEditGroup msg= cuRewrite.createGroupDescription(RefactoringCoreMessages.ExtractConstantRefactoring_declare_constant); + if (typeDom.getMethods().length == 0) { + listRewrite.insertLast(newConstructor, msg); + } else { + listRewrite.insertBefore(newConstructor, typeDom.getMethods()[0], msg); + } + } else { + AST ast = constructor.getAST(); + SingleVariableDeclaration newParam = createVariableDeclaration(ast, fieldName); + + ListRewrite listRewrite= cuRewrite.getASTRewrite().getListRewrite(constructor, MethodDeclaration.PARAMETERS_PROPERTY); + TextEditGroup msg= cuRewrite.createGroupDescription(RefactoringCoreMessages.ExtractConstantRefactoring_declare_constant); + List parameters = constructor.parameters(); + listRewrite.insertAfter(newParam, parameters.get(parameters.size() - 1), msg); + + Block block = constructor.getBody(); + listRewrite = cuRewrite.getASTRewrite().getListRewrite(block, Block.STATEMENTS_PROPERTY); + listRewrite.insertLast(ast.newExpressionStatement(createAssignment(ast, fieldName)), msg); + } + } + + private SingleVariableDeclaration createVariableDeclaration(AST ast, String fieldName) { + SingleVariableDeclaration newParam = ast.newSingleVariableDeclaration(); + newParam.setName(ast.newSimpleName(fieldName)); + newParam.setType(ast.newSimpleType(ast.newSimpleName(fieldTypeDeclarationName))); + return newParam; + } + + private Assignment createAssignment(AST ast, String fieldName) { + Assignment assign = ast.newAssignment(); + assign.setRightHandSide(ast.newSimpleName(fieldName)); + FieldAccess thisField = ast.newFieldAccess(); + thisField.setName(ast.newSimpleName(fieldName)); + thisField.setExpression(ast.newThisExpression()); + assign.setLeftHandSide(thisField); + return assign; + } + + public TextDocumentEdit computeEdits(String docUri, String typeName, String fieldType, String fieldName) { try { URI resourceUri = URI.create(docUri); IJavaProject project = ResourceUtils.getJavaProject(resourceUri); @@ -73,31 +194,9 @@ public final class InjectBean { CompilationUnitRewrite cuRewrite = new CompilationUnitRewrite(null, cu, domCu, Map.of()); - AST ast= cuRewrite.getAST(); - VariableDeclarationFragment variableDeclarationFragment= ast.newVariableDeclarationFragment(); - variableDeclarationFragment.setName(ast.newSimpleName(fieldName)); + createFieldDeclaration(project, cuRewrite, domCu, typeName, fieldType, fieldName); - FieldDeclaration fieldDeclaration= ast.newFieldDeclaration(variableDeclarationFragment); - - fieldDeclaration.modifiers().add(ast.newModifier(Modifier.ModifierKeyword.PRIVATE_KEYWORD)); - fieldDeclaration.modifiers().add(ast.newModifier(Modifier.ModifierKeyword.FINAL_KEYWORD)); - - IJavaElement el = project.findElement("L" + fieldType.replace(".", "/") + ";", null); - IType itype = project.findType(fieldType); - String packageName = itype.getPackageFragment().getElementName(); - String fqn = itype.getFullyQualifiedName(); -// org.eclipse.jdt.core.dom.ITypeBinding typeBinding= Bindings.normalizeForDeclarationUse(tb, ast); - ImportRewrite importRewrite= cuRewrite.getImportRewrite(); - ImportRewriteContext context= new ContextSensitiveImportRewriteContext(cuRewrite.getRoot(), 0, importRewrite); -// Type t = importRewrite.addImport(typeBinding, ast, context, TypeLocation.FIELD); - String typeName = importRewrite.addImport(fqn); - fieldDeclaration.setType(ast.newSimpleType(ast.newName(typeName))); - - AbstractTypeDeclaration parent = (AbstractTypeDeclaration) domCu.types().get(0); - Assert.isNotNull(parent); - ListRewrite listRewrite= cuRewrite.getASTRewrite().getListRewrite(parent, parent.getBodyDeclarationsProperty()); - TextEditGroup msg= cuRewrite.createGroupDescription(RefactoringCoreMessages.ExtractConstantRefactoring_declare_constant); - listRewrite.insertFirst(fieldDeclaration, msg); + maybeAddConstructor(project, cuRewrite, domCu, typeName, fieldType, fieldName); // TextEdit edit = cuRewrite.getASTRewrite().rewriteAST(); diff --git a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/STS4LanguageClientImpl.java b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/STS4LanguageClientImpl.java index 6673ddfde..5e948402f 100644 --- a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/STS4LanguageClientImpl.java +++ b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/STS4LanguageClientImpl.java @@ -652,7 +652,7 @@ public class STS4LanguageClientImpl extends LanguageClientImpl implements STS4La public CompletableFuture injectBean(InjectBeanParams params) { return CompletableFuture .supplyAsync(() -> new InjectBean(Logger.forEclipsePlugin(LanguageServerCommonsActivator::getInstance)) - .computeEdits(params.docUri(), params.type(), params.name())); + .computeEdits(params.docUri(), params.typeDeclarationName(), params.type(), params.name())); } } diff --git a/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/java/InjectBeanParams.java b/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/java/InjectBeanParams.java index 9b3242162..4b2202a01 100644 --- a/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/java/InjectBeanParams.java +++ b/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/java/InjectBeanParams.java @@ -1,5 +1,5 @@ package org.springframework.ide.vscode.commons.protocol.java; -public record InjectBeanParams(String docUri, String type, String name) { +public record InjectBeanParams(String docUri, String typeDeclarationName, String type, String name) { } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/beans/BeanCompletionProposal.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/beans/BeanCompletionProposal.java index 41be083dc..a64bc2262 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/beans/BeanCompletionProposal.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/beans/BeanCompletionProposal.java @@ -315,7 +315,8 @@ public class BeanCompletionProposal implements ICompletionProposalWithScore { long start = System.currentTimeMillis(); DocumentEdits additionalEdit = new DocumentEdits(doc, false); try { - TextDocumentEdit beanInjectEdits = server.getClient().injectBean(new InjectBeanParams(doc.getUri(), beanType, fieldName)).get(); + TextDocumentEdit beanInjectEdits = server.getClient().injectBean(new InjectBeanParams(doc.getUri(), + JavaType.ShallowClass.build(className).getClassName(), beanType, fieldName)).get(); if (beanInjectEdits != null) { for (org.eclipse.lsp4j.TextEdit e : beanInjectEdits.getEdits()) { try {