From 6a9c3902179a203ab5ff1fe3de5730bd627233b6 Mon Sep 17 00:00:00 2001 From: aboyko Date: Wed, 26 Feb 2025 14:07:34 -0500 Subject: [PATCH] Fix bean injection when bean name is different from type simple name --- .../commons/rewrite/java/AddFieldRecipe.java | 16 ++++-- .../java/ConstructorInjectionRecipe.java | 14 ++++-- .../java/InjectBeanCompletionRecipe.java | 4 +- .../rewrite/java/AddFieldRecipeTest.java | 18 +++---- .../java/ConstructorInjectionRecipeTest.java | 49 +++++++++++++++++-- 5 files changed, 80 insertions(+), 21 deletions(-) diff --git a/headless-services/commons/commons-rewrite/src/main/java/org/springframework/ide/vscode/commons/rewrite/java/AddFieldRecipe.java b/headless-services/commons/commons-rewrite/src/main/java/org/springframework/ide/vscode/commons/rewrite/java/AddFieldRecipe.java index e6dde257cf..578d1aba12 100644 --- a/headless-services/commons/commons-rewrite/src/main/java/org/springframework/ide/vscode/commons/rewrite/java/AddFieldRecipe.java +++ b/headless-services/commons/commons-rewrite/src/main/java/org/springframework/ide/vscode/commons/rewrite/java/AddFieldRecipe.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017, 2024 Broadcom, Inc. + * Copyright (c) 2017, 2025 Broadcom, 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 @@ -15,6 +15,7 @@ import java.util.List; import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; import org.openrewrite.ExecutionContext; import org.openrewrite.Recipe; import org.openrewrite.Tree; @@ -51,11 +52,20 @@ public String getDescription() { @NonNull String classFqName; + + String fieldName; + + transient JavaType.FullyQualified fullyQualifiedType; @JsonCreator - public AddFieldRecipe(@NonNull @JsonProperty("fullyQualifiedClassName") String fullyQualifiedName, @NonNull @JsonProperty("classFqName") String classFqName) { + public AddFieldRecipe( + @NonNull @JsonProperty("fullyQualifiedClassName") String fullyQualifiedName, + @NonNull @JsonProperty("classFqName") String classFqName, + @Nullable @JsonProperty("fieldName") String fieldName) { this.fullyQualifiedName = fullyQualifiedName; + fullyQualifiedType = JavaType.ShallowClass.build(fullyQualifiedName); this.classFqName = classFqName; + this.fieldName = fieldName == null ? getFieldName(fullyQualifiedType) : fieldName; } @Override @@ -63,9 +73,7 @@ public TreeVisitor getVisitor() { return new JavaIsoVisitor() { - JavaType.FullyQualified fullyQualifiedType = JavaType.ShallowClass.build(fullyQualifiedName); String fieldType = getFieldType(fullyQualifiedType); - String fieldName = getFieldName(fullyQualifiedType); @Override public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) { diff --git a/headless-services/commons/commons-rewrite/src/main/java/org/springframework/ide/vscode/commons/rewrite/java/ConstructorInjectionRecipe.java b/headless-services/commons/commons-rewrite/src/main/java/org/springframework/ide/vscode/commons/rewrite/java/ConstructorInjectionRecipe.java index 43c05bc1be..3b12d42b4f 100644 --- a/headless-services/commons/commons-rewrite/src/main/java/org/springframework/ide/vscode/commons/rewrite/java/ConstructorInjectionRecipe.java +++ b/headless-services/commons/commons-rewrite/src/main/java/org/springframework/ide/vscode/commons/rewrite/java/ConstructorInjectionRecipe.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017, 2024 Broadcom, Inc. + * Copyright (c) 2017, 2025 Broadcom, 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 @@ -289,9 +289,17 @@ public MethodDeclaration visitMethodDeclaration(MethodDeclaration method, Execut ShallowClass type = JavaType.ShallowClass.build(methodType); J.FieldAccess fa = new J.FieldAccess(Tree.randomId(), Space.EMPTY, Markers.EMPTY, new J.Identifier(Tree.randomId(), Space.EMPTY, Markers.EMPTY, Collections.emptyList(), "this", md.getMethodType().getDeclaringType(), null), JLeftPadded.build(createFieldNameIdentifier()), type); Assignment assign = new J.Assignment(Tree.randomId(), Space.build("\n", Collections.emptyList()), Markers.EMPTY, fa, JLeftPadded.build(createFieldNameIdentifier()), type); + assign = autoFormat(assign, p, getCursor()); List newStatements = new ArrayList<>(md.getBody().getStatements()); - newStatements.add(assign); - md = md.withBody(autoFormat(md.getBody().withStatements(newStatements), p, getCursor())); + boolean empty = newStatements.isEmpty(); + if (empty) { + newStatements.add(assign); + md = md.withBody(autoFormat(md.getBody().withStatements(newStatements), p, getCursor())); + } else { + // Prefix is off otherwise even after autoFormat + newStatements.add(assign.withPrefix(newStatements.get(newStatements.size() - 1).getPrefix())); + md = md.withBody(md.getBody().withStatements(newStatements)); + } } return md; } diff --git a/headless-services/commons/commons-rewrite/src/main/java/org/springframework/ide/vscode/commons/rewrite/java/InjectBeanCompletionRecipe.java b/headless-services/commons/commons-rewrite/src/main/java/org/springframework/ide/vscode/commons/rewrite/java/InjectBeanCompletionRecipe.java index bf2b101242..bcfe1f2d8c 100644 --- a/headless-services/commons/commons-rewrite/src/main/java/org/springframework/ide/vscode/commons/rewrite/java/InjectBeanCompletionRecipe.java +++ b/headless-services/commons/commons-rewrite/src/main/java/org/springframework/ide/vscode/commons/rewrite/java/InjectBeanCompletionRecipe.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017, 2024 Broadcom, Inc. + * Copyright (c) 2017, 2025 Broadcom, 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 @@ -47,7 +47,7 @@ public InjectBeanCompletionRecipe(String fullyQualifiedName, String fieldName, S @Override public List getRecipeList() { List list = new ArrayList<>(); - list.add(new AddFieldRecipe(fullyQualifiedName, classFqName)); + list.add(new AddFieldRecipe(fullyQualifiedName, classFqName, fieldName)); list.add(new ConstructorInjectionRecipe(fullyQualifiedName, fieldName, classFqName)); return list; } diff --git a/headless-services/commons/commons-rewrite/src/test/java/org/springframework/ide/vscode/commons/rewrite/java/AddFieldRecipeTest.java b/headless-services/commons/commons-rewrite/src/test/java/org/springframework/ide/vscode/commons/rewrite/java/AddFieldRecipeTest.java index 6385bdce91..587f196441 100644 --- a/headless-services/commons/commons-rewrite/src/test/java/org/springframework/ide/vscode/commons/rewrite/java/AddFieldRecipeTest.java +++ b/headless-services/commons/commons-rewrite/src/test/java/org/springframework/ide/vscode/commons/rewrite/java/AddFieldRecipeTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017, 2024 Broadcom, Inc. + * Copyright (c) 2017, 2025 Broadcom, 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 @@ -28,7 +28,7 @@ public class AddFieldRecipeTest implements RewriteTest { @Override public void defaults(RecipeSpec spec) { - spec.recipe(new AddFieldRecipe("com.example.test.OwnerRepository", "com.example.demo.FooBar")) + spec.recipe(new AddFieldRecipe("com.example.test.OwnerRepository", "com.example.demo.FooBar", null)) .parser(JavaParser.fromJavaVersion() .logCompilationWarningsAndErrors(true)); } @@ -86,7 +86,7 @@ public void test() { public interface OwnerRepository{} """; - Recipe recipe = new AddFieldRecipe("com.example.demo.OwnerRepository", "com.example.demo.FooBar"); + Recipe recipe = new AddFieldRecipe("com.example.demo.OwnerRepository", "com.example.demo.FooBar", null); runRecipeAndAssert(recipe, beforeSourceStr, expectedSourceStr, dependsOn); } @@ -128,7 +128,7 @@ public void test() { public interface OwnerRepository{} """; - Recipe recipe = new AddFieldRecipe("com.example.test.OwnerRepository", "com.example.demo.FooBar"); + Recipe recipe = new AddFieldRecipe("com.example.test.OwnerRepository", "com.example.demo.FooBar", null); runRecipeAndAssert(recipe, beforeSourceStr, expectedSourceStr, dependsOn); } @@ -170,7 +170,7 @@ public void test() { public interface OwnerRepository{} """; - Recipe recipe = new AddFieldRecipe("com.example.test.Inner.OwnerRepository", "com.example.demo.FooBar"); + Recipe recipe = new AddFieldRecipe("com.example.test.Inner.OwnerRepository", "com.example.demo.FooBar", null); runRecipeAndAssert(recipe, beforeSourceStr, expectedSourceStr, dependsOn); } @@ -214,7 +214,7 @@ public void test() { public interface OwnerRepository{} """; - Recipe recipe = new AddFieldRecipe("com.example.test.OwnerRepository", "com.example.demo.FooBar$Inner"); + Recipe recipe = new AddFieldRecipe("com.example.test.OwnerRepository", "com.example.demo.FooBar$Inner", null); runRecipeAndAssert(recipe, beforeSourceStr, expectedSourceStr, dependsOn); } @@ -266,7 +266,7 @@ public void test1() {} public interface OwnerRepository{} """; - Recipe recipe = new AddFieldRecipe("com.example.test.Inner.OwnerRepository", "com.example.demo.FooBar"); + Recipe recipe = new AddFieldRecipe("com.example.test.Inner.OwnerRepository", "com.example.demo.FooBar", null); runRecipeAndAssert(recipe, beforeSourceStr, expectedSourceStr, dependsOn); } @@ -313,7 +313,7 @@ public void test() { @Component class FooBarNew { - private final Inner.OwnerRepository ownerRepository; + private final Inner.OwnerRepository ownerRepo; public void test1() {} @@ -325,7 +325,7 @@ public void test1() {} public interface OwnerRepository{} """; - Recipe recipe = new AddFieldRecipe("com.example.test.Inner.OwnerRepository", "com.example.demo.FooBarNew"); + Recipe recipe = new AddFieldRecipe("com.example.test.Inner.OwnerRepository", "com.example.demo.FooBarNew", "ownerRepo"); runRecipeAndAssert(recipe, beforeSourceStr, expectedSourceStr, dependsOn); } diff --git a/headless-services/commons/commons-rewrite/src/test/java/org/springframework/ide/vscode/commons/rewrite/java/ConstructorInjectionRecipeTest.java b/headless-services/commons/commons-rewrite/src/test/java/org/springframework/ide/vscode/commons/rewrite/java/ConstructorInjectionRecipeTest.java index 4b1d087690..862cb936bf 100644 --- a/headless-services/commons/commons-rewrite/src/test/java/org/springframework/ide/vscode/commons/rewrite/java/ConstructorInjectionRecipeTest.java +++ b/headless-services/commons/commons-rewrite/src/test/java/org/springframework/ide/vscode/commons/rewrite/java/ConstructorInjectionRecipeTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017, 2024 Broadcom, Inc. + * Copyright (c) 2017, 2025 Broadcom, 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 @@ -21,6 +21,7 @@ import org.openrewrite.SourceFile; import org.openrewrite.internal.InMemoryLargeSourceSet; import org.openrewrite.java.JavaParser; +import org.openrewrite.java.JavaParser.Builder; import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; @@ -32,8 +33,12 @@ public void defaults(RecipeSpec spec) { .parser(JavaParser.fromJavaVersion().classpath("spring-beans")); } - public static void runRecipeAndAssert(Recipe recipe, String beforeSourceStr, String expectedSourceStr, String dependsOn) { - JavaParser javaParser = JavaParser.fromJavaVersion().dependsOn(dependsOn).build(); + public static void runRecipeAndAssert(Recipe recipe, String beforeSourceStr, String expectedSourceStr, String... dependsOn) { + Builder builder = JavaParser.fromJavaVersion(); + if (dependsOn.length > 0) { + builder.dependsOn(dependsOn); + } + JavaParser javaParser = builder.build(); List list = javaParser.parse(beforeSourceStr).toList(); SourceFile beforeSource = list.get(0); @@ -486,4 +491,42 @@ public interface OwnerRepository{} runRecipeAndAssert(recipe, beforeSourceStr, expectedSourceStr, dependsOn); } + @Test + void injectObjectFieldIntoNewConstructor() { + + String beforeSourceStr = """ + package com.example.demo; + + public class A { + + private final String s; + private final Object obj; + + A(String s) { + this.s = s; + } + + } + """; + + String expectedSourceStr = """ + package com.example.demo; + + public class A { + + private final String s; + private final Object obj; + + A(String s, Object obj) { + this.s = s; + this.obj = obj; + } + + } + """; + + Recipe recipe = new ConstructorInjectionRecipe("java.lang.Object", "obj", "com.example.demo.A"); + runRecipeAndAssert(recipe, beforeSourceStr, expectedSourceStr); + } + }