From a3e22b40659f0fc6ff6f8e85bbcb177b5bbf9cc6 Mon Sep 17 00:00:00 2001
From: timo <1398557+timo-a@users.noreply.github.com>
Date: Sun, 8 Dec 2024 00:45:37 +0100
Subject: [PATCH 01/31] feat: add recipe that converts explicit getters to the
lombok annotation
---
.../java/migrate/lombok/ConvertGetter.java | 162 ++++++
.../java/migrate/lombok/LombokUtils.java | 67 +++
.../migrate/lombok/ConvertGetterTest.java | 463 ++++++++++++++++++
3 files changed, 692 insertions(+)
create mode 100644 src/main/java/org/openrewrite/java/migrate/lombok/ConvertGetter.java
create mode 100644 src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
create mode 100644 src/test/java/org/openrewrite/java/migrate/lombok/ConvertGetterTest.java
diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/ConvertGetter.java b/src/main/java/org/openrewrite/java/migrate/lombok/ConvertGetter.java
new file mode 100644
index 0000000000..d54d339b9a
--- /dev/null
+++ b/src/main/java/org/openrewrite/java/migrate/lombok/ConvertGetter.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2021 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.migrate.lombok;
+
+import lombok.AccessLevel;
+import lombok.EqualsAndHashCode;
+import lombok.Value;
+import org.openrewrite.ExecutionContext;
+import org.openrewrite.Recipe;
+import org.openrewrite.TreeVisitor;
+import org.openrewrite.java.JavaIsoVisitor;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.java.JavaTemplate;
+import org.openrewrite.java.tree.J;
+
+import java.util.HashSet;
+import java.util.Optional;
+import java.util.Set;
+import java.util.StringJoiner;
+
+import static java.util.Comparator.comparing;
+import static org.openrewrite.java.tree.JavaType.Variable;
+
+@Value
+@EqualsAndHashCode(callSuper = false)
+public class ConvertGetter extends Recipe {
+
+ @Override
+ public String getDisplayName() {
+ //language=markdown
+ return "Convert getter methods to annotations";
+ }
+
+ @Override
+ public String getDescription() {
+ //language=markdown
+ return new StringJoiner("\n")
+ .add("Convert trivial getter methods to `@Getter` annotations on their respective fields.")
+ .add("")
+ .add("Limitations:")
+ .add("")
+ .add(" - Does not add a dependency to Lombok, users need to do that manually")
+ .add(" - Ignores fields that are declared on the same line as others, e.g. `private int foo, bar;" +
+ "Users who have such fields are advised to separate them beforehand with " +
+ "[org.openrewrite.staticanalysis.MultipleVariableDeclaration]" +
+ "(https://docs.openrewrite.org/recipes/staticanalysis/multiplevariabledeclarations).")
+ .add(" - Does not offer any of the configuration keys listed in https://projectlombok.org/features/GetterSetter.")
+ .toString();
+ }
+
+ @Override
+ public TreeVisitor, ExecutionContext> getVisitor() {
+ return new MethodRemover();
+ }
+
+
+ @Value
+ @EqualsAndHashCode(callSuper = false)
+ private static class MethodRemover extends JavaIsoVisitor {
+ private static final String FIELDS_TO_DECORATE_KEY = "FIELDS_TO_DECORATE";
+
+ @Override
+ public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) {
+
+ //initialize set of fields to annotate
+ getCursor().putMessage(FIELDS_TO_DECORATE_KEY, new HashSet());
+
+ //delete methods, note down corresponding fields
+ J.ClassDeclaration classDeclAfterVisit = super.visitClassDeclaration(classDecl, ctx);
+
+ //only thing that can have changed is removal of getter methods
+ if (classDeclAfterVisit != classDecl) {
+ //this set collects the fields for which existing methods have already been removed
+ Set fieldsToDecorate = getCursor().pollNearestMessage(FIELDS_TO_DECORATE_KEY);
+ doAfterVisit(new FieldAnnotator(fieldsToDecorate));
+ }
+ return classDeclAfterVisit;
+ }
+
+ @Override
+ public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) {
+ assert method.getMethodType() != null;
+
+ if (LombokUtils.isEffectivelyGetter(method)) {
+ J.Return return_ = (J.Return) method.getBody().getStatements().get(0);
+ Variable fieldType = ((J.Identifier) return_.getExpression()).getFieldType();
+ boolean nameMatch = method.getSimpleName().equals(LombokUtils.deriveGetterMethodName(fieldType));
+ if (nameMatch){
+ ((Set) getCursor().getNearestMessage(FIELDS_TO_DECORATE_KEY))
+ .add(new Finding(fieldType.getName(), LombokUtils.getAccessLevel(method.getModifiers())));
+ return null; //delete
+ }
+ }
+ return method;
+ }
+ }
+
+ @Value
+ private static class Finding {
+ String fieldName;
+ AccessLevel accessLevel;
+ }
+
+
+ @Value
+ @EqualsAndHashCode(callSuper = false)
+ static class FieldAnnotator extends JavaIsoVisitor{
+
+ Set fieldsToDecorate;
+
+ private JavaTemplate getAnnotation(AccessLevel accessLevel) {
+ JavaTemplate.Builder builder = AccessLevel.PUBLIC.equals(accessLevel)
+ ? JavaTemplate.builder("@Getter\n")
+ : JavaTemplate.builder("@Getter(AccessLevel." + accessLevel.name() + ")\n")
+ .imports("lombok.AccessLevel");
+
+ return builder
+ .imports("lombok.Getter")
+ .javaParser(JavaParser.fromJavaVersion()
+ .classpath("lombok"))
+ .build();
+ }
+
+ @Override
+ public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations multiVariable, ExecutionContext ctx) {
+
+ //we accept only one var decl per line, see description
+ if (multiVariable.getVariables().size() > 1) {
+ return multiVariable;
+ }
+
+ J.VariableDeclarations.NamedVariable variable = multiVariable.getVariables().get(0);
+ Optional field = fieldsToDecorate.stream()
+ .filter(f -> f.fieldName.equals(variable.getSimpleName()))
+ .findFirst();
+
+ if (!field.isPresent()) {
+ return multiVariable; //not the field we are looking for
+ }
+
+ J.VariableDeclarations annotated = getAnnotation(field.get().getAccessLevel()).apply(
+ getCursor(),
+ multiVariable.getCoordinates().addAnnotation(comparing(J.Annotation::getSimpleName)));
+ maybeAddImport("lombok.Getter");
+ maybeAddImport("lombok.AccessLevel");
+ return annotated;
+ }
+ }
+}
diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java b/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
new file mode 100644
index 0000000000..49388a97cd
--- /dev/null
+++ b/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
@@ -0,0 +1,67 @@
+package org.openrewrite.java.migrate.lombok;
+
+import com.google.common.collect.ImmutableMap;
+import lombok.AccessLevel;
+import org.openrewrite.internal.StringUtils;
+import org.openrewrite.java.tree.Expression;
+import org.openrewrite.java.tree.J;
+import org.openrewrite.java.tree.JavaType;
+
+import java.util.Collection;
+import java.util.Map;
+
+import static lombok.AccessLevel.*;
+import static org.openrewrite.java.tree.J.Modifier.Type.*;
+
+public class LombokUtils {
+
+ public static boolean isEffectivelyGetter(J.MethodDeclaration method) {
+ boolean takesNoParameters = method.getParameters().get(0) instanceof J.Empty;
+ boolean singularReturn = method.getBody() != null //abstract methods can be null
+ && method.getBody().getStatements().size() == 1
+ && method.getBody().getStatements().get(0) instanceof J.Return;
+
+ if (takesNoParameters && singularReturn) {
+ Expression returnExpression = ((J.Return) method.getBody().getStatements().get(0)).getExpression();
+ //returns just an identifier
+ if (returnExpression instanceof J.Identifier) {
+ J.Identifier identifier = (J.Identifier) returnExpression;
+ JavaType.Variable fieldType = identifier.getFieldType();
+ boolean typeMatch = method.getType().equals(fieldType.getType());
+ return typeMatch;
+ }
+ }
+ return false;
+ }
+
+ public static String deriveGetterMethodName(JavaType.Variable fieldType) {
+ boolean isPrimitiveBoolean = JavaType.Variable.Primitive.Boolean.equals(fieldType.getType());
+
+ final String fieldName = fieldType.getName();
+
+ boolean alreadyStartsWithIs = fieldName.length() >= 3
+ && fieldName.substring(0, 3).matches("is[A-Z]");
+
+ if (isPrimitiveBoolean)
+ if (alreadyStartsWithIs)
+ return fieldName;
+ else
+ return "is" + StringUtils.capitalize(fieldName);
+
+ return "get" + StringUtils.capitalize(fieldName);
+ }
+
+ public static AccessLevel getAccessLevel(Collection modifiers) {
+ Map map = ImmutableMap.builder()
+ .put(Public, PUBLIC)
+ .put(Protected, PROTECTED)
+ .put(Private, PRIVATE)
+ .build();
+
+ return modifiers.stream()
+ .map(modifier -> map.getOrDefault(modifier.getType(), AccessLevel.NONE))
+ .filter(a -> a != AccessLevel.NONE)
+ .findAny().orElse(AccessLevel.PACKAGE);
+ }
+
+}
diff --git a/src/test/java/org/openrewrite/java/migrate/lombok/ConvertGetterTest.java b/src/test/java/org/openrewrite/java/migrate/lombok/ConvertGetterTest.java
new file mode 100644
index 0000000000..4996feca45
--- /dev/null
+++ b/src/test/java/org/openrewrite/java/migrate/lombok/ConvertGetterTest.java
@@ -0,0 +1,463 @@
+/*
+ * Copyright 2021 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.migrate.lombok;
+
+import org.junit.jupiter.api.Test;
+import org.openrewrite.DocumentExample;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+// This is a test for the ConvertToNoArgsConstructor recipe, as an example of how to write a test for an imperative recipe.
+class ConvertGetterTest implements RewriteTest {
+
+ // Note, you can define defaults for the RecipeSpec and these defaults will be used for all tests.
+ // In this case, the recipe and the parser are common. See below, on how the defaults can be overridden
+ // per test.
+ @Override
+ public void defaults(RecipeSpec spec) {
+ spec.recipe(new ConvertGetter())
+ .parser(JavaParser.fromJavaVersion().logCompilationWarningsAndErrors(true));
+ }
+
+ @DocumentExample
+ @Test
+ void replaceGetter() {
+ rewriteRun(// language=java
+ java(
+ """
+ class A {
+
+ int foo = 9;
+
+ public int getFoo() {
+ return foo;
+ }
+ }
+ """,
+ """
+ import lombok.Getter;
+
+ class A {
+
+ @Getter
+ int foo = 9;
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void replacePackageGetter() {
+ rewriteRun(// language=java
+ java(
+ """
+ class A {
+
+ int foo = 9;
+
+ int getFoo() {
+ return foo;
+ }
+ }
+ """,
+ """
+ import lombok.AccessLevel;
+ import lombok.Getter;
+
+ class A {
+
+ @Getter(AccessLevel.PACKAGE)
+ int foo = 9;
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void replaceProtectedGetter() {
+ rewriteRun(// language=java
+ java(
+ """
+ class A {
+
+ int foo = 9;
+
+ protected int getFoo() {
+ return foo;
+ }
+ }
+ """,
+ """
+ import lombok.AccessLevel;
+ import lombok.Getter;
+
+ class A {
+
+ @Getter(AccessLevel.PROTECTED)
+ int foo = 9;
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void replacePrivateGetter() {
+ rewriteRun(// language=java
+ java(
+ """
+ class A {
+
+ int foo = 9;
+
+ private int getFoo() {
+ return foo;
+ }
+ }
+ """,
+ """
+ import lombok.AccessLevel;
+ import lombok.Getter;
+
+ class A {
+
+ @Getter(AccessLevel.PRIVATE)
+ int foo = 9;
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void replaceJustTheMatchingGetter() {
+ rewriteRun(// language=java
+ java(
+ """
+ class A {
+
+ int foo = 9;
+
+ int ba;
+
+ public A() {
+ ba = 1;
+ }
+
+ public int getFoo() {
+ return foo;
+ }
+
+ public int getMoo() {//method name wrong
+ return ba;
+ }
+ }
+ """,
+ """
+ import lombok.Getter;
+
+ class A {
+
+ @Getter
+ int foo = 9;
+
+ int ba;
+
+ public A() {
+ ba = 1;
+ }
+
+ public int getMoo() {//method name wrong
+ return ba;
+ }
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void noChangeWhenMethodNameDoesntMatch() {
+ rewriteRun(// language=java
+ java(
+ """
+ class A {
+
+ int foo = 9;
+
+ public A() {
+ }
+
+ int getfoo() {//method name wrong
+ return foo;
+ }
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void noChangeWhenReturnTypeDoesntMatch() {
+ rewriteRun(// language=java
+ java(
+ """
+ class A {
+
+ int foo = 9;
+
+ public A() {
+ }
+
+ long getfoo() {//return type wrong
+ return foo;
+ }
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void noChangeWhenFieldIsNotReturned() {
+ rewriteRun(// language=java
+ java(
+ """
+ class A {
+
+ int foo = 9;
+ int ba = 10;
+
+ public A() {
+ }
+
+ int getFoo() {
+ return 5;//does not return variable
+ }
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void noChangeWhenDifferentFieldIsReturned() {
+ rewriteRun(// language=java
+ java(
+ """
+ class A {
+
+ int foo = 9;
+ int ba = 10;
+
+ public A() {
+ }
+
+ int getFoo() {
+ return ba;//returns wrong variable
+ }
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void noChangeWhenSideEffects1() {
+ rewriteRun(// language=java
+ java(
+ """
+ class A {
+
+ int foo = 9;
+ int ba = 10;
+
+ public A() {
+ }
+
+ int getfoo() {
+ foo++;//does extra stuff
+ return foo;
+ }
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void noChangeWhenSideEffects2() {
+ rewriteRun(// language=java
+ java(
+ """
+ class A {
+
+ int foo = 9;
+ int ba = 10;
+
+ public A() {
+ }
+
+ int getFoo() {
+ ba++;//does unrelated extra stuff
+ return foo;
+ }
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void replacePrimitiveBoolean() {
+ rewriteRun(// language=java
+ java(
+ """
+ class A {
+
+ boolean foo = true;
+
+ public boolean isFoo() {
+ return foo;
+ }
+ }
+ """,
+ """
+ import lombok.Getter;
+
+ class A {
+
+ @Getter
+ boolean foo = true;
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void replacePrimitiveBooleanStartingWithIs() {
+ rewriteRun(// language=java
+ java(
+ """
+ class A {
+
+ boolean isFoo = true;
+
+ public boolean isFoo() {
+ return isFoo;
+ }
+ }
+ """,
+ """
+ import lombok.Getter;
+
+ class A {
+
+ @Getter
+ boolean isFoo = true;
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void noChangeWhenPrimitiveBooleanUsesGet() {
+ rewriteRun(// language=java
+ java(
+ """
+ class A {
+
+ boolean foo = true;
+
+ boolean getFoo() {
+ return foo;
+ }
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void replaceBoolean() {
+ rewriteRun(// language=java
+ java(
+ """
+ class A {
+
+ Boolean foo = true;
+
+ public Boolean getFoo() {
+ return foo;
+ }
+ }
+ """,
+ """
+ import lombok.Getter;
+
+ class A {
+
+ @Getter
+ Boolean foo = true;
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void noChangeWhenBooleanUsesIs() {
+ rewriteRun(// language=java
+ java(
+ """
+ class A {
+
+ Boolean foo = true;
+
+ Boolean isFoo() {
+ return foo;
+ }
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void noChangeWhenBooleanUsesIs2() {
+ rewriteRun(// language=java
+ java(
+ """
+ class A {
+
+ Boolean isfoo = true;
+
+ Boolean isFoo() {
+ return isfoo;
+ }
+ }
+ """
+ )
+ );
+ }
+}
From d6eebf2fbcee9579b24c339fa9dff96ea7eb4cc6 Mon Sep 17 00:00:00 2001
From: timo <1398557+timo-a@users.noreply.github.com>
Date: Sun, 8 Dec 2024 01:13:38 +0100
Subject: [PATCH 02/31] chore: IntelliJ auto-formatter
---
.../org/openrewrite/java/migrate/lombok/ConvertGetter.java | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/ConvertGetter.java b/src/main/java/org/openrewrite/java/migrate/lombok/ConvertGetter.java
index d54d339b9a..5366101974 100644
--- a/src/main/java/org/openrewrite/java/migrate/lombok/ConvertGetter.java
+++ b/src/main/java/org/openrewrite/java/migrate/lombok/ConvertGetter.java
@@ -98,7 +98,7 @@ public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, Ex
J.Return return_ = (J.Return) method.getBody().getStatements().get(0);
Variable fieldType = ((J.Identifier) return_.getExpression()).getFieldType();
boolean nameMatch = method.getSimpleName().equals(LombokUtils.deriveGetterMethodName(fieldType));
- if (nameMatch){
+ if (nameMatch) {
((Set) getCursor().getNearestMessage(FIELDS_TO_DECORATE_KEY))
.add(new Finding(fieldType.getName(), LombokUtils.getAccessLevel(method.getModifiers())));
return null; //delete
@@ -117,7 +117,7 @@ private static class Finding {
@Value
@EqualsAndHashCode(callSuper = false)
- static class FieldAnnotator extends JavaIsoVisitor{
+ static class FieldAnnotator extends JavaIsoVisitor {
Set fieldsToDecorate;
From b38f90d407b769608e189981e520c12c3d0bdedd Mon Sep 17 00:00:00 2001
From: timo-a <1398557+timo-a@users.noreply.github.com>
Date: Sun, 8 Dec 2024 13:20:01 +0100
Subject: [PATCH 03/31] add licence header
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
---
.../java/migrate/lombok/LombokUtils.java | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java b/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
index 49388a97cd..0e7481a198 100644
--- a/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
+++ b/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright 2024 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
package org.openrewrite.java.migrate.lombok;
import com.google.common.collect.ImmutableMap;
From c35b7d685ab22b595c2431c7dae9ab07406dcdae Mon Sep 17 00:00:00 2001
From: timo-a <1398557+timo-a@users.noreply.github.com>
Date: Sun, 8 Dec 2024 13:22:35 +0100
Subject: [PATCH 04/31] Apply suggestions from code review
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
---
.../org/openrewrite/java/migrate/lombok/ConvertGetter.java | 3 ++-
.../java/org/openrewrite/java/migrate/lombok/LombokUtils.java | 3 +--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/ConvertGetter.java b/src/main/java/org/openrewrite/java/migrate/lombok/ConvertGetter.java
index 5366101974..baa3ddcf1f 100644
--- a/src/main/java/org/openrewrite/java/migrate/lombok/ConvertGetter.java
+++ b/src/main/java/org/openrewrite/java/migrate/lombok/ConvertGetter.java
@@ -18,6 +18,7 @@
import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.Value;
+import org.jspecify.annotations.Nullable;
import org.openrewrite.ExecutionContext;
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;
@@ -91,7 +92,7 @@ public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, Ex
}
@Override
- public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) {
+ public @Nullable J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) {
assert method.getMethodType() != null;
if (LombokUtils.isEffectivelyGetter(method)) {
diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java b/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
index 0e7481a198..eb411e4d20 100644
--- a/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
+++ b/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
@@ -42,8 +42,7 @@ public static boolean isEffectivelyGetter(J.MethodDeclaration method) {
if (returnExpression instanceof J.Identifier) {
J.Identifier identifier = (J.Identifier) returnExpression;
JavaType.Variable fieldType = identifier.getFieldType();
- boolean typeMatch = method.getType().equals(fieldType.getType());
- return typeMatch;
+ return method.getType().equals(fieldType.getType());
}
}
return false;
From f06e2706c021272b388b282e0c2e7c07eac0b9fb Mon Sep 17 00:00:00 2001
From: Tim te Beek
Date: Sun, 8 Dec 2024 13:23:56 +0100
Subject: [PATCH 05/31] Apply suggestions from code review
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
---
.../openrewrite/java/migrate/lombok/ConvertGetter.java | 6 +++---
.../org/openrewrite/java/migrate/lombok/LombokUtils.java | 8 ++++----
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/ConvertGetter.java b/src/main/java/org/openrewrite/java/migrate/lombok/ConvertGetter.java
index baa3ddcf1f..5dd2490336 100644
--- a/src/main/java/org/openrewrite/java/migrate/lombok/ConvertGetter.java
+++ b/src/main/java/org/openrewrite/java/migrate/lombok/ConvertGetter.java
@@ -123,9 +123,9 @@ static class FieldAnnotator extends JavaIsoVisitor {
Set fieldsToDecorate;
private JavaTemplate getAnnotation(AccessLevel accessLevel) {
- JavaTemplate.Builder builder = AccessLevel.PUBLIC.equals(accessLevel)
- ? JavaTemplate.builder("@Getter\n")
- : JavaTemplate.builder("@Getter(AccessLevel." + accessLevel.name() + ")\n")
+ JavaTemplate.Builder builder = AccessLevel.PUBLIC.equals(accessLevel) ?
+ JavaTemplate.builder("@Getter\n") :
+ JavaTemplate.builder("@Getter(AccessLevel." + accessLevel.name() + ")\n")
.imports("lombok.AccessLevel");
return builder
diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java b/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
index eb411e4d20..84a448dd97 100644
--- a/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
+++ b/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
@@ -33,8 +33,8 @@ public class LombokUtils {
public static boolean isEffectivelyGetter(J.MethodDeclaration method) {
boolean takesNoParameters = method.getParameters().get(0) instanceof J.Empty;
boolean singularReturn = method.getBody() != null //abstract methods can be null
- && method.getBody().getStatements().size() == 1
- && method.getBody().getStatements().get(0) instanceof J.Return;
+ && method.getBody().getStatements().size() == 1 &&
+ method.getBody().getStatements().get(0) instanceof J.Return;
if (takesNoParameters && singularReturn) {
Expression returnExpression = ((J.Return) method.getBody().getStatements().get(0)).getExpression();
@@ -53,8 +53,8 @@ public static String deriveGetterMethodName(JavaType.Variable fieldType) {
final String fieldName = fieldType.getName();
- boolean alreadyStartsWithIs = fieldName.length() >= 3
- && fieldName.substring(0, 3).matches("is[A-Z]");
+ boolean alreadyStartsWithIs = fieldName.length() >= 3 &&
+ fieldName.substring(0, 3).matches("is[A-Z]");
if (isPrimitiveBoolean)
if (alreadyStartsWithIs)
From e6fd1bb4b1eabd3e639c8c7f32f06c10b0fce75a Mon Sep 17 00:00:00 2001
From: timo <1398557+timo-a@users.noreply.github.com>
Date: Sun, 8 Dec 2024 13:30:50 +0100
Subject: [PATCH 06/31] roll back nullable annotation
---
.../org/openrewrite/java/migrate/lombok/ConvertGetter.java | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/ConvertGetter.java b/src/main/java/org/openrewrite/java/migrate/lombok/ConvertGetter.java
index 5dd2490336..365f1d8f92 100644
--- a/src/main/java/org/openrewrite/java/migrate/lombok/ConvertGetter.java
+++ b/src/main/java/org/openrewrite/java/migrate/lombok/ConvertGetter.java
@@ -18,7 +18,6 @@
import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.Value;
-import org.jspecify.annotations.Nullable;
import org.openrewrite.ExecutionContext;
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;
@@ -92,7 +91,7 @@ public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, Ex
}
@Override
- public @Nullable J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) {
+ public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) {
assert method.getMethodType() != null;
if (LombokUtils.isEffectivelyGetter(method)) {
From 4b7fb671151ab9580a4718234a5ac1304d5d7142 Mon Sep 17 00:00:00 2001
From: Tim te Beek
Date: Sun, 8 Dec 2024 16:48:53 +0100
Subject: [PATCH 07/31] Light polish
---
.../java/migrate/lombok/ConvertGetter.java | 29 +++++++------------
.../java/migrate/lombok/LombokUtils.java | 21 ++++++--------
.../migrate/lombok/ConvertGetterTest.java | 8 +----
3 files changed, 20 insertions(+), 38 deletions(-)
diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/ConvertGetter.java b/src/main/java/org/openrewrite/java/migrate/lombok/ConvertGetter.java
index 365f1d8f92..dde3a88601 100644
--- a/src/main/java/org/openrewrite/java/migrate/lombok/ConvertGetter.java
+++ b/src/main/java/org/openrewrite/java/migrate/lombok/ConvertGetter.java
@@ -18,6 +18,7 @@
import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.Value;
+import org.jspecify.annotations.Nullable;
import org.openrewrite.ExecutionContext;
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;
@@ -29,7 +30,6 @@
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
-import java.util.StringJoiner;
import static java.util.Comparator.comparing;
import static org.openrewrite.java.tree.JavaType.Variable;
@@ -40,25 +40,18 @@ public class ConvertGetter extends Recipe {
@Override
public String getDisplayName() {
- //language=markdown
return "Convert getter methods to annotations";
}
@Override
public String getDescription() {
//language=markdown
- return new StringJoiner("\n")
- .add("Convert trivial getter methods to `@Getter` annotations on their respective fields.")
- .add("")
- .add("Limitations:")
- .add("")
- .add(" - Does not add a dependency to Lombok, users need to do that manually")
- .add(" - Ignores fields that are declared on the same line as others, e.g. `private int foo, bar;" +
- "Users who have such fields are advised to separate them beforehand with " +
- "[org.openrewrite.staticanalysis.MultipleVariableDeclaration]" +
- "(https://docs.openrewrite.org/recipes/staticanalysis/multiplevariabledeclarations).")
- .add(" - Does not offer any of the configuration keys listed in https://projectlombok.org/features/GetterSetter.")
- .toString();
+ return "Convert trivial getter methods to `@Getter` annotations on their respective fields.\n\n" +
+ "Limitations:\n\n" +
+ " - Does not add a dependency to Lombok, users need to do that manually\n" +
+ " - Ignores fields that are declared on the same line as others, e.g. `private int foo, bar; " +
+ "Users who have such fields are advised to separate them beforehand with [org.openrewrite.staticanalysis.MultipleVariableDeclaration](https://docs.openrewrite.org/recipes/staticanalysis/multiplevariabledeclarations).\n" +
+ " - Does not offer any of the configuration keys listed in https://projectlombok.org/features/GetterSetter.";
}
@Override
@@ -66,7 +59,6 @@ public TreeVisitor, ExecutionContext> getVisitor() {
return new MethodRemover();
}
-
@Value
@EqualsAndHashCode(callSuper = false)
private static class MethodRemover extends JavaIsoVisitor {
@@ -91,7 +83,7 @@ public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, Ex
}
@Override
- public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) {
+ public J.@Nullable MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) {
assert method.getMethodType() != null;
if (LombokUtils.isEffectivelyGetter(method)) {
@@ -125,12 +117,11 @@ private JavaTemplate getAnnotation(AccessLevel accessLevel) {
JavaTemplate.Builder builder = AccessLevel.PUBLIC.equals(accessLevel) ?
JavaTemplate.builder("@Getter\n") :
JavaTemplate.builder("@Getter(AccessLevel." + accessLevel.name() + ")\n")
- .imports("lombok.AccessLevel");
+ .imports("lombok.AccessLevel");
return builder
.imports("lombok.Getter")
- .javaParser(JavaParser.fromJavaVersion()
- .classpath("lombok"))
+ .javaParser(JavaParser.fromJavaVersion().classpath("lombok"))
.build();
}
diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java b/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
index 84a448dd97..fafb0fa056 100644
--- a/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
+++ b/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
@@ -28,7 +28,7 @@
import static lombok.AccessLevel.*;
import static org.openrewrite.java.tree.J.Modifier.Type.*;
-public class LombokUtils {
+class LombokUtils {
public static boolean isEffectivelyGetter(J.MethodDeclaration method) {
boolean takesNoParameters = method.getParameters().get(0) instanceof J.Empty;
@@ -49,19 +49,16 @@ public static boolean isEffectivelyGetter(J.MethodDeclaration method) {
}
public static String deriveGetterMethodName(JavaType.Variable fieldType) {
- boolean isPrimitiveBoolean = JavaType.Variable.Primitive.Boolean.equals(fieldType.getType());
-
- final String fieldName = fieldType.getName();
-
- boolean alreadyStartsWithIs = fieldName.length() >= 3 &&
- fieldName.substring(0, 3).matches("is[A-Z]");
-
- if (isPrimitiveBoolean)
- if (alreadyStartsWithIs)
+ String fieldName = fieldType.getName();
+ if (fieldType.getType() == JavaType.Variable.Primitive.Boolean) {
+ boolean alreadyStartsWithIs = fieldName.length() >= 3 &&
+ fieldName.substring(0, 3).matches("is[A-Z]");
+ if (alreadyStartsWithIs) {
return fieldName;
- else
+ } else {
return "is" + StringUtils.capitalize(fieldName);
-
+ }
+ }
return "get" + StringUtils.capitalize(fieldName);
}
diff --git a/src/test/java/org/openrewrite/java/migrate/lombok/ConvertGetterTest.java b/src/test/java/org/openrewrite/java/migrate/lombok/ConvertGetterTest.java
index 4996feca45..72b51828b2 100644
--- a/src/test/java/org/openrewrite/java/migrate/lombok/ConvertGetterTest.java
+++ b/src/test/java/org/openrewrite/java/migrate/lombok/ConvertGetterTest.java
@@ -17,22 +17,16 @@
import org.junit.jupiter.api.Test;
import org.openrewrite.DocumentExample;
-import org.openrewrite.java.JavaParser;
import org.openrewrite.test.RecipeSpec;
import org.openrewrite.test.RewriteTest;
import static org.openrewrite.java.Assertions.java;
-// This is a test for the ConvertToNoArgsConstructor recipe, as an example of how to write a test for an imperative recipe.
class ConvertGetterTest implements RewriteTest {
- // Note, you can define defaults for the RecipeSpec and these defaults will be used for all tests.
- // In this case, the recipe and the parser are common. See below, on how the defaults can be overridden
- // per test.
@Override
public void defaults(RecipeSpec spec) {
- spec.recipe(new ConvertGetter())
- .parser(JavaParser.fromJavaVersion().logCompilationWarningsAndErrors(true));
+ spec.recipe(new ConvertGetter());
}
@DocumentExample
From 665e73a0987c7b1ac3d1298374c736cb6fc91a94 Mon Sep 17 00:00:00 2001
From: Tim te Beek
Date: Mon, 9 Dec 2024 09:49:13 +0100
Subject: [PATCH 08/31] Rename and add Lombok tag
---
.../{ConvertGetter.java => UseLombokGetter.java} | 13 ++++++++-----
...vertGetterTest.java => UseLombokGetterTest.java} | 4 ++--
2 files changed, 10 insertions(+), 7 deletions(-)
rename src/main/java/org/openrewrite/java/migrate/lombok/{ConvertGetter.java => UseLombokGetter.java} (95%)
rename src/test/java/org/openrewrite/java/migrate/lombok/{ConvertGetterTest.java => UseLombokGetterTest.java} (98%)
diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/ConvertGetter.java b/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokGetter.java
similarity index 95%
rename from src/main/java/org/openrewrite/java/migrate/lombok/ConvertGetter.java
rename to src/main/java/org/openrewrite/java/migrate/lombok/UseLombokGetter.java
index dde3a88601..fdc9ed8250 100644
--- a/src/main/java/org/openrewrite/java/migrate/lombok/ConvertGetter.java
+++ b/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokGetter.java
@@ -27,6 +27,7 @@
import org.openrewrite.java.JavaTemplate;
import org.openrewrite.java.tree.J;
+import java.util.Collections;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
@@ -36,7 +37,7 @@
@Value
@EqualsAndHashCode(callSuper = false)
-public class ConvertGetter extends Recipe {
+public class UseLombokGetter extends Recipe {
@Override
public String getDisplayName() {
@@ -54,6 +55,11 @@ public String getDescription() {
" - Does not offer any of the configuration keys listed in https://projectlombok.org/features/GetterSetter.";
}
+ @Override
+ public Set getTags() {
+ return Collections.singleton("lombok");
+ }
+
@Override
public TreeVisitor, ExecutionContext> getVisitor() {
return new MethodRemover();
@@ -84,9 +90,7 @@ public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, Ex
@Override
public J.@Nullable MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) {
- assert method.getMethodType() != null;
-
- if (LombokUtils.isEffectivelyGetter(method)) {
+ if (method.getMethodType() != null && LombokUtils.isEffectivelyGetter(method)) {
J.Return return_ = (J.Return) method.getBody().getStatements().get(0);
Variable fieldType = ((J.Identifier) return_.getExpression()).getFieldType();
boolean nameMatch = method.getSimpleName().equals(LombokUtils.deriveGetterMethodName(fieldType));
@@ -106,7 +110,6 @@ private static class Finding {
AccessLevel accessLevel;
}
-
@Value
@EqualsAndHashCode(callSuper = false)
static class FieldAnnotator extends JavaIsoVisitor {
diff --git a/src/test/java/org/openrewrite/java/migrate/lombok/ConvertGetterTest.java b/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokGetterTest.java
similarity index 98%
rename from src/test/java/org/openrewrite/java/migrate/lombok/ConvertGetterTest.java
rename to src/test/java/org/openrewrite/java/migrate/lombok/UseLombokGetterTest.java
index 72b51828b2..748fff7cf3 100644
--- a/src/test/java/org/openrewrite/java/migrate/lombok/ConvertGetterTest.java
+++ b/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokGetterTest.java
@@ -22,11 +22,11 @@
import static org.openrewrite.java.Assertions.java;
-class ConvertGetterTest implements RewriteTest {
+class UseLombokGetterTest implements RewriteTest {
@Override
public void defaults(RecipeSpec spec) {
- spec.recipe(new ConvertGetter());
+ spec.recipe(new UseLombokGetter());
}
@DocumentExample
From 91c579b6b27b370b15b8f6d4c90d655d565481b9 Mon Sep 17 00:00:00 2001
From: Tim te Beek
Date: Mon, 9 Dec 2024 10:23:21 +0100
Subject: [PATCH 09/31] Also handle field access
---
.../java/migrate/lombok/LombokUtils.java | 42 +++++++++++--------
.../java/migrate/lombok/UseLombokGetter.java | 24 ++++++++---
.../migrate/lombok/UseLombokGetterTest.java | 27 ++++++++++++
3 files changed, 69 insertions(+), 24 deletions(-)
diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java b/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
index fafb0fa056..179c0094b0 100644
--- a/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
+++ b/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
@@ -30,27 +30,33 @@
class LombokUtils {
- public static boolean isEffectivelyGetter(J.MethodDeclaration method) {
- boolean takesNoParameters = method.getParameters().get(0) instanceof J.Empty;
- boolean singularReturn = method.getBody() != null //abstract methods can be null
- && method.getBody().getStatements().size() == 1 &&
- method.getBody().getStatements().get(0) instanceof J.Return;
-
- if (takesNoParameters && singularReturn) {
- Expression returnExpression = ((J.Return) method.getBody().getStatements().get(0)).getExpression();
- //returns just an identifier
- if (returnExpression instanceof J.Identifier) {
- J.Identifier identifier = (J.Identifier) returnExpression;
- JavaType.Variable fieldType = identifier.getFieldType();
- return method.getType().equals(fieldType.getType());
- }
+ static boolean isEffectivelyGetter(J.MethodDeclaration method) {
+ // Check signature: no parameters
+ if (!(method.getParameters().get(0) instanceof J.Empty) || method.getReturnTypeExpression() == null) {
+ return false;
+ }
+ // Check body: just a return statement
+ if (method.getBody() == null ||
+ method.getBody().getStatements().size() != 1 ||
+ !(method.getBody().getStatements().get(0) instanceof J.Return)) {
+ return false;
+ }
+ Expression returnExpression = ((J.Return) method.getBody().getStatements().get(0)).getExpression();
+ //returns just an identifier
+ if (returnExpression instanceof J.Identifier) {
+ J.Identifier identifier = (J.Identifier) returnExpression;
+ JavaType.Variable fieldType = identifier.getFieldType();
+ return method.getType().equals(fieldType.getType());
+ } else if (returnExpression instanceof J.FieldAccess) {
+ J.FieldAccess fieldAccess = (J.FieldAccess) returnExpression;
+ JavaType fieldType = fieldAccess.getType();
+ return method.getType().equals(fieldType);
}
return false;
}
- public static String deriveGetterMethodName(JavaType.Variable fieldType) {
- String fieldName = fieldType.getName();
- if (fieldType.getType() == JavaType.Variable.Primitive.Boolean) {
+ static String deriveGetterMethodName(JavaType type, String fieldName) {
+ if (type == JavaType.Primitive.Boolean) {
boolean alreadyStartsWithIs = fieldName.length() >= 3 &&
fieldName.substring(0, 3).matches("is[A-Z]");
if (alreadyStartsWithIs) {
@@ -62,7 +68,7 @@ public static String deriveGetterMethodName(JavaType.Variable fieldType) {
return "get" + StringUtils.capitalize(fieldName);
}
- public static AccessLevel getAccessLevel(Collection modifiers) {
+ static AccessLevel getAccessLevel(Collection modifiers) {
Map map = ImmutableMap.builder()
.put(Public, PUBLIC)
.put(Protected, PROTECTED)
diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokGetter.java b/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokGetter.java
index fdc9ed8250..062d4178d8 100644
--- a/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokGetter.java
+++ b/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokGetter.java
@@ -25,6 +25,7 @@
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.JavaParser;
import org.openrewrite.java.JavaTemplate;
+import org.openrewrite.java.tree.Expression;
import org.openrewrite.java.tree.J;
import java.util.Collections;
@@ -92,12 +93,23 @@ public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, Ex
public J.@Nullable MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) {
if (method.getMethodType() != null && LombokUtils.isEffectivelyGetter(method)) {
J.Return return_ = (J.Return) method.getBody().getStatements().get(0);
- Variable fieldType = ((J.Identifier) return_.getExpression()).getFieldType();
- boolean nameMatch = method.getSimpleName().equals(LombokUtils.deriveGetterMethodName(fieldType));
- if (nameMatch) {
- ((Set) getCursor().getNearestMessage(FIELDS_TO_DECORATE_KEY))
- .add(new Finding(fieldType.getName(), LombokUtils.getAccessLevel(method.getModifiers())));
- return null; //delete
+ Expression returnExpression = return_.getExpression();
+ if (returnExpression instanceof J.Identifier) {
+ J.Identifier identifier = (J.Identifier) returnExpression;
+ String deriveGetterMethodName = LombokUtils.deriveGetterMethodName(identifier.getType(), identifier.getSimpleName());
+ if (method.getSimpleName().equals(deriveGetterMethodName)) {
+ ((Set) getCursor().getNearestMessage(FIELDS_TO_DECORATE_KEY))
+ .add(new Finding(identifier.getSimpleName(), LombokUtils.getAccessLevel(method.getModifiers())));
+ return null;
+ }
+ } else if (returnExpression instanceof J.FieldAccess) {
+ J.FieldAccess fieldAccess = (J.FieldAccess) returnExpression;
+ String deriveGetterMethodName = LombokUtils.deriveGetterMethodName(fieldAccess.getType(), fieldAccess.getSimpleName());
+ if (method.getSimpleName().equals(deriveGetterMethodName)) {
+ ((Set) getCursor().getNearestMessage(FIELDS_TO_DECORATE_KEY))
+ .add(new Finding(fieldAccess.getSimpleName(), LombokUtils.getAccessLevel(method.getModifiers())));
+ return null;
+ }
}
}
return method;
diff --git a/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokGetterTest.java b/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokGetterTest.java
index 748fff7cf3..77074fe6cf 100644
--- a/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokGetterTest.java
+++ b/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokGetterTest.java
@@ -57,6 +57,33 @@ class A {
);
}
+ @Test
+ void replaceGetterWithFieldAccess() {
+ rewriteRun(// language=java
+ java(
+ """
+ class A {
+
+ int foo = 9;
+
+ public int getFoo() {
+ return this.foo;
+ }
+ }
+ """,
+ """
+ import lombok.Getter;
+
+ class A {
+
+ @Getter
+ int foo = 9;
+ }
+ """
+ )
+ );
+ }
+
@Test
void replacePackageGetter() {
rewriteRun(// language=java
From c7d3febbb28b310043e5e0dc56c2aa979d12869c Mon Sep 17 00:00:00 2001
From: Tim te Beek
Date: Mon, 9 Dec 2024 11:00:46 +0100
Subject: [PATCH 10/31] Push down method and variable name matching into utils
---
.../java/migrate/lombok/LombokUtils.java | 19 ++++++++-----
.../java/migrate/lombok/UseLombokGetter.java | 27 +++++++------------
.../migrate/lombok/UseLombokGetterTest.java | 2 +-
3 files changed, 24 insertions(+), 24 deletions(-)
diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java b/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
index 179c0094b0..e2eba1332b 100644
--- a/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
+++ b/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
@@ -17,6 +17,7 @@
import com.google.common.collect.ImmutableMap;
import lombok.AccessLevel;
+import org.jspecify.annotations.Nullable;
import org.openrewrite.internal.StringUtils;
import org.openrewrite.java.tree.Expression;
import org.openrewrite.java.tree.J;
@@ -41,21 +42,27 @@ static boolean isEffectivelyGetter(J.MethodDeclaration method) {
!(method.getBody().getStatements().get(0) instanceof J.Return)) {
return false;
}
+ // Check return: type and matching field name
Expression returnExpression = ((J.Return) method.getBody().getStatements().get(0)).getExpression();
- //returns just an identifier
if (returnExpression instanceof J.Identifier) {
J.Identifier identifier = (J.Identifier) returnExpression;
- JavaType.Variable fieldType = identifier.getFieldType();
- return method.getType().equals(fieldType.getType());
+ return hasMatchingTypeAndName(method, identifier.getType(), identifier.getSimpleName());
} else if (returnExpression instanceof J.FieldAccess) {
J.FieldAccess fieldAccess = (J.FieldAccess) returnExpression;
- JavaType fieldType = fieldAccess.getType();
- return method.getType().equals(fieldType);
+ return hasMatchingTypeAndName(method, fieldAccess.getType(), fieldAccess.getSimpleName());
}
return false;
}
- static String deriveGetterMethodName(JavaType type, String fieldName) {
+ private static boolean hasMatchingTypeAndName(J.MethodDeclaration method, @Nullable JavaType type, String simpleName) {
+ if (method.getType().equals(type)) {
+ String deriveGetterMethodName = deriveGetterMethodName(type, simpleName);
+ return method.getSimpleName().equals(deriveGetterMethodName);
+ }
+ return false;
+ }
+
+ private static String deriveGetterMethodName(@Nullable JavaType type, String fieldName) {
if (type == JavaType.Primitive.Boolean) {
boolean alreadyStartsWithIs = fieldName.length() >= 3 &&
fieldName.substring(0, 3).matches("is[A-Z]");
diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokGetter.java b/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokGetter.java
index 062d4178d8..655a6e271f 100644
--- a/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokGetter.java
+++ b/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokGetter.java
@@ -34,7 +34,6 @@
import java.util.Set;
import static java.util.Comparator.comparing;
-import static org.openrewrite.java.tree.JavaType.Variable;
@Value
@EqualsAndHashCode(callSuper = false)
@@ -92,24 +91,18 @@ public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, Ex
@Override
public J.@Nullable MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) {
if (method.getMethodType() != null && LombokUtils.isEffectivelyGetter(method)) {
- J.Return return_ = (J.Return) method.getBody().getStatements().get(0);
- Expression returnExpression = return_.getExpression();
+ Set set = getCursor().getNearestMessage(FIELDS_TO_DECORATE_KEY);
+ Expression returnExpression = ((J.Return) method.getBody().getStatements().get(0)).getExpression();
if (returnExpression instanceof J.Identifier) {
- J.Identifier identifier = (J.Identifier) returnExpression;
- String deriveGetterMethodName = LombokUtils.deriveGetterMethodName(identifier.getType(), identifier.getSimpleName());
- if (method.getSimpleName().equals(deriveGetterMethodName)) {
- ((Set) getCursor().getNearestMessage(FIELDS_TO_DECORATE_KEY))
- .add(new Finding(identifier.getSimpleName(), LombokUtils.getAccessLevel(method.getModifiers())));
- return null;
- }
+ set.add(new Finding(
+ ((J.Identifier) returnExpression).getSimpleName(),
+ LombokUtils.getAccessLevel(method.getModifiers())));
+ return null;
} else if (returnExpression instanceof J.FieldAccess) {
- J.FieldAccess fieldAccess = (J.FieldAccess) returnExpression;
- String deriveGetterMethodName = LombokUtils.deriveGetterMethodName(fieldAccess.getType(), fieldAccess.getSimpleName());
- if (method.getSimpleName().equals(deriveGetterMethodName)) {
- ((Set) getCursor().getNearestMessage(FIELDS_TO_DECORATE_KEY))
- .add(new Finding(fieldAccess.getSimpleName(), LombokUtils.getAccessLevel(method.getModifiers())));
- return null;
- }
+ set.add(new Finding(
+ ((J.FieldAccess) returnExpression).getSimpleName(),
+ LombokUtils.getAccessLevel(method.getModifiers())));
+ return null;
}
}
return method;
diff --git a/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokGetterTest.java b/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokGetterTest.java
index 77074fe6cf..90ea33c0a4 100644
--- a/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokGetterTest.java
+++ b/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokGetterTest.java
@@ -248,7 +248,7 @@ class A {
public A() {
}
- long getfoo() {//return type wrong
+ long getFoo() { //return type wrong
return foo;
}
}
From f51e00f950f167aa32fc6770fdbff96e13d6ad4f Mon Sep 17 00:00:00 2001
From: Tim te Beek
Date: Mon, 9 Dec 2024 11:09:42 +0100
Subject: [PATCH 11/31] Demonstrate failing case of nested inner class getter
---
.../migrate/lombok/UseLombokGetterTest.java | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokGetterTest.java b/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokGetterTest.java
index 90ea33c0a4..1f8da6bbf7 100644
--- a/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokGetterTest.java
+++ b/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokGetterTest.java
@@ -481,4 +481,23 @@ Boolean isFoo() {
)
);
}
+
+ @Test
+ void noChangeNestedClassGetter() {
+ rewriteRun(// language=java
+ java(
+ """
+ class Outer {
+ int foo = 9;
+
+ class Inner {
+ public int getFoo() {
+ return foo;
+ }
+ }
+ }
+ """
+ )
+ );
+ }
}
From 80ec54875fe93870550b6d23a93f44e1f01e7c08 Mon Sep 17 00:00:00 2001
From: timo <1398557+timo-a@users.noreply.github.com>
Date: Tue, 10 Dec 2024 23:00:07 +0100
Subject: [PATCH 12/31] fix: year in licence header
had copy-pasted from the example recipe
---
.../org/openrewrite/java/migrate/lombok/UseLombokGetter.java | 2 +-
.../openrewrite/java/migrate/lombok/UseLombokGetterTest.java | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokGetter.java b/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokGetter.java
index 655a6e271f..23dc2a0af4 100644
--- a/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokGetter.java
+++ b/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokGetter.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 the original author or authors.
+ * Copyright 2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokGetterTest.java b/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokGetterTest.java
index 1f8da6bbf7..f11556ae7d 100644
--- a/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokGetterTest.java
+++ b/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokGetterTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 the original author or authors.
+ * Copyright 2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
From 0bb8f7367b2d8702be25475cc7264e2728f4aac9 Mon Sep 17 00:00:00 2001
From: timo <1398557+timo-a@users.noreply.github.com>
Date: Tue, 10 Dec 2024 22:47:14 +0100
Subject: [PATCH 13/31] migrate existing recipe as-is
---
.../java/migrate/lombok/ConvertSetter.java | 172 +++++++
.../java/migrate/lombok/LombokUtils.java | 40 ++
.../migrate/lombok/ConvertSetterTest.java | 471 ++++++++++++++++++
3 files changed, 683 insertions(+)
create mode 100644 src/main/java/org/openrewrite/java/migrate/lombok/ConvertSetter.java
create mode 100644 src/test/java/org/openrewrite/java/migrate/lombok/ConvertSetterTest.java
diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/ConvertSetter.java b/src/main/java/org/openrewrite/java/migrate/lombok/ConvertSetter.java
new file mode 100644
index 0000000000..1b98055d53
--- /dev/null
+++ b/src/main/java/org/openrewrite/java/migrate/lombok/ConvertSetter.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2021 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.migrate.lombok;
+
+import lombok.AccessLevel;
+import lombok.EqualsAndHashCode;
+import lombok.Value;
+import org.openrewrite.ExecutionContext;
+import org.openrewrite.Recipe;
+import org.openrewrite.TreeVisitor;
+import org.openrewrite.java.JavaIsoVisitor;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.java.JavaTemplate;
+import org.openrewrite.java.tree.J;
+
+import java.util.HashSet;
+import java.util.Optional;
+import java.util.Set;
+import java.util.StringJoiner;
+
+import static java.util.Comparator.comparing;
+import static org.openrewrite.java.tree.JavaType.Variable;
+
+@Value
+@EqualsAndHashCode(callSuper = false)
+public class ConvertSetter extends Recipe {
+
+ @Override
+ public String getDisplayName() {
+ //language=markdown
+ return "Convert setter methods to annotations";
+ }
+
+ @Override
+ public String getDescription() {
+ //language=markdown
+ return new StringJoiner("\n")
+ .add("Convert trivial setter methods to `@Setter` annotations on their respective fields.")
+ .add("")
+ .add("Limitations:")
+ .add("")
+ .add(" - Does not add a dependency to Lombok, users need to do that manually")
+ .add(" - Ignores fields that are declared on the same line as others, e.g. `private int foo, bar;" +
+ "Users who have such fields are advised to separate them beforehand with " +
+ "[org.openrewrite.staticanalysis.MultipleVariableDeclaration]" +
+ "(https://docs.openrewrite.org/recipes/staticanalysis/multiplevariabledeclarations).")
+ .add(" - Does not offer any of the configuration keys listed in https://projectlombok.org/features/GetterSetter.")
+ .toString();
+ }
+
+ @Override
+ public TreeVisitor, ExecutionContext> getVisitor() {
+ return new MethodRemover();
+ }
+
+
+ @Value
+ @EqualsAndHashCode(callSuper = false)
+ private static class MethodRemover extends JavaIsoVisitor {
+ private static final String FIELDS_TO_DECORATE_KEY = "FIELDS_TO_DECORATE";
+
+ @Override
+ public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) {
+
+ //initialize set of fields to annotate
+ getCursor().putMessage(FIELDS_TO_DECORATE_KEY, new HashSet());
+
+ //delete methods, note down corresponding fields
+ J.ClassDeclaration classDeclAfterVisit = super.visitClassDeclaration(classDecl, ctx);
+
+ //only thing that can have changed is removal of setter methods
+ if (classDeclAfterVisit != classDecl) {
+ //this set collects the fields for which existing methods have already been removed
+ Set fieldsToDecorate = getCursor().pollNearestMessage(FIELDS_TO_DECORATE_KEY);
+ doAfterVisit(new FieldAnnotator(fieldsToDecorate));
+ }
+ return classDeclAfterVisit;
+ }
+
+ @Override
+ public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) {
+ assert method.getMethodType() != null;
+
+ if (LombokUtils.isEffectivelySetter(method)) {
+ J.Assignment assignment_ = (J.Assignment) method.getBody().getStatements().get(0);
+ J.FieldAccess fieldAccess = (J.FieldAccess) assignment_.getVariable();
+
+ Variable fieldType = fieldAccess.getName().getFieldType();
+ boolean nameMatch = method.getSimpleName().equals(LombokUtils.deriveSetterMethodName(fieldType));
+ if (nameMatch){
+ ((Set) getCursor().getNearestMessage(FIELDS_TO_DECORATE_KEY))
+ .add(new Finding(fieldType.getName(), LombokUtils.getAccessLevel(method.getModifiers())));
+ return null; //delete
+ }
+ }
+ return method;
+ }
+ }
+
+ @Value
+ private static class Finding {
+ String fieldName;
+ AccessLevel accessLevel;
+ }
+
+
+ @Value
+ @EqualsAndHashCode(callSuper = false)
+ static class FieldAnnotator extends JavaIsoVisitor{
+
+ Set fieldsToDecorate;
+
+ private JavaTemplate getAnnotation(AccessLevel accessLevel) {
+ JavaTemplate.Builder builder = AccessLevel.PUBLIC.equals(accessLevel)
+ ? JavaTemplate.builder("@Setter\n")
+ : JavaTemplate.builder("@Setter(AccessLevel." + accessLevel.name() + ")\n")
+ .imports("lombok.AccessLevel");
+
+ return builder
+ .imports("lombok.Setter")
+ .javaParser(JavaParser.fromJavaVersion()
+ .classpath("lombok"))
+ .build();
+ }
+
+ @Override
+ public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations multiVariable, ExecutionContext ctx) {
+
+ //we accept only one var decl per line, see description
+ if (multiVariable.getVariables().size() > 1) {
+ return multiVariable;
+ }
+
+ //we only want to annotate fields and not e.g. method parameters, so we require a lass declaration to be close in the cursor.
+ if (getCursor().getPathAsStream().limit( 4 ).noneMatch( e -> e instanceof J.ClassDeclaration )) {
+ return multiVariable;
+ }
+
+ J.VariableDeclarations.NamedVariable variable = multiVariable.getVariables().get(0);
+ Optional field = fieldsToDecorate.stream()
+ .filter(f -> f.fieldName.equals(variable.getSimpleName()))
+ .findFirst();
+
+ if (!field.isPresent()) {
+ return multiVariable; //not the field we are looking for
+ }
+
+ //remove it from list
+ //fieldsToDecorate.remove(field.get());
+
+ J.VariableDeclarations annotated = getAnnotation(field.get().getAccessLevel()).apply(
+ getCursor(),
+ multiVariable.getCoordinates().addAnnotation(comparing(J.Annotation::getSimpleName)));
+ maybeAddImport("lombok.Setter");
+ maybeAddImport("lombok.AccessLevel");
+ return annotated;
+ }
+ }
+}
diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java b/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
index e2eba1332b..043409dcff 100644
--- a/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
+++ b/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
@@ -22,9 +22,12 @@
import org.openrewrite.java.tree.Expression;
import org.openrewrite.java.tree.J;
import org.openrewrite.java.tree.JavaType;
+import org.openrewrite.java.tree.Statement;
import java.util.Collection;
+import java.util.List;
import java.util.Map;
+import java.util.stream.Collectors;
import static lombok.AccessLevel.*;
import static org.openrewrite.java.tree.J.Modifier.Type.*;
@@ -54,6 +57,39 @@ static boolean isEffectivelyGetter(J.MethodDeclaration method) {
return false;
}
+ public static boolean isEffectivelySetter(J.MethodDeclaration method) {
+ boolean isVoid = "void".equals(method.getType().toString());
+ List actualParameters = method.getParameters().stream()
+ .filter(s -> !(s instanceof J.Empty))
+ .collect(Collectors.toList());
+ boolean oneParam = actualParameters.size() == 1;
+ if (!isVoid || !oneParam)
+ return false;
+
+ J.VariableDeclarations variableDeclarations = (J.VariableDeclarations) actualParameters.get(0);
+ J.VariableDeclarations.NamedVariable param = variableDeclarations.getVariables().get(0);
+ String paramName = param.getName().toString();
+
+ boolean singularStatement = method.getBody() != null //abstract methods can be null
+ && method.getBody().getStatements().size() == 1
+ && method.getBody().getStatements().get(0) instanceof J.Assignment;
+
+ if (!singularStatement) {
+ return false;
+ }
+ J.Assignment assignment = (J.Assignment) method.getBody().getStatements().get(0);
+
+ J.FieldAccess fieldAccess = (J.FieldAccess) assignment.getVariable();
+
+ return
+ // assigned value is exactly the parameter
+ assignment.getAssignment().toString().equals(paramName)
+
+ // type of parameter and field have to match
+ && param.getType().equals(fieldAccess.getType());
+
+ }
+
private static boolean hasMatchingTypeAndName(J.MethodDeclaration method, @Nullable JavaType type, String simpleName) {
if (method.getType().equals(type)) {
String deriveGetterMethodName = deriveGetterMethodName(type, simpleName);
@@ -75,6 +111,10 @@ private static String deriveGetterMethodName(@Nullable JavaType type, String fie
return "get" + StringUtils.capitalize(fieldName);
}
+ public static String deriveSetterMethodName(JavaType.Variable fieldType) {
+ return "set" + StringUtils.capitalize(fieldType.getName());
+ }
+
static AccessLevel getAccessLevel(Collection modifiers) {
Map map = ImmutableMap.builder()
.put(Public, PUBLIC)
diff --git a/src/test/java/org/openrewrite/java/migrate/lombok/ConvertSetterTest.java b/src/test/java/org/openrewrite/java/migrate/lombok/ConvertSetterTest.java
new file mode 100644
index 0000000000..2b9719dc54
--- /dev/null
+++ b/src/test/java/org/openrewrite/java/migrate/lombok/ConvertSetterTest.java
@@ -0,0 +1,471 @@
+/*
+ * Copyright 2021 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.migrate.lombok;
+
+import org.junit.jupiter.api.Test;
+import org.openrewrite.DocumentExample;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+// This is a test for the ConvertToNoArgsConstructor recipe, as an example of how to write a test for an imperative recipe.
+class ConvertSetterTest implements RewriteTest {
+
+ // Note, you can define defaults for the RecipeSpec and these defaults will be used for all tests.
+ // In this case, the recipe and the parser are common. See below, on how the defaults can be overridden
+ // per test.
+ @Override
+ public void defaults(RecipeSpec spec) {
+ spec.recipe(new ConvertSetter())
+ .parser(JavaParser.fromJavaVersion()
+ .logCompilationWarningsAndErrors(true));
+ }
+
+ int foo;
+
+ @DocumentExample
+ @Test
+ void replaceSetter() {
+ rewriteRun(// language=java
+ java(
+ """
+ class A {
+
+ int foo = 9;
+
+ public void setFoo(int foo) {
+ this.foo = foo;
+ }
+ }
+ """,
+ """
+ import lombok.Setter;
+
+ class A {
+
+ @Setter
+ int foo = 9;
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void tolerantToNonstandardParameterNames() {
+ rewriteRun(// language=java
+ java(
+ """
+ class A {
+
+ int foo = 9;
+
+ public void setFoo(int fub) {
+ this.foo = fub;
+ }
+ }
+ """,
+ """
+ import lombok.Setter;
+
+ class A {
+
+ @Setter
+ int foo = 9;
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void replacePackageSetter() {
+ rewriteRun(// language=java
+ java(
+ """
+ class A {
+
+ int foo = 9;
+
+ void setFoo(int foo) {
+ this.foo = foo;
+ }
+ }
+ """,
+ """
+ import lombok.AccessLevel;
+ import lombok.Setter;
+
+ class A {
+
+ @Setter(AccessLevel.PACKAGE)
+ int foo = 9;
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void replaceProtectedSetter() {
+ rewriteRun(// language=java
+ java(
+ """
+ class A {
+
+ int foo = 9;
+
+ protected void setFoo(int foo) {
+ this.foo = foo;
+ }
+ }
+ """,
+ """
+ import lombok.AccessLevel;
+ import lombok.Setter;
+
+ class A {
+
+ @Setter(AccessLevel.PROTECTED)
+ int foo = 9;
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void replacePrivateSetter() {
+ rewriteRun(// language=java
+ java(
+ """
+ class A {
+
+ int foo = 9;
+
+ private void setFoo(int foo) {
+ this.foo = foo;
+ }
+ }
+ """,
+ """
+ import lombok.AccessLevel;
+ import lombok.Setter;
+
+ class A {
+
+ @Setter(AccessLevel.PRIVATE)
+ int foo = 9;
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void replaceJustTheMatchingSetter() {
+ rewriteRun(// language=java
+ java(
+ """
+ class A {
+
+ int foo = 9;
+
+ int ba;
+
+ public A() {
+ ba = 1;
+ }
+
+ public void setFoo(int foo) {
+ this.foo = foo;
+ }
+
+ public void setMoo(int foo) {//method name wrong
+ this.foo = foo;
+ }
+ }
+ """,
+ """
+ import lombok.Setter;
+
+ class A {
+
+ @Setter
+ int foo = 9;
+
+ int ba;
+
+ public A() {
+ ba = 1;
+ }
+
+ public void setMoo(int foo) {//method name wrong
+ this.foo = foo;
+ }
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void noChangeWhenMethodNameDoesntMatch() {
+ rewriteRun(// language=java
+ java(
+ """
+ class A {
+
+ int foo = 9;
+
+ public A() {
+ }
+
+ public void setfoo(int foo) {//method name wrong
+ this.foo = foo;
+ }
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void noChangeWhenParameterTypeDoesntMatch() {
+ rewriteRun(// language=java
+ java(
+ """
+ class A {
+
+ int foo = 9;
+
+ public A() {
+ }
+
+ public void setFoo(long foo) {//parameter type wrong
+ this.foo = (int) foo;
+ }
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void noChangeWhenFieldIsNotAssigned() {
+ rewriteRun(// language=java
+ java(
+ """
+ class A {
+
+ int foo = 9;
+ int ba = 10;
+
+ public A() {
+ }
+
+ public void setFoo(int foo) {
+ int foo = foo;
+ }
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void noChangeWhenDifferentFieldIsAssigned() {
+ rewriteRun(// language=java
+ java(
+ """
+ class A {
+
+ int foo = 9;
+ int ba = 10;
+
+ public A() {
+ }
+
+ public void setFoo(int foo) {
+ this.ba = foo; //assigns wrong variable
+ }
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void noChangeWhenSideEffects1() {
+ rewriteRun(// language=java
+ java(
+ """
+ class A {
+
+ int foo = 9;
+ int ba = 10;
+
+ public A() {
+ }
+
+ public void setFoo(int foo) {
+ foo++;//does extra stuff
+ this.foo = foo;
+ }
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void replacePrimitiveBoolean() {
+ rewriteRun(// language=java
+ java(
+ """
+ class A {
+
+ boolean foo = true;
+
+ public void setFoo(boolean foo) {
+ this.foo = foo;
+ }
+ }
+ """,
+ """
+ import lombok.Setter;
+
+ class A {
+
+ @Setter
+ boolean foo = true;
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void replaceBoolean() {
+ rewriteRun(// language=java
+ java(
+ """
+ class A {
+
+ Boolean foo = true;
+
+ public void setFoo(Boolean foo) {
+ this.foo = foo;
+ }
+ }
+ """,
+ """
+ import lombok.Setter;
+
+ class A {
+
+ @Setter
+ Boolean foo = true;
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void annotateOnlyFields() {
+ rewriteRun(// language=java
+ java(
+ """
+ class A {
+
+ int foo = 9;
+
+ public void setFoo(int foo) {
+ this.foo = foo;
+ }
+
+ public void unrelated1(int foo) {
+ }
+ public void unrelated2() {
+ int foo = 10;
+ }
+ }
+ """,
+ """
+ import lombok.Setter;
+
+ class A {
+
+ @Setter
+ int foo = 9;
+
+ public void unrelated1(int foo) {
+ }
+ public void unrelated2() {
+ int foo = 10;
+ }
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void annotateOnlyFields2() {
+ rewriteRun(// language=java
+ java(
+ """
+ class A {
+
+ public void setFoo(int foo) {
+ this.foo = foo;
+ }
+
+ public void unrelated1(int foo) {
+ }
+ public void unrelated2() {
+ int foo = 10;
+ }
+
+ int foo = 9;
+ }
+ """,
+ """
+ import lombok.Setter;
+
+ class A {
+
+ public void unrelated1(int foo) {
+ }
+ public void unrelated2() {
+ int foo = 10;
+ }
+
+ @Setter
+ int foo = 9;
+ }
+ """
+ )
+ );
+ }
+
+
+
+
+}
From 5e9e379396d0954a681b97cbd80d89f4cc510b04 Mon Sep 17 00:00:00 2001
From: timo <1398557+timo-a@users.noreply.github.com>
Date: Tue, 10 Dec 2024 22:48:17 +0100
Subject: [PATCH 14/31] deactivate getter test for development
---
.../openrewrite/java/migrate/lombok/UseLombokGetterTest.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokGetterTest.java b/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokGetterTest.java
index f11556ae7d..59c2def8dd 100644
--- a/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokGetterTest.java
+++ b/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokGetterTest.java
@@ -482,7 +482,7 @@ Boolean isFoo() {
);
}
- @Test
+ //@Test todo activate again
void noChangeNestedClassGetter() {
rewriteRun(// language=java
java(
From 7510f63a5b3ab6763d5f01e0ef6392cdeede8a38 Mon Sep 17 00:00:00 2001
From: timo <1398557+timo-a@users.noreply.github.com>
Date: Tue, 10 Dec 2024 22:55:36 +0100
Subject: [PATCH 15/31] Rename and add Lombok tag
---
.../{ConvertSetter.java => UseLombokSetter.java} | 12 +++++++-----
...nvertSetterTest.java => UseLombokSetterTest.java} | 4 ++--
2 files changed, 9 insertions(+), 7 deletions(-)
rename src/main/java/org/openrewrite/java/migrate/lombok/{ConvertSetter.java => UseLombokSetter.java} (97%)
rename src/test/java/org/openrewrite/java/migrate/lombok/{ConvertSetterTest.java => UseLombokSetterTest.java} (99%)
diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/ConvertSetter.java b/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokSetter.java
similarity index 97%
rename from src/main/java/org/openrewrite/java/migrate/lombok/ConvertSetter.java
rename to src/main/java/org/openrewrite/java/migrate/lombok/UseLombokSetter.java
index 1b98055d53..722215d7c7 100644
--- a/src/main/java/org/openrewrite/java/migrate/lombok/ConvertSetter.java
+++ b/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokSetter.java
@@ -26,17 +26,14 @@
import org.openrewrite.java.JavaTemplate;
import org.openrewrite.java.tree.J;
-import java.util.HashSet;
-import java.util.Optional;
-import java.util.Set;
-import java.util.StringJoiner;
+import java.util.*;
import static java.util.Comparator.comparing;
import static org.openrewrite.java.tree.JavaType.Variable;
@Value
@EqualsAndHashCode(callSuper = false)
-public class ConvertSetter extends Recipe {
+public class UseLombokSetter extends Recipe {
@Override
public String getDisplayName() {
@@ -61,6 +58,11 @@ public String getDescription() {
.toString();
}
+ @Override
+ public Set getTags() {
+ return Collections.singleton("lombok");
+ }
+
@Override
public TreeVisitor, ExecutionContext> getVisitor() {
return new MethodRemover();
diff --git a/src/test/java/org/openrewrite/java/migrate/lombok/ConvertSetterTest.java b/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokSetterTest.java
similarity index 99%
rename from src/test/java/org/openrewrite/java/migrate/lombok/ConvertSetterTest.java
rename to src/test/java/org/openrewrite/java/migrate/lombok/UseLombokSetterTest.java
index 2b9719dc54..680db52b79 100644
--- a/src/test/java/org/openrewrite/java/migrate/lombok/ConvertSetterTest.java
+++ b/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokSetterTest.java
@@ -24,14 +24,14 @@
import static org.openrewrite.java.Assertions.java;
// This is a test for the ConvertToNoArgsConstructor recipe, as an example of how to write a test for an imperative recipe.
-class ConvertSetterTest implements RewriteTest {
+class UseLombokSetterTest implements RewriteTest {
// Note, you can define defaults for the RecipeSpec and these defaults will be used for all tests.
// In this case, the recipe and the parser are common. See below, on how the defaults can be overridden
// per test.
@Override
public void defaults(RecipeSpec spec) {
- spec.recipe(new ConvertSetter())
+ spec.recipe(new UseLombokSetter())
.parser(JavaParser.fromJavaVersion()
.logCompilationWarningsAndErrors(true));
}
From 46519ddd5e9b53f2121934e61b608dd5f5bf7998 Mon Sep 17 00:00:00 2001
From: timo <1398557+timo-a@users.noreply.github.com>
Date: Tue, 10 Dec 2024 22:57:01 +0100
Subject: [PATCH 16/31] fix year in licence header
---
.../org/openrewrite/java/migrate/lombok/UseLombokSetter.java | 2 +-
.../openrewrite/java/migrate/lombok/UseLombokSetterTest.java | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokSetter.java b/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokSetter.java
index 722215d7c7..4ab44f991c 100644
--- a/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokSetter.java
+++ b/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokSetter.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 the original author or authors.
+ * Copyright 2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokSetterTest.java b/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokSetterTest.java
index 680db52b79..33936b5303 100644
--- a/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokSetterTest.java
+++ b/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokSetterTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 the original author or authors.
+ * Copyright 2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
From 9bba4fe3c8f2ba1b3a361a7262715fd2eaabb964 Mon Sep 17 00:00:00 2001
From: timo <1398557+timo-a@users.noreply.github.com>
Date: Tue, 10 Dec 2024 22:58:33 +0100
Subject: [PATCH 17/31] chore: IntelliJ auto-formatter
---
.../openrewrite/java/migrate/lombok/UseLombokSetter.java | 6 +++---
.../java/migrate/lombok/UseLombokSetterTest.java | 2 --
2 files changed, 3 insertions(+), 5 deletions(-)
diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokSetter.java b/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokSetter.java
index 4ab44f991c..65c21304ea 100644
--- a/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokSetter.java
+++ b/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokSetter.java
@@ -102,7 +102,7 @@ public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, Ex
Variable fieldType = fieldAccess.getName().getFieldType();
boolean nameMatch = method.getSimpleName().equals(LombokUtils.deriveSetterMethodName(fieldType));
- if (nameMatch){
+ if (nameMatch) {
((Set) getCursor().getNearestMessage(FIELDS_TO_DECORATE_KEY))
.add(new Finding(fieldType.getName(), LombokUtils.getAccessLevel(method.getModifiers())));
return null; //delete
@@ -121,7 +121,7 @@ private static class Finding {
@Value
@EqualsAndHashCode(callSuper = false)
- static class FieldAnnotator extends JavaIsoVisitor{
+ static class FieldAnnotator extends JavaIsoVisitor {
Set fieldsToDecorate;
@@ -147,7 +147,7 @@ public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations m
}
//we only want to annotate fields and not e.g. method parameters, so we require a lass declaration to be close in the cursor.
- if (getCursor().getPathAsStream().limit( 4 ).noneMatch( e -> e instanceof J.ClassDeclaration )) {
+ if (getCursor().getPathAsStream().limit(4).noneMatch(e -> e instanceof J.ClassDeclaration)) {
return multiVariable;
}
diff --git a/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokSetterTest.java b/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokSetterTest.java
index 33936b5303..8ab26f4aa1 100644
--- a/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokSetterTest.java
+++ b/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokSetterTest.java
@@ -466,6 +466,4 @@ public void unrelated2() {
}
-
-
}
From 7f4d48600dd9e2800bff27e0070c9d0c2bcab0bb Mon Sep 17 00:00:00 2001
From: timo <1398557+timo-a@users.noreply.github.com>
Date: Tue, 10 Dec 2024 23:23:17 +0100
Subject: [PATCH 18/31] apply best practices
---
.../org/openrewrite/java/migrate/lombok/LombokUtils.java | 9 +++++----
.../openrewrite/java/migrate/lombok/UseLombokSetter.java | 9 +++++----
2 files changed, 10 insertions(+), 8 deletions(-)
diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java b/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
index 043409dcff..2d741b3e96 100644
--- a/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
+++ b/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
@@ -71,8 +71,8 @@ public static boolean isEffectivelySetter(J.MethodDeclaration method) {
String paramName = param.getName().toString();
boolean singularStatement = method.getBody() != null //abstract methods can be null
- && method.getBody().getStatements().size() == 1
- && method.getBody().getStatements().get(0) instanceof J.Assignment;
+ && method.getBody().getStatements().size() == 1 &&
+ method.getBody().getStatements().get(0) instanceof J.Assignment;
if (!singularStatement) {
return false;
@@ -83,10 +83,11 @@ public static boolean isEffectivelySetter(J.MethodDeclaration method) {
return
// assigned value is exactly the parameter
- assignment.getAssignment().toString().equals(paramName)
+ assignment.getAssignment().toString().equals(paramName) // type of parameter and field have to match
+ &&
// type of parameter and field have to match
- && param.getType().equals(fieldAccess.getType());
+ param.getType().equals(fieldAccess.getType());
}
diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokSetter.java b/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokSetter.java
index 65c21304ea..c18807a1e8 100644
--- a/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokSetter.java
+++ b/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokSetter.java
@@ -18,6 +18,7 @@
import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.Value;
+import org.jspecify.annotations.Nullable;
import org.openrewrite.ExecutionContext;
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;
@@ -93,7 +94,7 @@ public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, Ex
}
@Override
- public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) {
+ public J.@Nullable MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) {
assert method.getMethodType() != null;
if (LombokUtils.isEffectivelySetter(method)) {
@@ -126,9 +127,9 @@ static class FieldAnnotator extends JavaIsoVisitor {
Set fieldsToDecorate;
private JavaTemplate getAnnotation(AccessLevel accessLevel) {
- JavaTemplate.Builder builder = AccessLevel.PUBLIC.equals(accessLevel)
- ? JavaTemplate.builder("@Setter\n")
- : JavaTemplate.builder("@Setter(AccessLevel." + accessLevel.name() + ")\n")
+ JavaTemplate.Builder builder = AccessLevel.PUBLIC.equals(accessLevel) ?
+ JavaTemplate.builder("@Setter\n") :
+ JavaTemplate.builder("@Setter(AccessLevel." + accessLevel.name() + ")\n")
.imports("lombok.AccessLevel");
return builder
From f68e252815523ad72909e7db2deef7c4521873d8 Mon Sep 17 00:00:00 2001
From: timo <1398557+timo-a@users.noreply.github.com>
Date: Tue, 10 Dec 2024 23:32:46 +0100
Subject: [PATCH 19/31] light polish
---
.../java/migrate/lombok/UseLombokSetter.java | 25 ++++++-------------
1 file changed, 8 insertions(+), 17 deletions(-)
diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokSetter.java b/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokSetter.java
index c18807a1e8..6ee613618e 100644
--- a/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokSetter.java
+++ b/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokSetter.java
@@ -38,25 +38,18 @@ public class UseLombokSetter extends Recipe {
@Override
public String getDisplayName() {
- //language=markdown
return "Convert setter methods to annotations";
}
@Override
public String getDescription() {
//language=markdown
- return new StringJoiner("\n")
- .add("Convert trivial setter methods to `@Setter` annotations on their respective fields.")
- .add("")
- .add("Limitations:")
- .add("")
- .add(" - Does not add a dependency to Lombok, users need to do that manually")
- .add(" - Ignores fields that are declared on the same line as others, e.g. `private int foo, bar;" +
- "Users who have such fields are advised to separate them beforehand with " +
- "[org.openrewrite.staticanalysis.MultipleVariableDeclaration]" +
- "(https://docs.openrewrite.org/recipes/staticanalysis/multiplevariabledeclarations).")
- .add(" - Does not offer any of the configuration keys listed in https://projectlombok.org/features/GetterSetter.")
- .toString();
+ return "Convert trivial setter methods to `@Setter` annotations on their respective fields.\n\n" +
+ "Limitations:\n\n" +
+ " - Does not add a dependency to Lombok, users need to do that manually\n" +
+ " - Ignores fields that are declared on the same line as others, e.g. `private int foo, bar; " +
+ "Users who have such fields are advised to separate them beforehand with [org.openrewrite.staticanalysis.MultipleVariableDeclaration](https://docs.openrewrite.org/recipes/staticanalysis/multiplevariabledeclarations).\n" +
+ " - Does not offer any of the configuration keys listed in https://projectlombok.org/features/GetterSetter.";
}
@Override
@@ -69,7 +62,6 @@ public TreeVisitor, ExecutionContext> getVisitor() {
return new MethodRemover();
}
-
@Value
@EqualsAndHashCode(callSuper = false)
private static class MethodRemover extends JavaIsoVisitor {
@@ -130,12 +122,11 @@ private JavaTemplate getAnnotation(AccessLevel accessLevel) {
JavaTemplate.Builder builder = AccessLevel.PUBLIC.equals(accessLevel) ?
JavaTemplate.builder("@Setter\n") :
JavaTemplate.builder("@Setter(AccessLevel." + accessLevel.name() + ")\n")
- .imports("lombok.AccessLevel");
+ .imports("lombok.AccessLevel");
return builder
.imports("lombok.Setter")
- .javaParser(JavaParser.fromJavaVersion()
- .classpath("lombok"))
+ .javaParser(JavaParser.fromJavaVersion().classpath("lombok"))
.build();
}
From 0700c201afc639688c2e99377b5ddf3f12387728 Mon Sep 17 00:00:00 2001
From: timo <1398557+timo-a@users.noreply.github.com>
Date: Tue, 10 Dec 2024 23:52:04 +0100
Subject: [PATCH 20/31] copy from: Also handle field access
---
.../java/org/openrewrite/java/migrate/lombok/LombokUtils.java | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java b/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
index 2d741b3e96..92d126052b 100644
--- a/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
+++ b/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
@@ -57,7 +57,7 @@ static boolean isEffectivelyGetter(J.MethodDeclaration method) {
return false;
}
- public static boolean isEffectivelySetter(J.MethodDeclaration method) {
+ static boolean isEffectivelySetter(J.MethodDeclaration method) {
boolean isVoid = "void".equals(method.getType().toString());
List actualParameters = method.getParameters().stream()
.filter(s -> !(s instanceof J.Empty))
@@ -112,7 +112,7 @@ private static String deriveGetterMethodName(@Nullable JavaType type, String fie
return "get" + StringUtils.capitalize(fieldName);
}
- public static String deriveSetterMethodName(JavaType.Variable fieldType) {
+ static String deriveSetterMethodName(JavaType.Variable fieldType) {
return "set" + StringUtils.capitalize(fieldType.getName());
}
From 60ee08f11f594ab80c1f9c8183c39c5f692fda50 Mon Sep 17 00:00:00 2001
From: timo <1398557+timo-a@users.noreply.github.com>
Date: Wed, 11 Dec 2024 00:14:15 +0100
Subject: [PATCH 21/31] minor changes
---
.../java/migrate/lombok/LombokUtils.java | 16 ++++++++--------
.../java/migrate/lombok/UseLombokSetter.java | 9 ++-------
.../java/migrate/lombok/UseLombokSetterTest.java | 2 --
3 files changed, 10 insertions(+), 17 deletions(-)
diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java b/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
index 92d126052b..49200ea1d4 100644
--- a/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
+++ b/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
@@ -57,6 +57,14 @@ static boolean isEffectivelyGetter(J.MethodDeclaration method) {
return false;
}
+ private static boolean hasMatchingTypeAndName(J.MethodDeclaration method, @Nullable JavaType type, String simpleName) {
+ if (method.getType().equals(type)) {
+ String deriveGetterMethodName = deriveGetterMethodName(type, simpleName);
+ return method.getSimpleName().equals(deriveGetterMethodName);
+ }
+ return false;
+ }
+
static boolean isEffectivelySetter(J.MethodDeclaration method) {
boolean isVoid = "void".equals(method.getType().toString());
List actualParameters = method.getParameters().stream()
@@ -91,14 +99,6 @@ static boolean isEffectivelySetter(J.MethodDeclaration method) {
}
- private static boolean hasMatchingTypeAndName(J.MethodDeclaration method, @Nullable JavaType type, String simpleName) {
- if (method.getType().equals(type)) {
- String deriveGetterMethodName = deriveGetterMethodName(type, simpleName);
- return method.getSimpleName().equals(deriveGetterMethodName);
- }
- return false;
- }
-
private static String deriveGetterMethodName(@Nullable JavaType type, String fieldName) {
if (type == JavaType.Primitive.Boolean) {
boolean alreadyStartsWithIs = fieldName.length() >= 3 &&
diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokSetter.java b/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokSetter.java
index 6ee613618e..f678ff9343 100644
--- a/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokSetter.java
+++ b/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokSetter.java
@@ -87,8 +87,6 @@ public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, Ex
@Override
public J.@Nullable MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) {
- assert method.getMethodType() != null;
-
if (LombokUtils.isEffectivelySetter(method)) {
J.Assignment assignment_ = (J.Assignment) method.getBody().getStatements().get(0);
J.FieldAccess fieldAccess = (J.FieldAccess) assignment_.getVariable();
@@ -96,8 +94,8 @@ public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, Ex
Variable fieldType = fieldAccess.getName().getFieldType();
boolean nameMatch = method.getSimpleName().equals(LombokUtils.deriveSetterMethodName(fieldType));
if (nameMatch) {
- ((Set) getCursor().getNearestMessage(FIELDS_TO_DECORATE_KEY))
- .add(new Finding(fieldType.getName(), LombokUtils.getAccessLevel(method.getModifiers())));
+ Set set = getCursor().getNearestMessage(FIELDS_TO_DECORATE_KEY);
+ set.add(new Finding(fieldType.getName(), LombokUtils.getAccessLevel(method.getModifiers())));
return null; //delete
}
}
@@ -152,9 +150,6 @@ public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations m
return multiVariable; //not the field we are looking for
}
- //remove it from list
- //fieldsToDecorate.remove(field.get());
-
J.VariableDeclarations annotated = getAnnotation(field.get().getAccessLevel()).apply(
getCursor(),
multiVariable.getCoordinates().addAnnotation(comparing(J.Annotation::getSimpleName)));
diff --git a/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokSetterTest.java b/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokSetterTest.java
index 8ab26f4aa1..33e02601f7 100644
--- a/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokSetterTest.java
+++ b/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokSetterTest.java
@@ -464,6 +464,4 @@ public void unrelated2() {
)
);
}
-
-
}
From d43806171455182fc1fb9f05a4bb618cf4456e45 Mon Sep 17 00:00:00 2001
From: Tim te Beek
Date: Sat, 14 Dec 2024 20:02:54 +0100
Subject: [PATCH 22/31] Minimize changes with `main` branch ahead of rebase to
avoid conflicts
---
.../java/migrate/lombok/LombokUtils.java | 85 ++++++------
.../java/migrate/lombok/UseLombokGetter.java | 126 +++++-------------
.../migrate/lombok/UseLombokGetterTest.java | 30 ++++-
3 files changed, 110 insertions(+), 131 deletions(-)
diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java b/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
index 49200ea1d4..3612bbc99f 100644
--- a/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
+++ b/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
@@ -15,7 +15,6 @@
*/
package org.openrewrite.java.migrate.lombok;
-import com.google.common.collect.ImmutableMap;
import lombok.AccessLevel;
import org.jspecify.annotations.Nullable;
import org.openrewrite.internal.StringUtils;
@@ -24,17 +23,18 @@
import org.openrewrite.java.tree.JavaType;
import org.openrewrite.java.tree.Statement;
-import java.util.Collection;
import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
+import static java.util.stream.Collectors.toList;
import static lombok.AccessLevel.*;
import static org.openrewrite.java.tree.J.Modifier.Type.*;
class LombokUtils {
- static boolean isEffectivelyGetter(J.MethodDeclaration method) {
+ static boolean isGetter(J.MethodDeclaration method) {
+ if (method.getMethodType() == null) {
+ return false;
+ }
// Check signature: no parameters
if (!(method.getParameters().get(0) instanceof J.Empty) || method.getReturnTypeExpression() == null) {
return false;
@@ -45,31 +45,65 @@ static boolean isEffectivelyGetter(J.MethodDeclaration method) {
!(method.getBody().getStatements().get(0) instanceof J.Return)) {
return false;
}
- // Check return: type and matching field name
+ // Check field is declared on method type
+ JavaType.FullyQualified declaringType = method.getMethodType().getDeclaringType();
Expression returnExpression = ((J.Return) method.getBody().getStatements().get(0)).getExpression();
if (returnExpression instanceof J.Identifier) {
J.Identifier identifier = (J.Identifier) returnExpression;
- return hasMatchingTypeAndName(method, identifier.getType(), identifier.getSimpleName());
+ if (identifier.getFieldType() != null && declaringType == identifier.getFieldType().getOwner()) {
+ // Check return: type and matching field name
+ return hasMatchingTypeAndName(method, identifier.getType(), identifier.getSimpleName());
+ }
} else if (returnExpression instanceof J.FieldAccess) {
J.FieldAccess fieldAccess = (J.FieldAccess) returnExpression;
- return hasMatchingTypeAndName(method, fieldAccess.getType(), fieldAccess.getSimpleName());
+ Expression target = fieldAccess.getTarget();
+ if (target instanceof J.Identifier && ((J.Identifier) target).getFieldType() != null &&
+ declaringType == ((J.Identifier) target).getFieldType().getOwner()) {
+ // Check return: type and matching field name
+ return hasMatchingTypeAndName(method, fieldAccess.getType(), fieldAccess.getSimpleName());
+ }
}
return false;
}
private static boolean hasMatchingTypeAndName(J.MethodDeclaration method, @Nullable JavaType type, String simpleName) {
- if (method.getType().equals(type)) {
+ if (method.getType() == type) {
String deriveGetterMethodName = deriveGetterMethodName(type, simpleName);
return method.getSimpleName().equals(deriveGetterMethodName);
}
return false;
}
+ private static String deriveGetterMethodName(@Nullable JavaType type, String fieldName) {
+ if (type == JavaType.Primitive.Boolean) {
+ boolean alreadyStartsWithIs = fieldName.length() >= 3 &&
+ fieldName.substring(0, 3).matches("is[A-Z]");
+ if (alreadyStartsWithIs) {
+ return fieldName;
+ } else {
+ return "is" + StringUtils.capitalize(fieldName);
+ }
+ }
+ return "get" + StringUtils.capitalize(fieldName);
+ }
+
+ static AccessLevel getAccessLevel(J.MethodDeclaration modifiers) {
+ if (modifiers.hasModifier(Public)) {
+ return PUBLIC;
+ } else if (modifiers.hasModifier(Protected)) {
+ return PROTECTED;
+ } else if (modifiers.hasModifier(Private)) {
+ return PRIVATE;
+ }
+ return PACKAGE;
+ }
+
+
static boolean isEffectivelySetter(J.MethodDeclaration method) {
boolean isVoid = "void".equals(method.getType().toString());
List actualParameters = method.getParameters().stream()
.filter(s -> !(s instanceof J.Empty))
- .collect(Collectors.toList());
+ .collect(toList());
boolean oneParam = actualParameters.size() == 1;
if (!isVoid || !oneParam)
return false;
@@ -98,35 +132,4 @@ static boolean isEffectivelySetter(J.MethodDeclaration method) {
param.getType().equals(fieldAccess.getType());
}
-
- private static String deriveGetterMethodName(@Nullable JavaType type, String fieldName) {
- if (type == JavaType.Primitive.Boolean) {
- boolean alreadyStartsWithIs = fieldName.length() >= 3 &&
- fieldName.substring(0, 3).matches("is[A-Z]");
- if (alreadyStartsWithIs) {
- return fieldName;
- } else {
- return "is" + StringUtils.capitalize(fieldName);
- }
- }
- return "get" + StringUtils.capitalize(fieldName);
- }
-
- static String deriveSetterMethodName(JavaType.Variable fieldType) {
- return "set" + StringUtils.capitalize(fieldType.getName());
- }
-
- static AccessLevel getAccessLevel(Collection modifiers) {
- Map map = ImmutableMap.builder()
- .put(Public, PUBLIC)
- .put(Protected, PROTECTED)
- .put(Private, PRIVATE)
- .build();
-
- return modifiers.stream()
- .map(modifier -> map.getOrDefault(modifier.getType(), AccessLevel.NONE))
- .filter(a -> a != AccessLevel.NONE)
- .findAny().orElse(AccessLevel.PACKAGE);
- }
-
}
diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokGetter.java b/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokGetter.java
index 23dc2a0af4..b8de2d23f4 100644
--- a/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokGetter.java
+++ b/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokGetter.java
@@ -27,13 +27,14 @@
import org.openrewrite.java.JavaTemplate;
import org.openrewrite.java.tree.Expression;
import org.openrewrite.java.tree.J;
+import org.openrewrite.java.tree.JavaType;
import java.util.Collections;
-import java.util.HashSet;
-import java.util.Optional;
+import java.util.List;
import java.util.Set;
import static java.util.Comparator.comparing;
+import static lombok.AccessLevel.PUBLIC;
@Value
@EqualsAndHashCode(callSuper = false)
@@ -47,12 +48,7 @@ public String getDisplayName() {
@Override
public String getDescription() {
//language=markdown
- return "Convert trivial getter methods to `@Getter` annotations on their respective fields.\n\n" +
- "Limitations:\n\n" +
- " - Does not add a dependency to Lombok, users need to do that manually\n" +
- " - Ignores fields that are declared on the same line as others, e.g. `private int foo, bar; " +
- "Users who have such fields are advised to separate them beforehand with [org.openrewrite.staticanalysis.MultipleVariableDeclaration](https://docs.openrewrite.org/recipes/staticanalysis/multiplevariabledeclarations).\n" +
- " - Does not offer any of the configuration keys listed in https://projectlombok.org/features/GetterSetter.";
+ return "Convert trivial getter methods to `@Getter` annotations on their respective fields.";
}
@Override
@@ -62,100 +58,52 @@ public Set getTags() {
@Override
public TreeVisitor, ExecutionContext> getVisitor() {
- return new MethodRemover();
- }
-
- @Value
- @EqualsAndHashCode(callSuper = false)
- private static class MethodRemover extends JavaIsoVisitor {
- private static final String FIELDS_TO_DECORATE_KEY = "FIELDS_TO_DECORATE";
-
- @Override
- public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) {
-
- //initialize set of fields to annotate
- getCursor().putMessage(FIELDS_TO_DECORATE_KEY, new HashSet());
-
- //delete methods, note down corresponding fields
- J.ClassDeclaration classDeclAfterVisit = super.visitClassDeclaration(classDecl, ctx);
-
- //only thing that can have changed is removal of getter methods
- if (classDeclAfterVisit != classDecl) {
- //this set collects the fields for which existing methods have already been removed
- Set fieldsToDecorate = getCursor().pollNearestMessage(FIELDS_TO_DECORATE_KEY);
- doAfterVisit(new FieldAnnotator(fieldsToDecorate));
- }
- return classDeclAfterVisit;
- }
-
- @Override
- public J.@Nullable MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) {
- if (method.getMethodType() != null && LombokUtils.isEffectivelyGetter(method)) {
- Set set = getCursor().getNearestMessage(FIELDS_TO_DECORATE_KEY);
- Expression returnExpression = ((J.Return) method.getBody().getStatements().get(0)).getExpression();
- if (returnExpression instanceof J.Identifier) {
- set.add(new Finding(
- ((J.Identifier) returnExpression).getSimpleName(),
- LombokUtils.getAccessLevel(method.getModifiers())));
- return null;
- } else if (returnExpression instanceof J.FieldAccess) {
- set.add(new Finding(
- ((J.FieldAccess) returnExpression).getSimpleName(),
- LombokUtils.getAccessLevel(method.getModifiers())));
- return null;
+ return new JavaIsoVisitor() {
+ @Override
+ public J.@Nullable MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) {
+ if (LombokUtils.isGetter(method)) {
+ Expression returnExpression = ((J.Return) method.getBody().getStatements().get(0)).getExpression();
+ if (returnExpression instanceof J.Identifier &&
+ ((J.Identifier) returnExpression).getFieldType() != null) {
+ doAfterVisit(new FieldAnnotator(
+ ((J.Identifier) returnExpression).getFieldType(),
+ LombokUtils.getAccessLevel(method)));
+ return null;
+ } else if (returnExpression instanceof J.FieldAccess &&
+ ((J.FieldAccess) returnExpression).getName().getFieldType() != null) {
+ doAfterVisit(new FieldAnnotator(
+ ((J.FieldAccess) returnExpression).getName().getFieldType(),
+ LombokUtils.getAccessLevel(method)));
+ return null;
+ }
}
+ return method;
}
- return method;
- }
+ };
}
- @Value
- private static class Finding {
- String fieldName;
- AccessLevel accessLevel;
- }
@Value
@EqualsAndHashCode(callSuper = false)
static class FieldAnnotator extends JavaIsoVisitor {
- Set fieldsToDecorate;
-
- private JavaTemplate getAnnotation(AccessLevel accessLevel) {
- JavaTemplate.Builder builder = AccessLevel.PUBLIC.equals(accessLevel) ?
- JavaTemplate.builder("@Getter\n") :
- JavaTemplate.builder("@Getter(AccessLevel." + accessLevel.name() + ")\n")
- .imports("lombok.AccessLevel");
-
- return builder
- .imports("lombok.Getter")
- .javaParser(JavaParser.fromJavaVersion().classpath("lombok"))
- .build();
- }
+ JavaType field;
+ AccessLevel accessLevel;
@Override
public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations multiVariable, ExecutionContext ctx) {
-
- //we accept only one var decl per line, see description
- if (multiVariable.getVariables().size() > 1) {
- return multiVariable;
- }
-
- J.VariableDeclarations.NamedVariable variable = multiVariable.getVariables().get(0);
- Optional field = fieldsToDecorate.stream()
- .filter(f -> f.fieldName.equals(variable.getSimpleName()))
- .findFirst();
-
- if (!field.isPresent()) {
- return multiVariable; //not the field we are looking for
+ for (J.VariableDeclarations.NamedVariable variable : multiVariable.getVariables()) {
+ if (variable.getName().getFieldType() == field) {
+ maybeAddImport("lombok.Getter");
+ maybeAddImport("lombok.AccessLevel");
+ String suffix = accessLevel == PUBLIC ? "" : String.format("(AccessLevel.%s)", accessLevel.name());
+ return JavaTemplate.builder("@Getter" + suffix)
+ .imports("lombok.Getter", "lombok.AccessLevel")
+ .javaParser(JavaParser.fromJavaVersion().classpath("lombok"))
+ .build().apply(getCursor(), multiVariable.getCoordinates().addAnnotation(comparing(J.Annotation::getSimpleName)));
+ }
}
-
- J.VariableDeclarations annotated = getAnnotation(field.get().getAccessLevel()).apply(
- getCursor(),
- multiVariable.getCoordinates().addAnnotation(comparing(J.Annotation::getSimpleName)));
- maybeAddImport("lombok.Getter");
- maybeAddImport("lombok.AccessLevel");
- return annotated;
+ return multiVariable;
}
}
}
diff --git a/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokGetterTest.java b/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokGetterTest.java
index 59c2def8dd..ec62019a51 100644
--- a/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokGetterTest.java
+++ b/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokGetterTest.java
@@ -84,6 +84,34 @@ class A {
);
}
+ @Test
+ void replaceGetterWithMultiVariable() {
+ // Technically this adds a new public getter not there previously, but we'll tolerate it
+ rewriteRun(// language=java
+ java(
+ """
+ class A {
+
+ int foo = 9, bar = 10;
+
+ public int getFoo() {
+ return foo;
+ }
+ }
+ """,
+ """
+ import lombok.Getter;
+
+ class A {
+
+ @Getter
+ int foo = 9, bar = 10;
+ }
+ """
+ )
+ );
+ }
+
@Test
void replacePackageGetter() {
rewriteRun(// language=java
@@ -482,7 +510,7 @@ Boolean isFoo() {
);
}
- //@Test todo activate again
+ @Test
void noChangeNestedClassGetter() {
rewriteRun(// language=java
java(
From 6fd95b7d55f3d4abaab73ae0bc3be55d24d83117 Mon Sep 17 00:00:00 2001
From: Tim te Beek
Date: Sun, 15 Dec 2024 16:51:16 +0100
Subject: [PATCH 23/31] Resolve compilation issues
---
.../java/migrate/lombok/LombokUtils.java | 68 ++++++++++---------
.../java/migrate/lombok/UseLombokSetter.java | 2 +-
2 files changed, 37 insertions(+), 33 deletions(-)
diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java b/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
index c90bb6f0ad..2da112d7a7 100644
--- a/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
+++ b/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
@@ -66,38 +66,6 @@ static boolean isGetter(J.MethodDeclaration method) {
return false;
}
- private static boolean hasMatchingTypeAndName(J.MethodDeclaration method, @Nullable JavaType type, String simpleName) {
- if (method.getType() == type) {
- String deriveGetterMethodName = deriveGetterMethodName(type, simpleName);
- return method.getSimpleName().equals(deriveGetterMethodName);
- }
- return false;
- }
-
- private static String deriveGetterMethodName(@Nullable JavaType type, String fieldName) {
- if (type == JavaType.Primitive.Boolean) {
- boolean alreadyStartsWithIs = fieldName.length() >= 3 &&
- fieldName.substring(0, 3).matches("is[A-Z]");
- if (alreadyStartsWithIs) {
- return fieldName;
- } else {
- return "is" + StringUtils.capitalize(fieldName);
- }
- }
- return "get" + StringUtils.capitalize(fieldName);
- }
-
- static AccessLevel getAccessLevel(J.MethodDeclaration modifiers) {
- if (modifiers.hasModifier(Public)) {
- return PUBLIC;
- } else if (modifiers.hasModifier(Protected)) {
- return PROTECTED;
- } else if (modifiers.hasModifier(Private)) {
- return PRIVATE;
- }
- return PACKAGE;
- }
-
static boolean isEffectivelySetter(J.MethodDeclaration method) {
boolean isVoid = "void".equals(method.getType().toString());
List actualParameters = method.getParameters().stream()
@@ -131,4 +99,40 @@ static boolean isEffectivelySetter(J.MethodDeclaration method) {
param.getType().equals(fieldAccess.getType());
}
+
+ private static boolean hasMatchingTypeAndName(J.MethodDeclaration method, @Nullable JavaType type, String simpleName) {
+ if (method.getType() == type) {
+ String deriveGetterMethodName = deriveGetterMethodName(type, simpleName);
+ return method.getSimpleName().equals(deriveGetterMethodName);
+ }
+ return false;
+ }
+
+ private static String deriveGetterMethodName(@Nullable JavaType type, String fieldName) {
+ if (type == JavaType.Primitive.Boolean) {
+ boolean alreadyStartsWithIs = fieldName.length() >= 3 &&
+ fieldName.substring(0, 3).matches("is[A-Z]");
+ if (alreadyStartsWithIs) {
+ return fieldName;
+ } else {
+ return "is" + StringUtils.capitalize(fieldName);
+ }
+ }
+ return "get" + StringUtils.capitalize(fieldName);
+ }
+
+ static String deriveSetterMethodName(JavaType.Variable fieldType) {
+ return "set" + StringUtils.capitalize(fieldType.getName());
+ }
+
+ static AccessLevel getAccessLevel(J.MethodDeclaration methodDeclaration) {
+ if (methodDeclaration.hasModifier(Public)) {
+ return PUBLIC;
+ } else if (methodDeclaration.hasModifier(Protected)) {
+ return PROTECTED;
+ } else if (methodDeclaration.hasModifier(Private)) {
+ return PRIVATE;
+ }
+ return PACKAGE;
+ }
}
diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokSetter.java b/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokSetter.java
index f678ff9343..d2fad57492 100644
--- a/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokSetter.java
+++ b/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokSetter.java
@@ -95,7 +95,7 @@ public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, Ex
boolean nameMatch = method.getSimpleName().equals(LombokUtils.deriveSetterMethodName(fieldType));
if (nameMatch) {
Set set = getCursor().getNearestMessage(FIELDS_TO_DECORATE_KEY);
- set.add(new Finding(fieldType.getName(), LombokUtils.getAccessLevel(method.getModifiers())));
+ set.add(new Finding(fieldType.getName(), LombokUtils.getAccessLevel(method)));
return null; //delete
}
}
From 22810ec21e6991d4d0ecb7ef65791e9bc1618d99 Mon Sep 17 00:00:00 2001
From: Tim te Beek
Date: Sun, 15 Dec 2024 17:25:58 +0100
Subject: [PATCH 24/31] Ensure there is no change for a nested Setter
---
.../migrate/lombok/UseLombokSetterTest.java | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokSetterTest.java b/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokSetterTest.java
index 33e02601f7..bab9ddf258 100644
--- a/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokSetterTest.java
+++ b/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokSetterTest.java
@@ -464,4 +464,23 @@ public void unrelated2() {
)
);
}
+
+ @Test
+ void noChangeNestedClassSetter() {
+ rewriteRun(// language=java
+ java(
+ """
+ class Outer {
+ int foo = 9;
+
+ class Inner {
+ public void setFoo(int foo) {
+ Outer.this.foo = foo;
+ }
+ }
+ }
+ """
+ )
+ );
+ }
}
From a1288ef140efe0a385759c1846bfb441cbb4d6d8 Mon Sep 17 00:00:00 2001
From: Tim te Beek
Date: Sun, 15 Dec 2024 17:32:04 +0100
Subject: [PATCH 25/31] Extract a reusable FieldAnnotator class
---
.../java/migrate/lombok/FieldAnnotator.java | 54 +++++++++++++++++++
.../java/migrate/lombok/UseLombokGetter.java | 34 ++----------
2 files changed, 57 insertions(+), 31 deletions(-)
create mode 100644 src/main/java/org/openrewrite/java/migrate/lombok/FieldAnnotator.java
diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/FieldAnnotator.java b/src/main/java/org/openrewrite/java/migrate/lombok/FieldAnnotator.java
new file mode 100644
index 0000000000..2a42531b80
--- /dev/null
+++ b/src/main/java/org/openrewrite/java/migrate/lombok/FieldAnnotator.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2024 the original author or authors.
+ *
+ * Licensed under the Moderne Source Available License (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://docs.moderne.io/licensing/moderne-source-available-license
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.migrate.lombok;
+
+import lombok.AccessLevel;
+import lombok.EqualsAndHashCode;
+import lombok.Value;
+import org.openrewrite.ExecutionContext;
+import org.openrewrite.java.JavaIsoVisitor;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.java.JavaTemplate;
+import org.openrewrite.java.tree.J;
+import org.openrewrite.java.tree.JavaType;
+
+import static java.util.Comparator.comparing;
+import static lombok.AccessLevel.PUBLIC;
+
+@Value
+@EqualsAndHashCode(callSuper = false)
+class FieldAnnotator extends JavaIsoVisitor {
+
+ Class> annotation;
+ JavaType field;
+ AccessLevel accessLevel;
+
+ @Override
+ public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations multiVariable, ExecutionContext ctx) {
+ for (J.VariableDeclarations.NamedVariable variable : multiVariable.getVariables()) {
+ if (variable.getName().getFieldType() == field) {
+ maybeAddImport(annotation.getName());
+ maybeAddImport("lombok.AccessLevel");
+ String suffix = accessLevel == PUBLIC ? "" : String.format("(AccessLevel.%s)", accessLevel.name());
+ return JavaTemplate.builder("@" + annotation.getSimpleName() + suffix)
+ .imports(annotation.getName(), "lombok.AccessLevel")
+ .javaParser(JavaParser.fromJavaVersion().classpath("lombok"))
+ .build().apply(getCursor(), multiVariable.getCoordinates().addAnnotation(comparing(J.Annotation::getSimpleName)));
+ }
+ }
+ return multiVariable;
+ }
+}
diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokGetter.java b/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokGetter.java
index e7c99bbdff..b3584d79ab 100644
--- a/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokGetter.java
+++ b/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokGetter.java
@@ -15,26 +15,21 @@
*/
package org.openrewrite.java.migrate.lombok;
-import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
+import lombok.Getter;
import lombok.Value;
import org.jspecify.annotations.Nullable;
import org.openrewrite.ExecutionContext;
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;
import org.openrewrite.java.JavaIsoVisitor;
-import org.openrewrite.java.JavaParser;
-import org.openrewrite.java.JavaTemplate;
import org.openrewrite.java.tree.Expression;
import org.openrewrite.java.tree.J;
-import org.openrewrite.java.tree.JavaType;
import java.util.Collections;
-import java.util.List;
import java.util.Set;
import static java.util.Comparator.comparing;
-import static lombok.AccessLevel.PUBLIC;
@Value
@EqualsAndHashCode(callSuper = false)
@@ -66,12 +61,14 @@ public TreeVisitor, ExecutionContext> getVisitor() {
if (returnExpression instanceof J.Identifier &&
((J.Identifier) returnExpression).getFieldType() != null) {
doAfterVisit(new FieldAnnotator(
+ Getter.class,
((J.Identifier) returnExpression).getFieldType(),
LombokUtils.getAccessLevel(method)));
return null;
} else if (returnExpression instanceof J.FieldAccess &&
((J.FieldAccess) returnExpression).getName().getFieldType() != null) {
doAfterVisit(new FieldAnnotator(
+ Getter.class,
((J.FieldAccess) returnExpression).getName().getFieldType(),
LombokUtils.getAccessLevel(method)));
return null;
@@ -81,29 +78,4 @@ public TreeVisitor, ExecutionContext> getVisitor() {
}
};
}
-
-
- @Value
- @EqualsAndHashCode(callSuper = false)
- static class FieldAnnotator extends JavaIsoVisitor {
-
- JavaType field;
- AccessLevel accessLevel;
-
- @Override
- public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations multiVariable, ExecutionContext ctx) {
- for (J.VariableDeclarations.NamedVariable variable : multiVariable.getVariables()) {
- if (variable.getName().getFieldType() == field) {
- maybeAddImport("lombok.Getter");
- maybeAddImport("lombok.AccessLevel");
- String suffix = accessLevel == PUBLIC ? "" : String.format("(AccessLevel.%s)", accessLevel.name());
- return JavaTemplate.builder("@Getter" + suffix)
- .imports("lombok.Getter", "lombok.AccessLevel")
- .javaParser(JavaParser.fromJavaVersion().classpath("lombok"))
- .build().apply(getCursor(), multiVariable.getCoordinates().addAnnotation(comparing(J.Annotation::getSimpleName)));
- }
- }
- return multiVariable;
- }
- }
}
From 108fdfd92a75753706a1552bc67f9771b132f1ad Mon Sep 17 00:00:00 2001
From: Tim te Beek
Date: Sun, 15 Dec 2024 17:35:25 +0100
Subject: [PATCH 26/31] Adopt now shared `FieldAnnotator` for setters as well
---
.../java/migrate/lombok/UseLombokSetter.java | 90 ++-----------------
1 file changed, 8 insertions(+), 82 deletions(-)
diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokSetter.java b/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokSetter.java
index d2fad57492..3265a6b61c 100644
--- a/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokSetter.java
+++ b/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokSetter.java
@@ -15,21 +15,19 @@
*/
package org.openrewrite.java.migrate.lombok;
-import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
+import lombok.Setter;
import lombok.Value;
import org.jspecify.annotations.Nullable;
import org.openrewrite.ExecutionContext;
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;
import org.openrewrite.java.JavaIsoVisitor;
-import org.openrewrite.java.JavaParser;
-import org.openrewrite.java.JavaTemplate;
import org.openrewrite.java.tree.J;
-import java.util.*;
+import java.util.Collections;
+import java.util.Set;
-import static java.util.Comparator.comparing;
import static org.openrewrite.java.tree.JavaType.Variable;
@Value
@@ -65,25 +63,6 @@ public TreeVisitor, ExecutionContext> getVisitor() {
@Value
@EqualsAndHashCode(callSuper = false)
private static class MethodRemover extends JavaIsoVisitor {
- private static final String FIELDS_TO_DECORATE_KEY = "FIELDS_TO_DECORATE";
-
- @Override
- public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) {
-
- //initialize set of fields to annotate
- getCursor().putMessage(FIELDS_TO_DECORATE_KEY, new HashSet());
-
- //delete methods, note down corresponding fields
- J.ClassDeclaration classDeclAfterVisit = super.visitClassDeclaration(classDecl, ctx);
-
- //only thing that can have changed is removal of setter methods
- if (classDeclAfterVisit != classDecl) {
- //this set collects the fields for which existing methods have already been removed
- Set fieldsToDecorate = getCursor().pollNearestMessage(FIELDS_TO_DECORATE_KEY);
- doAfterVisit(new FieldAnnotator(fieldsToDecorate));
- }
- return classDeclAfterVisit;
- }
@Override
public J.@Nullable MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) {
@@ -94,68 +73,15 @@ public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, Ex
Variable fieldType = fieldAccess.getName().getFieldType();
boolean nameMatch = method.getSimpleName().equals(LombokUtils.deriveSetterMethodName(fieldType));
if (nameMatch) {
- Set set = getCursor().getNearestMessage(FIELDS_TO_DECORATE_KEY);
- set.add(new Finding(fieldType.getName(), LombokUtils.getAccessLevel(method)));
+ doAfterVisit(new FieldAnnotator(
+ Setter.class,
+ fieldType,
+ LombokUtils.getAccessLevel(method)
+ ));
return null; //delete
}
}
return method;
}
}
-
- @Value
- private static class Finding {
- String fieldName;
- AccessLevel accessLevel;
- }
-
-
- @Value
- @EqualsAndHashCode(callSuper = false)
- static class FieldAnnotator extends JavaIsoVisitor {
-
- Set fieldsToDecorate;
-
- private JavaTemplate getAnnotation(AccessLevel accessLevel) {
- JavaTemplate.Builder builder = AccessLevel.PUBLIC.equals(accessLevel) ?
- JavaTemplate.builder("@Setter\n") :
- JavaTemplate.builder("@Setter(AccessLevel." + accessLevel.name() + ")\n")
- .imports("lombok.AccessLevel");
-
- return builder
- .imports("lombok.Setter")
- .javaParser(JavaParser.fromJavaVersion().classpath("lombok"))
- .build();
- }
-
- @Override
- public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations multiVariable, ExecutionContext ctx) {
-
- //we accept only one var decl per line, see description
- if (multiVariable.getVariables().size() > 1) {
- return multiVariable;
- }
-
- //we only want to annotate fields and not e.g. method parameters, so we require a lass declaration to be close in the cursor.
- if (getCursor().getPathAsStream().limit(4).noneMatch(e -> e instanceof J.ClassDeclaration)) {
- return multiVariable;
- }
-
- J.VariableDeclarations.NamedVariable variable = multiVariable.getVariables().get(0);
- Optional field = fieldsToDecorate.stream()
- .filter(f -> f.fieldName.equals(variable.getSimpleName()))
- .findFirst();
-
- if (!field.isPresent()) {
- return multiVariable; //not the field we are looking for
- }
-
- J.VariableDeclarations annotated = getAnnotation(field.get().getAccessLevel()).apply(
- getCursor(),
- multiVariable.getCoordinates().addAnnotation(comparing(J.Annotation::getSimpleName)));
- maybeAddImport("lombok.Setter");
- maybeAddImport("lombok.AccessLevel");
- return annotated;
- }
- }
}
From 81c96990f161e363d8d002ab6cd66ef1d895e07d Mon Sep 17 00:00:00 2001
From: Tim te Beek
Date: Sun, 15 Dec 2024 18:12:42 +0100
Subject: [PATCH 27/31] Convert most of the checks as used for UseLombokGetter
---
.../java/migrate/lombok/LombokUtils.java | 80 +++++++++----------
.../java/migrate/lombok/UseLombokSetter.java | 40 +++-------
2 files changed, 51 insertions(+), 69 deletions(-)
diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java b/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
index 2da112d7a7..267cf778aa 100644
--- a/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
+++ b/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
@@ -21,11 +21,7 @@
import org.openrewrite.java.tree.Expression;
import org.openrewrite.java.tree.J;
import org.openrewrite.java.tree.JavaType;
-import org.openrewrite.java.tree.Statement;
-import java.util.List;
-
-import static java.util.stream.Collectors.toList;
import static lombok.AccessLevel.*;
import static org.openrewrite.java.tree.J.Modifier.Type.*;
@@ -66,40 +62,6 @@ static boolean isGetter(J.MethodDeclaration method) {
return false;
}
- static boolean isEffectivelySetter(J.MethodDeclaration method) {
- boolean isVoid = "void".equals(method.getType().toString());
- List actualParameters = method.getParameters().stream()
- .filter(s -> !(s instanceof J.Empty))
- .collect(toList());
- boolean oneParam = actualParameters.size() == 1;
- if (!isVoid || !oneParam)
- return false;
-
- J.VariableDeclarations variableDeclarations = (J.VariableDeclarations) actualParameters.get(0);
- J.VariableDeclarations.NamedVariable param = variableDeclarations.getVariables().get(0);
- String paramName = param.getName().toString();
-
- boolean singularStatement = method.getBody() != null //abstract methods can be null
- && method.getBody().getStatements().size() == 1 &&
- method.getBody().getStatements().get(0) instanceof J.Assignment;
-
- if (!singularStatement) {
- return false;
- }
- J.Assignment assignment = (J.Assignment) method.getBody().getStatements().get(0);
-
- J.FieldAccess fieldAccess = (J.FieldAccess) assignment.getVariable();
-
- return
- // assigned value is exactly the parameter
- assignment.getAssignment().toString().equals(paramName) // type of parameter and field have to match
- &&
-
- // type of parameter and field have to match
- param.getType().equals(fieldAccess.getType());
-
- }
-
private static boolean hasMatchingTypeAndName(J.MethodDeclaration method, @Nullable JavaType type, String simpleName) {
if (method.getType() == type) {
String deriveGetterMethodName = deriveGetterMethodName(type, simpleName);
@@ -121,8 +83,46 @@ private static String deriveGetterMethodName(@Nullable JavaType type, String fie
return "get" + StringUtils.capitalize(fieldName);
}
- static String deriveSetterMethodName(JavaType.Variable fieldType) {
- return "set" + StringUtils.capitalize(fieldType.getName());
+ static boolean isSetter(J.MethodDeclaration method) {
+ // Check return type: void
+ if (method.getType() != JavaType.Primitive.Void) {
+ return false;
+ }
+ // Check signature: single parameter
+ if (method.getParameters().size() != 1 || method.getParameters().get(0) instanceof J.Empty) {
+ return false;
+ }
+ // Check body: just an assignment
+ if (method.getBody() == null || //abstract methods can be null
+ method.getBody().getStatements().size() != 1 ||
+ !(method.getBody().getStatements().get(0) instanceof J.Assignment)) {
+ return false;
+ }
+ J.Assignment assignment = (J.Assignment) method.getBody().getStatements().get(0);
+ J.FieldAccess fieldAccess = (J.FieldAccess) assignment.getVariable();
+ if (!method.getSimpleName().equals(deriveSetterMethodName(fieldAccess))) {
+ return false;
+ }
+
+ // Check argument is assigned to field
+ J.VariableDeclarations variableDeclarations = (J.VariableDeclarations) method.getParameters().get(0);
+ J.VariableDeclarations.NamedVariable param = variableDeclarations.getVariables().get(0);
+ JavaType paramType = param.getType();
+ String paramName = param.getName().toString();
+
+
+ return
+ // assigned value is exactly the parameter
+ assignment.getAssignment().toString().equals(paramName) // type of parameter and field have to match
+ &&
+
+ // type of parameter and field have to match
+ param.getType().equals(fieldAccess.getType());
+
+ }
+
+ private static String deriveSetterMethodName(J.FieldAccess fieldAccess) {
+ return "set" + StringUtils.capitalize(fieldAccess.getSimpleName());
}
static AccessLevel getAccessLevel(J.MethodDeclaration methodDeclaration) {
diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokSetter.java b/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokSetter.java
index 3265a6b61c..c02702d89a 100644
--- a/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokSetter.java
+++ b/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokSetter.java
@@ -42,12 +42,7 @@ public String getDisplayName() {
@Override
public String getDescription() {
//language=markdown
- return "Convert trivial setter methods to `@Setter` annotations on their respective fields.\n\n" +
- "Limitations:\n\n" +
- " - Does not add a dependency to Lombok, users need to do that manually\n" +
- " - Ignores fields that are declared on the same line as others, e.g. `private int foo, bar; " +
- "Users who have such fields are advised to separate them beforehand with [org.openrewrite.staticanalysis.MultipleVariableDeclaration](https://docs.openrewrite.org/recipes/staticanalysis/multiplevariabledeclarations).\n" +
- " - Does not offer any of the configuration keys listed in https://projectlombok.org/features/GetterSetter.";
+ return "Convert trivial setter methods to `@Setter` annotations on their respective fields.";
}
@Override
@@ -57,31 +52,18 @@ public Set getTags() {
@Override
public TreeVisitor, ExecutionContext> getVisitor() {
- return new MethodRemover();
- }
-
- @Value
- @EqualsAndHashCode(callSuper = false)
- private static class MethodRemover extends JavaIsoVisitor {
-
- @Override
- public J.@Nullable MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) {
- if (LombokUtils.isEffectivelySetter(method)) {
- J.Assignment assignment_ = (J.Assignment) method.getBody().getStatements().get(0);
- J.FieldAccess fieldAccess = (J.FieldAccess) assignment_.getVariable();
-
- Variable fieldType = fieldAccess.getName().getFieldType();
- boolean nameMatch = method.getSimpleName().equals(LombokUtils.deriveSetterMethodName(fieldType));
- if (nameMatch) {
- doAfterVisit(new FieldAnnotator(
- Setter.class,
- fieldType,
- LombokUtils.getAccessLevel(method)
- ));
+ return new JavaIsoVisitor() {
+ @Override
+ public J.@Nullable MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) {
+ if (LombokUtils.isSetter(method)) {
+ J.Assignment assignment_ = (J.Assignment) method.getBody().getStatements().get(0);
+ J.FieldAccess fieldAccess = (J.FieldAccess) assignment_.getVariable();
+ Variable fieldType = fieldAccess.getName().getFieldType();
+ doAfterVisit(new FieldAnnotator(Setter.class, fieldType, LombokUtils.getAccessLevel(method)));
return null; //delete
}
+ return method;
}
- return method;
- }
+ };
}
}
From 868328ba7813668436c23a71fdf56c7654e9d0d6 Mon Sep 17 00:00:00 2001
From: Tim te Beek
Date: Sun, 15 Dec 2024 18:23:35 +0100
Subject: [PATCH 28/31] Add one more style we ought to cover
---
.../java/migrate/lombok/LombokUtils.java | 28 ++++++----
.../migrate/lombok/UseLombokSetterTest.java | 54 +++++++++++++++++++
2 files changed, 71 insertions(+), 11 deletions(-)
diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java b/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
index 267cf778aa..4343ed8513 100644
--- a/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
+++ b/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
@@ -99,26 +99,32 @@ static boolean isSetter(J.MethodDeclaration method) {
return false;
}
J.Assignment assignment = (J.Assignment) method.getBody().getStatements().get(0);
- J.FieldAccess fieldAccess = (J.FieldAccess) assignment.getVariable();
- if (!method.getSimpleName().equals(deriveSetterMethodName(fieldAccess))) {
+ J.FieldAccess assignedField = (J.FieldAccess) assignment.getVariable();
+ if (!method.getSimpleName().equals(deriveSetterMethodName(assignedField))) {
return false;
}
// Check argument is assigned to field
J.VariableDeclarations variableDeclarations = (J.VariableDeclarations) method.getParameters().get(0);
J.VariableDeclarations.NamedVariable param = variableDeclarations.getVariables().get(0);
- JavaType paramType = param.getType();
- String paramName = param.getName().toString();
+ // type of parameter and field have to match
+ if (!param.getType().equals(assignedField.getType())) {
+ return false;
+ }
- return
- // assigned value is exactly the parameter
- assignment.getAssignment().toString().equals(paramName) // type of parameter and field have to match
- &&
-
- // type of parameter and field have to match
- param.getType().equals(fieldAccess.getType());
+ // Check field is declared on method type
+ JavaType.FullyQualified declaringType = method.getMethodType().getDeclaringType();
+ if (assignedField.getTarget() instanceof J.Identifier) {
+ J.Identifier target = (J.Identifier) assignedField.getTarget();
+ return target.getFieldType() != null && declaringType == target.getFieldType().getOwner();
+ } else if (assignedField.getTarget() instanceof J.FieldAccess) {
+ Expression target = ((J.FieldAccess) assignedField.getTarget()).getTarget();
+ return target instanceof J.Identifier && ((J.Identifier) target).getFieldType() != null &&
+ declaringType == ((J.Identifier) target).getFieldType().getOwner();
+ }
+ return false;
}
private static String deriveSetterMethodName(J.FieldAccess fieldAccess) {
diff --git a/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokSetterTest.java b/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokSetterTest.java
index bab9ddf258..5116b15e02 100644
--- a/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokSetterTest.java
+++ b/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokSetterTest.java
@@ -66,6 +66,60 @@ class A {
);
}
+ @Test
+ void replaceSetterWhenArgNameWithUnderscore() {
+ rewriteRun(// language=java
+ java(
+ """
+ class A {
+
+ int foo = 9;
+
+ public void setFoo(int foo_) {
+ this.foo = foo_;
+ }
+ }
+ """,
+ """
+ import lombok.Setter;
+
+ class A {
+
+ @Setter
+ int foo = 9;
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void replaceSetterWhenArgNameWithUnderscoreAndUnqualifiedFieldAccess() {
+ rewriteRun(// language=java
+ java(
+ """
+ class A {
+
+ int foo = 9;
+
+ public void setFoo(int foo_) {
+ foo = foo_;
+ }
+ }
+ """,
+ """
+ import lombok.Setter;
+
+ class A {
+
+ @Setter
+ int foo = 9;
+ }
+ """
+ )
+ );
+ }
+
@Test
void tolerantToNonstandardParameterNames() {
rewriteRun(// language=java
From a1184771f20df6f60ada37f6dca48866c0c2577c Mon Sep 17 00:00:00 2001
From: Tim te Beek
Date: Sun, 15 Dec 2024 18:59:26 +0100
Subject: [PATCH 29/31] Add remaining checks to make all tests pass
---
.../java/migrate/lombok/LombokUtils.java | 52 ++++++++++---------
.../java/migrate/lombok/UseLombokSetter.java | 28 ++++++----
.../migrate/lombok/UseLombokSetterTest.java | 4 +-
3 files changed, 48 insertions(+), 36 deletions(-)
diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java b/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
index 4343ed8513..66bc1282ae 100644
--- a/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
+++ b/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
@@ -48,7 +48,7 @@ static boolean isGetter(J.MethodDeclaration method) {
J.Identifier identifier = (J.Identifier) returnExpression;
if (identifier.getFieldType() != null && declaringType == identifier.getFieldType().getOwner()) {
// Check return: type and matching field name
- return hasMatchingTypeAndName(method, identifier.getType(), identifier.getSimpleName());
+ return hasMatchingTypeAndGetterName(method, identifier.getType(), identifier.getSimpleName());
}
} else if (returnExpression instanceof J.FieldAccess) {
J.FieldAccess fieldAccess = (J.FieldAccess) returnExpression;
@@ -56,13 +56,13 @@ static boolean isGetter(J.MethodDeclaration method) {
if (target instanceof J.Identifier && ((J.Identifier) target).getFieldType() != null &&
declaringType == ((J.Identifier) target).getFieldType().getOwner()) {
// Check return: type and matching field name
- return hasMatchingTypeAndName(method, fieldAccess.getType(), fieldAccess.getSimpleName());
+ return hasMatchingTypeAndGetterName(method, fieldAccess.getType(), fieldAccess.getSimpleName());
}
}
return false;
}
- private static boolean hasMatchingTypeAndName(J.MethodDeclaration method, @Nullable JavaType type, String simpleName) {
+ private static boolean hasMatchingTypeAndGetterName(J.MethodDeclaration method, @Nullable JavaType type, String simpleName) {
if (method.getType() == type) {
String deriveGetterMethodName = deriveGetterMethodName(type, simpleName);
return method.getSimpleName().equals(deriveGetterMethodName);
@@ -83,6 +83,10 @@ private static String deriveGetterMethodName(@Nullable JavaType type, String fie
return "get" + StringUtils.capitalize(fieldName);
}
+ private static boolean hasMatchingSetterMethodName(J.MethodDeclaration method, String simpleName) {
+ return method.getSimpleName().equals("set" + StringUtils.capitalize(simpleName));
+ }
+
static boolean isSetter(J.MethodDeclaration method) {
// Check return type: void
if (method.getType() != JavaType.Primitive.Void) {
@@ -98,39 +102,39 @@ static boolean isSetter(J.MethodDeclaration method) {
!(method.getBody().getStatements().get(0) instanceof J.Assignment)) {
return false;
}
- J.Assignment assignment = (J.Assignment) method.getBody().getStatements().get(0);
- J.FieldAccess assignedField = (J.FieldAccess) assignment.getVariable();
- if (!method.getSimpleName().equals(deriveSetterMethodName(assignedField))) {
- return false;
- }
+ JavaType.FullyQualified declaringType = method.getMethodType().getDeclaringType();
- // Check argument is assigned to field
+ // Method parameter
J.VariableDeclarations variableDeclarations = (J.VariableDeclarations) method.getParameters().get(0);
J.VariableDeclarations.NamedVariable param = variableDeclarations.getVariables().get(0);
- // type of parameter and field have to match
- if (!param.getType().equals(assignedField.getType())) {
+ // Check there's no up/down cast between parameter and field
+ J.Assignment assignment = (J.Assignment) method.getBody().getStatements().get(0);
+ Expression variable = assignment.getVariable();
+ if (param.getType() != variable.getType()) {
return false;
}
- // Check field is declared on method type
- JavaType.FullyQualified declaringType = method.getMethodType().getDeclaringType();
- if (assignedField.getTarget() instanceof J.Identifier) {
- J.Identifier target = (J.Identifier) assignedField.getTarget();
- return target.getFieldType() != null && declaringType == target.getFieldType().getOwner();
- } else if (assignedField.getTarget() instanceof J.FieldAccess) {
- Expression target = ((J.FieldAccess) assignedField.getTarget()).getTarget();
- return target instanceof J.Identifier && ((J.Identifier) target).getFieldType() != null &&
- declaringType == ((J.Identifier) target).getFieldType().getOwner();
+ // Method name has to match
+ if (variable instanceof J.Identifier) {
+ J.Identifier assignedVar = (J.Identifier) variable;
+ if (hasMatchingSetterMethodName(method, assignedVar.getSimpleName())) {
+ // Check field is declared on method type
+ return assignedVar.getFieldType() != null && declaringType == assignedVar.getFieldType().getOwner();
+ }
+ } else if (variable instanceof J.FieldAccess) {
+ J.FieldAccess assignedField = (J.FieldAccess) variable;
+ if (hasMatchingSetterMethodName(method, assignedField.getSimpleName())) {
+ Expression target = assignedField.getTarget();
+ // Check field is declared on method type
+ return target instanceof J.Identifier && ((J.Identifier) target).getFieldType() != null &&
+ declaringType == ((J.Identifier) target).getFieldType().getOwner();
+ }
}
return false;
}
- private static String deriveSetterMethodName(J.FieldAccess fieldAccess) {
- return "set" + StringUtils.capitalize(fieldAccess.getSimpleName());
- }
-
static AccessLevel getAccessLevel(J.MethodDeclaration methodDeclaration) {
if (methodDeclaration.hasModifier(Public)) {
return PUBLIC;
diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokSetter.java b/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokSetter.java
index c02702d89a..9bf659606a 100644
--- a/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokSetter.java
+++ b/src/main/java/org/openrewrite/java/migrate/lombok/UseLombokSetter.java
@@ -1,11 +1,11 @@
/*
* Copyright 2024 the original author or authors.
*
- * Licensed under the Apache License, Version 2.0 (the "License");
+ * Licensed under the Moderne Source Available License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * https://www.apache.org/licenses/LICENSE-2.0
+ * https://docs.moderne.io/licensing/moderne-source-available-license
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -23,13 +23,12 @@
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;
import org.openrewrite.java.JavaIsoVisitor;
+import org.openrewrite.java.tree.Expression;
import org.openrewrite.java.tree.J;
import java.util.Collections;
import java.util.Set;
-import static org.openrewrite.java.tree.JavaType.Variable;
-
@Value
@EqualsAndHashCode(callSuper = false)
public class UseLombokSetter extends Recipe {
@@ -41,7 +40,6 @@ public String getDisplayName() {
@Override
public String getDescription() {
- //language=markdown
return "Convert trivial setter methods to `@Setter` annotations on their respective fields.";
}
@@ -56,11 +54,21 @@ public TreeVisitor, ExecutionContext> getVisitor() {
@Override
public J.@Nullable MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) {
if (LombokUtils.isSetter(method)) {
- J.Assignment assignment_ = (J.Assignment) method.getBody().getStatements().get(0);
- J.FieldAccess fieldAccess = (J.FieldAccess) assignment_.getVariable();
- Variable fieldType = fieldAccess.getName().getFieldType();
- doAfterVisit(new FieldAnnotator(Setter.class, fieldType, LombokUtils.getAccessLevel(method)));
- return null; //delete
+ Expression assignmentVariable = ((J.Assignment) method.getBody().getStatements().get(0)).getVariable();
+ if (assignmentVariable instanceof J.FieldAccess &&
+ ((J.FieldAccess) assignmentVariable).getName().getFieldType() != null) {
+ doAfterVisit(new FieldAnnotator(Setter.class,
+ ((J.FieldAccess) assignmentVariable).getName().getFieldType(),
+ LombokUtils.getAccessLevel(method)));
+ return null; //delete
+
+ } else if (assignmentVariable instanceof J.Identifier &&
+ ((J.Identifier) assignmentVariable).getFieldType() != null) {
+ doAfterVisit(new FieldAnnotator(Setter.class,
+ ((J.Identifier) assignmentVariable).getFieldType(),
+ LombokUtils.getAccessLevel(method)));
+ return null; //delete
+ }
}
return method;
}
diff --git a/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokSetterTest.java b/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokSetterTest.java
index 5116b15e02..3b0bb02c4c 100644
--- a/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokSetterTest.java
+++ b/src/test/java/org/openrewrite/java/migrate/lombok/UseLombokSetterTest.java
@@ -1,11 +1,11 @@
/*
* Copyright 2024 the original author or authors.
*
- * Licensed under the Apache License, Version 2.0 (the "License");
+ * Licensed under the Moderne Source Available License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * https://www.apache.org/licenses/LICENSE-2.0
+ * https://docs.moderne.io/licensing/moderne-source-available-license
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
From 0356976a6be696d75552b63acb3b03996d67ae17 Mon Sep 17 00:00:00 2001
From: Tim te Beek
Date: Sun, 15 Dec 2024 19:04:29 +0100
Subject: [PATCH 30/31] Move down variable and method closer to usage
---
.../openrewrite/java/migrate/lombok/LombokUtils.java | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java b/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
index 66bc1282ae..99689c44e2 100644
--- a/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
+++ b/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
@@ -83,10 +83,6 @@ private static String deriveGetterMethodName(@Nullable JavaType type, String fie
return "get" + StringUtils.capitalize(fieldName);
}
- private static boolean hasMatchingSetterMethodName(J.MethodDeclaration method, String simpleName) {
- return method.getSimpleName().equals("set" + StringUtils.capitalize(simpleName));
- }
-
static boolean isSetter(J.MethodDeclaration method) {
// Check return type: void
if (method.getType() != JavaType.Primitive.Void) {
@@ -102,7 +98,6 @@ static boolean isSetter(J.MethodDeclaration method) {
!(method.getBody().getStatements().get(0) instanceof J.Assignment)) {
return false;
}
- JavaType.FullyQualified declaringType = method.getMethodType().getDeclaringType();
// Method parameter
J.VariableDeclarations variableDeclarations = (J.VariableDeclarations) method.getParameters().get(0);
@@ -116,6 +111,7 @@ static boolean isSetter(J.MethodDeclaration method) {
}
// Method name has to match
+ JavaType.FullyQualified declaringType = method.getMethodType().getDeclaringType();
if (variable instanceof J.Identifier) {
J.Identifier assignedVar = (J.Identifier) variable;
if (hasMatchingSetterMethodName(method, assignedVar.getSimpleName())) {
@@ -135,6 +131,10 @@ static boolean isSetter(J.MethodDeclaration method) {
return false;
}
+ private static boolean hasMatchingSetterMethodName(J.MethodDeclaration method, String simpleName) {
+ return method.getSimpleName().equals("set" + StringUtils.capitalize(simpleName));
+ }
+
static AccessLevel getAccessLevel(J.MethodDeclaration methodDeclaration) {
if (methodDeclaration.hasModifier(Public)) {
return PUBLIC;
From 3bbb8ad2efc1cad0c7bb601fddc5b432d3bcba79 Mon Sep 17 00:00:00 2001
From: Tim te Beek
Date: Sun, 15 Dec 2024 19:29:42 +0100
Subject: [PATCH 31/31] Inline variables used only once
---
.../org/openrewrite/java/migrate/lombok/LombokUtils.java | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)
diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java b/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
index 99689c44e2..a397bf9042 100644
--- a/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
+++ b/src/main/java/org/openrewrite/java/migrate/lombok/LombokUtils.java
@@ -99,13 +99,9 @@ static boolean isSetter(J.MethodDeclaration method) {
return false;
}
- // Method parameter
- J.VariableDeclarations variableDeclarations = (J.VariableDeclarations) method.getParameters().get(0);
- J.VariableDeclarations.NamedVariable param = variableDeclarations.getVariables().get(0);
-
// Check there's no up/down cast between parameter and field
- J.Assignment assignment = (J.Assignment) method.getBody().getStatements().get(0);
- Expression variable = assignment.getVariable();
+ J.VariableDeclarations.NamedVariable param = ((J.VariableDeclarations) method.getParameters().get(0)).getVariables().get(0);
+ Expression variable = ((J.Assignment) method.getBody().getStatements().get(0)).getVariable();
if (param.getType() != variable.getType()) {
return false;
}