From 52339a8c41418309a643086ba363554c2ad8139c Mon Sep 17 00:00:00 2001
From: timo <1398557+timo-a@users.noreply.github.com>
Date: Tue, 24 Dec 2024 12:01:13 +0100
Subject: [PATCH 1/5] migrate recipe as-is
---
.../migrate/lombok/UseNoArgsConstructor.java | 104 ++++++++++++++
.../lombok/UseNoArgsConstructorTest.java | 133 ++++++++++++++++++
2 files changed, 237 insertions(+)
create mode 100644 src/main/java/org/openrewrite/java/migrate/lombok/UseNoArgsConstructor.java
create mode 100644 src/test/java/org/openrewrite/java/migrate/lombok/UseNoArgsConstructorTest.java
diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/UseNoArgsConstructor.java b/src/main/java/org/openrewrite/java/migrate/lombok/UseNoArgsConstructor.java
new file mode 100644
index 000000000..9e08c9af7
--- /dev/null
+++ b/src/main/java/org/openrewrite/java/migrate/lombok/UseNoArgsConstructor.java
@@ -0,0 +1,104 @@
+/*
+ * 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 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 static java.util.Comparator.comparing;
+
+@Value
+@EqualsAndHashCode(callSuper = false)
+public class UseNoArgsConstructor extends Recipe {
+
+ @Override
+ public String getDisplayName() {
+ //language=markdown
+ return "Use `@NoArgsConstructor` where applicable";
+ }
+
+ @Override
+ public String getDescription() {
+ //language=markdown
+ return "Prefer the lombok annotation `@NoArgsConstructor` over explicitly written out constructors.\n" +
+ "This recipe does not create annotations for implicit constructors.";
+ }
+
+ @Override
+ public TreeVisitor, ExecutionContext> getVisitor() {
+ return new JavaIsoVisitor() {
+ public static final String FOUND_EMPTY_CONSTRUCTOR = "FOUND_EMPTY_CONSTRUCTOR";
+
+
+ @Override
+ public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) {
+ J.ClassDeclaration classDeclAfterVisit = super.visitClassDeclaration(classDecl, ctx);
+
+ J.MethodDeclaration message = getCursor().pollMessage(FOUND_EMPTY_CONSTRUCTOR);
+
+ //if no constructor is found return immediately
+ if (message == null) {
+ return classDecl;//since nothing changed the original can be returned
+ }
+
+ maybeAddImport("lombok.NoArgsConstructor");
+
+ AccessLevel accessLevel = LombokUtils.getAccessLevel(message);
+
+ return getAnnotation(accessLevel).apply(
+ updateCursor(classDeclAfterVisit),
+ classDeclAfterVisit.getCoordinates().addAnnotation(comparing(J.Annotation::getSimpleName)));
+ }
+
+ private JavaTemplate getAnnotation(AccessLevel accessLevel) {
+
+ JavaTemplate.Builder builder = AccessLevel.PUBLIC.equals(accessLevel)
+ ? JavaTemplate.builder("@NoArgsConstructor()\n")
+ : JavaTemplate.builder("@NoArgsConstructor(access = AccessLevel." + accessLevel.name() + ")\n")
+ .imports("lombok.AccessLevel");
+
+ return builder
+ .imports("lombok.NoArgsConstructor")
+ .javaParser(JavaParser.fromJavaVersion()
+ .classpath("lombok"))
+ .build();
+ }
+
+ @Override
+ public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) {
+ assert method.getMethodType() != null;
+ if (method.getMethodType().getName().equals("") //it's a constructor
+ && method.getParameters().get(0) instanceof J.Empty //no parameters
+ && method.getBody().getStatements().isEmpty() //no side effects (=> does nothing)
+ ) {
+ getCursor().putMessageOnFirstEnclosing(J.ClassDeclaration.class, FOUND_EMPTY_CONSTRUCTOR, method);
+ return null;
+ }
+ return super.visitMethodDeclaration(method, ctx);
+ }
+
+ };
+ }
+
+}
diff --git a/src/test/java/org/openrewrite/java/migrate/lombok/UseNoArgsConstructorTest.java b/src/test/java/org/openrewrite/java/migrate/lombok/UseNoArgsConstructorTest.java
new file mode 100644
index 000000000..e450708c4
--- /dev/null
+++ b/src/test/java/org/openrewrite/java/migrate/lombok/UseNoArgsConstructorTest.java
@@ -0,0 +1,133 @@
+/*
+ * 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 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;
+
+class UseNoArgsConstructorTest implements RewriteTest {
+
+ @Override
+ public void defaults(RecipeSpec spec) {
+ spec.recipe(new UseNoArgsConstructor())
+ .parser(JavaParser.fromJavaVersion().logCompilationWarningsAndErrors(true));
+ }
+
+ @DocumentExample
+ @Test
+ void replaceEmptyPublicConstructor() {
+ rewriteRun(// language=java
+ java(
+ """
+ class A {
+ public A() {}
+ }
+ """,
+ """
+ import lombok.NoArgsConstructor;
+
+ @NoArgsConstructor()
+ class A {
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void keepNonEmptyPublicConstructor() {
+ rewriteRun(
+ java(
+ """
+ class A {
+
+ int foo;
+
+ public A() {
+ foo = 7;
+ }
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void replaceEmptyProtectedConstructor() {
+ rewriteRun(
+ java(
+ """
+ class A {
+ protected A() {}
+ }
+ """,
+ """
+ import lombok.NoArgsConstructor;
+
+ @NoArgsConstructor(access = AccessLevel.PROTECTED)
+ class A {
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void replaceEmptyPrivateConstructor() {
+ rewriteRun(
+ java(
+ """
+ class A {
+ private A() {}
+ }
+ """,
+ """
+ import lombok.NoArgsConstructor;
+
+ @NoArgsConstructor(access = AccessLevel.PRIVATE)
+ class A {
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void replaceEmptyPackageConstructor() {
+ rewriteRun(
+ java(
+ """
+ class A {
+ A() {}
+ }
+ """,
+ """
+ import lombok.NoArgsConstructor;
+
+ @NoArgsConstructor(access = AccessLevel.PACKAGE)
+ class A {
+ }
+ """
+ )
+ );
+ }
+
+}
From 3652d0615cda9af7c4543b532d7d942457e78672 Mon Sep 17 00:00:00 2001
From: Tim te Beek
Date: Sun, 5 Jan 2025 18:53:51 +0100
Subject: [PATCH 2/5] Apply suggestions from code review
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
---
.../java/migrate/lombok/UseNoArgsConstructor.java | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/UseNoArgsConstructor.java b/src/main/java/org/openrewrite/java/migrate/lombok/UseNoArgsConstructor.java
index 9e08c9af7..24bead0e2 100644
--- a/src/main/java/org/openrewrite/java/migrate/lombok/UseNoArgsConstructor.java
+++ b/src/main/java/org/openrewrite/java/migrate/lombok/UseNoArgsConstructor.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;
@@ -73,9 +74,9 @@ public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, Ex
private JavaTemplate getAnnotation(AccessLevel accessLevel) {
- JavaTemplate.Builder builder = AccessLevel.PUBLIC.equals(accessLevel)
- ? JavaTemplate.builder("@NoArgsConstructor()\n")
- : JavaTemplate.builder("@NoArgsConstructor(access = AccessLevel." + accessLevel.name() + ")\n")
+ JavaTemplate.Builder builder = AccessLevel.PUBLIC.equals(accessLevel) ?
+ JavaTemplate.builder("@NoArgsConstructor()\n") :
+ JavaTemplate.builder("@NoArgsConstructor(access = AccessLevel." + accessLevel.name() + ")\n")
.imports("lombok.AccessLevel");
return builder
@@ -86,7 +87,7 @@ private JavaTemplate getAnnotation(AccessLevel accessLevel) {
}
@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 (method.getMethodType().getName().equals("") //it's a constructor
&& method.getParameters().get(0) instanceof J.Empty //no parameters
From 35494ea3177017b417f98478cf98810c49f53c22 Mon Sep 17 00:00:00 2001
From: Tim te Beek
Date: Sun, 5 Jan 2025 19:09:35 +0100
Subject: [PATCH 3/5] Minimize implementation
---
.../migrate/lombok/UseNoArgsConstructor.java | 76 +++++++------------
.../lombok/UseNoArgsConstructorTest.java | 9 ++-
2 files changed, 32 insertions(+), 53 deletions(-)
diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/UseNoArgsConstructor.java b/src/main/java/org/openrewrite/java/migrate/lombok/UseNoArgsConstructor.java
index 24bead0e2..9130db7d6 100644
--- a/src/main/java/org/openrewrite/java/migrate/lombok/UseNoArgsConstructor.java
+++ b/src/main/java/org/openrewrite/java/migrate/lombok/UseNoArgsConstructor.java
@@ -1,11 +1,11 @@
/*
- * Copyright 2024 the original author or authors.
+ * Copyright 2025 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,
@@ -26,6 +26,7 @@
import org.openrewrite.java.JavaParser;
import org.openrewrite.java.JavaTemplate;
import org.openrewrite.java.tree.J;
+import org.openrewrite.java.tree.TypeUtils;
import static java.util.Comparator.comparing;
@@ -42,64 +43,39 @@ public String getDisplayName() {
@Override
public String getDescription() {
//language=markdown
- return "Prefer the lombok annotation `@NoArgsConstructor` over explicitly written out constructors.\n" +
- "This recipe does not create annotations for implicit constructors.";
+ return "Prefer the Lombok `@NoArgsConstructor` annotation over explicitly written out constructors.";
}
@Override
public TreeVisitor, ExecutionContext> getVisitor() {
return new JavaIsoVisitor() {
- public static final String FOUND_EMPTY_CONSTRUCTOR = "FOUND_EMPTY_CONSTRUCTOR";
-
-
- @Override
- public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) {
- J.ClassDeclaration classDeclAfterVisit = super.visitClassDeclaration(classDecl, ctx);
-
- J.MethodDeclaration message = getCursor().pollMessage(FOUND_EMPTY_CONSTRUCTOR);
-
- //if no constructor is found return immediately
- if (message == null) {
- return classDecl;//since nothing changed the original can be returned
- }
-
- maybeAddImport("lombok.NoArgsConstructor");
-
- AccessLevel accessLevel = LombokUtils.getAccessLevel(message);
-
- return getAnnotation(accessLevel).apply(
- updateCursor(classDeclAfterVisit),
- classDeclAfterVisit.getCoordinates().addAnnotation(comparing(J.Annotation::getSimpleName)));
- }
-
- private JavaTemplate getAnnotation(AccessLevel accessLevel) {
-
- JavaTemplate.Builder builder = AccessLevel.PUBLIC.equals(accessLevel) ?
- JavaTemplate.builder("@NoArgsConstructor()\n") :
- JavaTemplate.builder("@NoArgsConstructor(access = AccessLevel." + accessLevel.name() + ")\n")
- .imports("lombok.AccessLevel");
-
- return builder
- .imports("lombok.NoArgsConstructor")
- .javaParser(JavaParser.fromJavaVersion()
- .classpath("lombok"))
- .build();
- }
-
@Override
public J.@Nullable MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) {
- assert method.getMethodType() != null;
- if (method.getMethodType().getName().equals("") //it's a constructor
- && method.getParameters().get(0) instanceof J.Empty //no parameters
- && method.getBody().getStatements().isEmpty() //no side effects (=> does nothing)
- ) {
- getCursor().putMessageOnFirstEnclosing(J.ClassDeclaration.class, FOUND_EMPTY_CONSTRUCTOR, method);
+ if (method.isConstructor() &&
+ method.getParameters().get(0) instanceof J.Empty &&
+ method.getBody() != null && method.getBody().getStatements().isEmpty()) {
+ J.ClassDeclaration enclosing = getCursor().firstEnclosing(J.ClassDeclaration.class);
+ AccessLevel accessLevel = LombokUtils.getAccessLevel(method);
+ doAfterVisit(new JavaIsoVisitor() {
+ @Override
+ public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) {
+ if (TypeUtils.isOfType(classDecl.getType(), enclosing.getType())) {
+ String template = "@NoArgsConstructor" + (accessLevel == AccessLevel.PUBLIC ?
+ "" : "(access = AccessLevel." + accessLevel.name() + ")");
+ maybeAddImport("lombok.NoArgsConstructor");
+ return JavaTemplate.builder(template)
+ .imports("lombok.*")
+ .javaParser(JavaParser.fromJavaVersion().classpath("lombok"))
+ .build()
+ .apply(getCursor(), classDecl.getCoordinates().addAnnotation(comparing(J.Annotation::getSimpleName)));
+ }
+ return super.visitClassDeclaration(classDecl, ctx);
+ }
+ });
return null;
}
return super.visitMethodDeclaration(method, ctx);
}
-
};
}
-
}
diff --git a/src/test/java/org/openrewrite/java/migrate/lombok/UseNoArgsConstructorTest.java b/src/test/java/org/openrewrite/java/migrate/lombok/UseNoArgsConstructorTest.java
index e450708c4..413c44588 100644
--- a/src/test/java/org/openrewrite/java/migrate/lombok/UseNoArgsConstructorTest.java
+++ b/src/test/java/org/openrewrite/java/migrate/lombok/UseNoArgsConstructorTest.java
@@ -27,8 +27,7 @@ class UseNoArgsConstructorTest implements RewriteTest {
@Override
public void defaults(RecipeSpec spec) {
- spec.recipe(new UseNoArgsConstructor())
- .parser(JavaParser.fromJavaVersion().logCompilationWarningsAndErrors(true));
+ spec.recipe(new UseNoArgsConstructor());
}
@DocumentExample
@@ -44,7 +43,7 @@ public A() {}
"""
import lombok.NoArgsConstructor;
- @NoArgsConstructor()
+ @NoArgsConstructor
class A {
}
"""
@@ -55,6 +54,7 @@ class A {
@Test
void keepNonEmptyPublicConstructor() {
rewriteRun(
+ //language=java
java(
"""
class A {
@@ -73,6 +73,7 @@ public A() {
@Test
void replaceEmptyProtectedConstructor() {
rewriteRun(
+ //language=java
java(
"""
class A {
@@ -93,6 +94,7 @@ class A {
@Test
void replaceEmptyPrivateConstructor() {
rewriteRun(
+ //language=java
java(
"""
class A {
@@ -113,6 +115,7 @@ class A {
@Test
void replaceEmptyPackageConstructor() {
rewriteRun(
+ //language=java
java(
"""
class A {
From 5bc0325436c026fe327c3415da888ce0ee15024a Mon Sep 17 00:00:00 2001
From: Tim te Beek
Date: Sun, 5 Jan 2025 19:11:05 +0100
Subject: [PATCH 4/5] Add missing AccessLevel imports
---
.../openrewrite/java/migrate/lombok/UseNoArgsConstructor.java | 1 +
.../java/migrate/lombok/UseNoArgsConstructorTest.java | 3 +++
2 files changed, 4 insertions(+)
diff --git a/src/main/java/org/openrewrite/java/migrate/lombok/UseNoArgsConstructor.java b/src/main/java/org/openrewrite/java/migrate/lombok/UseNoArgsConstructor.java
index 9130db7d6..630041410 100644
--- a/src/main/java/org/openrewrite/java/migrate/lombok/UseNoArgsConstructor.java
+++ b/src/main/java/org/openrewrite/java/migrate/lombok/UseNoArgsConstructor.java
@@ -62,6 +62,7 @@ public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, Ex
if (TypeUtils.isOfType(classDecl.getType(), enclosing.getType())) {
String template = "@NoArgsConstructor" + (accessLevel == AccessLevel.PUBLIC ?
"" : "(access = AccessLevel." + accessLevel.name() + ")");
+ maybeAddImport("lombok.AccessLevel");
maybeAddImport("lombok.NoArgsConstructor");
return JavaTemplate.builder(template)
.imports("lombok.*")
diff --git a/src/test/java/org/openrewrite/java/migrate/lombok/UseNoArgsConstructorTest.java b/src/test/java/org/openrewrite/java/migrate/lombok/UseNoArgsConstructorTest.java
index 413c44588..d3aca34f1 100644
--- a/src/test/java/org/openrewrite/java/migrate/lombok/UseNoArgsConstructorTest.java
+++ b/src/test/java/org/openrewrite/java/migrate/lombok/UseNoArgsConstructorTest.java
@@ -81,6 +81,7 @@ protected A() {}
}
""",
"""
+ import lombok.AccessLevel;
import lombok.NoArgsConstructor;
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@@ -102,6 +103,7 @@ private A() {}
}
""",
"""
+ import lombok.AccessLevel;
import lombok.NoArgsConstructor;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@@ -123,6 +125,7 @@ class A {
}
""",
"""
+ import lombok.AccessLevel;
import lombok.NoArgsConstructor;
@NoArgsConstructor(access = AccessLevel.PACKAGE)
From 50c2375a420c64918a731bbe8965c1f700ecd0b0 Mon Sep 17 00:00:00 2001
From: Tim te Beek
Date: Sun, 5 Jan 2025 19:15:06 +0100
Subject: [PATCH 5/5] Remove unused import
---
.../java/migrate/lombok/UseNoArgsConstructorTest.java | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/test/java/org/openrewrite/java/migrate/lombok/UseNoArgsConstructorTest.java b/src/test/java/org/openrewrite/java/migrate/lombok/UseNoArgsConstructorTest.java
index d3aca34f1..92ec7a702 100644
--- a/src/test/java/org/openrewrite/java/migrate/lombok/UseNoArgsConstructorTest.java
+++ b/src/test/java/org/openrewrite/java/migrate/lombok/UseNoArgsConstructorTest.java
@@ -17,7 +17,6 @@
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;