From ac75430659debad5298e4a0e07a515e5f869fa68 Mon Sep 17 00:00:00 2001 From: e5LA <208197507+e5LA@users.noreply.github.com> Date: Sat, 13 Sep 2025 22:07:15 +0200 Subject: [PATCH 1/7] Add ReplaceSystemOutWithIOPrint to migrate System.out.print/println to IO.print/println --- .../io/ReplaceSystemOutWithIOPrint.java | 84 +++++++ .../io/ReplaceSystemOutWithIOPrintTest.java | 223 ++++++++++++++++++ 2 files changed, 307 insertions(+) create mode 100644 src/main/java/org/openrewrite/java/migrate/io/ReplaceSystemOutWithIOPrint.java create mode 100644 src/test/java/org/openrewrite/java/migrate/io/ReplaceSystemOutWithIOPrintTest.java diff --git a/src/main/java/org/openrewrite/java/migrate/io/ReplaceSystemOutWithIOPrint.java b/src/main/java/org/openrewrite/java/migrate/io/ReplaceSystemOutWithIOPrint.java new file mode 100644 index 0000000000..3eb6f09f86 --- /dev/null +++ b/src/main/java/org/openrewrite/java/migrate/io/ReplaceSystemOutWithIOPrint.java @@ -0,0 +1,84 @@ +/* + * Copyright 2025 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.io; + +import org.openrewrite.ExecutionContext; +import org.openrewrite.Recipe; +import org.openrewrite.TreeVisitor; +import org.openrewrite.java.JavaIsoVisitor; +import org.openrewrite.java.JavaTemplate; +import org.openrewrite.java.tree.J; + +public class ReplaceSystemOutWithIOPrint extends Recipe { + + @Override + public String getDisplayName() { + return "Migrate System.out.print to Java 25 IO utility class"; + } + + @Override + public String getDescription() { + return "Replace System.out.print(), System.out.println() with IO.print() and IO.println(). Migrates to the new IO utility class introduced in Java 25."; + } + + @Override + public TreeVisitor getVisitor() { + return new JavaIsoVisitor() { + private final JavaTemplate printTemplate = JavaTemplate.builder("IO.print(#{any()})").build(); + private final JavaTemplate printlnTemplate = JavaTemplate.builder("IO.println(#{any()})").build(); + private final JavaTemplate printEmptyTemplate = JavaTemplate.builder("IO.print()").build(); + private final JavaTemplate printlnEmptyTemplate = JavaTemplate.builder("IO.println()").build(); + + @Override + public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { + J.MethodInvocation m = super.visitMethodInvocation(method, ctx); + + if (!isSystemOutMethod(m)) { + return m; + } + String methodName = m.getName().getSimpleName(); + JavaTemplate tpl; + + if ("print".equals(methodName)) { + tpl = m.getArguments().isEmpty() ? printEmptyTemplate : printTemplate; + } else if ("println".equals(methodName)) { + tpl = m.getArguments().isEmpty() ? printlnEmptyTemplate : printlnTemplate; + } else { + return m; + } + + return applyTemplate(tpl, m); + } + + private J.MethodInvocation applyTemplate(JavaTemplate tpl, J.MethodInvocation m) { + return m.getArguments().isEmpty() + ? tpl.apply(getCursor(), m.getCoordinates().replace()) + : tpl.apply(getCursor(), m.getCoordinates().replace(), m.getArguments().get(0)); + } + + private boolean isSystemOutMethod(J.MethodInvocation mi) { + if (!(mi.getSelect() instanceof J.FieldAccess)) { + return false; + } + + J.FieldAccess fieldAccess = (J.FieldAccess) mi.getSelect(); + return fieldAccess.getTarget() instanceof J.Identifier && + ((J.Identifier) fieldAccess.getTarget()).getSimpleName().equals("System") && fieldAccess.getName().getSimpleName().equals("out") && + (mi.getName().getSimpleName().equals("print") || mi.getName().getSimpleName().equals("println")); + } + }; + } +} diff --git a/src/test/java/org/openrewrite/java/migrate/io/ReplaceSystemOutWithIOPrintTest.java b/src/test/java/org/openrewrite/java/migrate/io/ReplaceSystemOutWithIOPrintTest.java new file mode 100644 index 0000000000..963941bb20 --- /dev/null +++ b/src/test/java/org/openrewrite/java/migrate/io/ReplaceSystemOutWithIOPrintTest.java @@ -0,0 +1,223 @@ +/* + * Copyright 2025 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.io; + +import org.junit.jupiter.api.Test; +import org.openrewrite.java.JavaParser; +import org.openrewrite.java.search.FindMissingTypes; +import org.openrewrite.test.RecipeSpec; +import org.openrewrite.test.RewriteTest; +import org.openrewrite.test.TypeValidation; + +import static org.openrewrite.java.Assertions.java; +import static org.openrewrite.java.Assertions.javaVersion; + +class ReplaceSystemOutWithIOPrintTest implements RewriteTest { + + @Override + public void defaults(RecipeSpec spec) { + spec.recipe(new ReplaceSystemOutWithIOPrint()) + .afterTypeValidationOptions(TypeValidation.all().allowMissingType(o -> { + assert o instanceof FindMissingTypes.MissingTypeResult; + FindMissingTypes.MissingTypeResult result = (FindMissingTypes.MissingTypeResult) o; + return result.getPrintedTree().contains("IO"); + })) // TODO remove once tests run on Java 25+ + .parser(JavaParser.fromJavaVersion()) + .allSources(s -> s.markers(javaVersion(25))); + } + + @Test + void replaceSystemOutPrint() { + rewriteRun( + java( + """ + class Example { + void test() { + System.out.print("Hello"); + } + } + """, + """ + class Example { + void test() { + IO.print("Hello"); + } + } + """ + ) + ); + } + + @Test + void replaceSystemOutPrintln() { + rewriteRun( + java( + """ + class Example { + void test() { + System.out.println("Hello"); + } + } + """, + """ + class Example { + void test() { + IO.println("Hello"); + } + } + """ + ) + ); + } + + @Test + void replaceSystemOutPrintWithVariable() { + rewriteRun( + java( + """ + class Example { + void test() { + String message = "Hello World"; + System.out.print(message); + } + } + """, + """ + class Example { + void test() { + String message = "Hello World"; + IO.print(message); + } + } + """ + ) + ); + } + + @Test + void replaceSystemOutPrintlnEmpty() { + rewriteRun( + java( + """ + class Example { + void test() { + System.out.println(); + } + } + """, + """ + class Example { + void test() { + IO.println(); + } + } + """ + ) + ); + } + + @Test + void replaceMultipleSystemOutCalls() { + rewriteRun( + java( + """ + class Example { + void test() { + System.out.print("Hello"); + System.out.println(" World"); + System.out.print(42); + System.out.println(); + } + } + """, + """ + class Example { + void test() { + IO.print("Hello"); + IO.println(" World"); + IO.print(42); + IO.println(); + } + } + """ + ) + ); + } + + @Test + void handlesPrintWithComplexExpressions() { + rewriteRun( + java( + """ + class Example { + void test() { + String name = "John"; + int age = 30; + System.out.print("Name: " + name + ", Age: " + age); + System.out.println(String.format("Formatted: %s is %d years old", name, age)); + } + } + """, + """ + class Example { + void test() { + String name = "John"; + int age = 30; + IO.print("Name: " + name + ", Age: " + age); + IO.println(String.format("Formatted: %s is %d years old", name, age)); + } + } + """ + ) + ); + } + + + @Test + void doesNotReplaceSystemErrCalls() { + rewriteRun( + java( + """ + class Example { + void test() { + System.err.print("Error message"); + System.err.println("Error message"); + } + } + """ + ) + ); + } + + @Test + void doesNotReplaceOtherPrintStreams() { + rewriteRun( + java( + """ + import java.io.PrintStream; + + class Example { + void test() { + PrintStream ps = new PrintStream(System.out); + ps.print("Should not change"); + ps.println("Should not change"); + } + } + """ + ) + ); + } +} From 739873f00ef50ceb14123487c46c4e9bf329b027 Mon Sep 17 00:00:00 2001 From: e5LA <208197507+e5LA@users.noreply.github.com> Date: Sun, 14 Sep 2025 10:24:54 +0200 Subject: [PATCH 2/7] refactor: reformatting --- .../java/migrate/io/ReplaceSystemOutWithIOPrint.java | 10 +++++----- .../migrate/io/ReplaceSystemOutWithIOPrintTest.java | 2 ++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/openrewrite/java/migrate/io/ReplaceSystemOutWithIOPrint.java b/src/main/java/org/openrewrite/java/migrate/io/ReplaceSystemOutWithIOPrint.java index 3eb6f09f86..bb0ae561e4 100644 --- a/src/main/java/org/openrewrite/java/migrate/io/ReplaceSystemOutWithIOPrint.java +++ b/src/main/java/org/openrewrite/java/migrate/io/ReplaceSystemOutWithIOPrint.java @@ -64,9 +64,9 @@ public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, Execu } private J.MethodInvocation applyTemplate(JavaTemplate tpl, J.MethodInvocation m) { - return m.getArguments().isEmpty() - ? tpl.apply(getCursor(), m.getCoordinates().replace()) - : tpl.apply(getCursor(), m.getCoordinates().replace(), m.getArguments().get(0)); + return m.getArguments().isEmpty() ? + tpl.apply(getCursor(), m.getCoordinates().replace()) : + tpl.apply(getCursor(), m.getCoordinates().replace(), m.getArguments().get(0)); } private boolean isSystemOutMethod(J.MethodInvocation mi) { @@ -76,8 +76,8 @@ private boolean isSystemOutMethod(J.MethodInvocation mi) { J.FieldAccess fieldAccess = (J.FieldAccess) mi.getSelect(); return fieldAccess.getTarget() instanceof J.Identifier && - ((J.Identifier) fieldAccess.getTarget()).getSimpleName().equals("System") && fieldAccess.getName().getSimpleName().equals("out") && - (mi.getName().getSimpleName().equals("print") || mi.getName().getSimpleName().equals("println")); + "System".equals(((J.Identifier) fieldAccess.getTarget()).getSimpleName()) && "out".equals(fieldAccess.getName().getSimpleName()) && + ("print".equals(mi.getName().getSimpleName()) || "println".equals(mi.getName().getSimpleName())); } }; } diff --git a/src/test/java/org/openrewrite/java/migrate/io/ReplaceSystemOutWithIOPrintTest.java b/src/test/java/org/openrewrite/java/migrate/io/ReplaceSystemOutWithIOPrintTest.java index 963941bb20..1a9a432aeb 100644 --- a/src/test/java/org/openrewrite/java/migrate/io/ReplaceSystemOutWithIOPrintTest.java +++ b/src/test/java/org/openrewrite/java/migrate/io/ReplaceSystemOutWithIOPrintTest.java @@ -16,6 +16,7 @@ package org.openrewrite.java.migrate.io; import org.junit.jupiter.api.Test; +import org.openrewrite.DocumentExample; import org.openrewrite.java.JavaParser; import org.openrewrite.java.search.FindMissingTypes; import org.openrewrite.test.RecipeSpec; @@ -39,6 +40,7 @@ public void defaults(RecipeSpec spec) { .allSources(s -> s.markers(javaVersion(25))); } + @DocumentExample @Test void replaceSystemOutPrint() { rewriteRun( From 613a4c0bcfb34c3a532d8cf0cf75fcfa9ef686e3 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Tue, 16 Sep 2025 18:01:48 +0200 Subject: [PATCH 3/7] Handle additional edge case with static import --- .../io/ReplaceSystemOutWithIOPrint.java | 30 ++++++++++++------- .../io/ReplaceSystemOutWithIOPrintTest.java | 25 +++++++++++++++- 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/openrewrite/java/migrate/io/ReplaceSystemOutWithIOPrint.java b/src/main/java/org/openrewrite/java/migrate/io/ReplaceSystemOutWithIOPrint.java index bb0ae561e4..b3d227442b 100644 --- a/src/main/java/org/openrewrite/java/migrate/io/ReplaceSystemOutWithIOPrint.java +++ b/src/main/java/org/openrewrite/java/migrate/io/ReplaceSystemOutWithIOPrint.java @@ -20,7 +20,10 @@ import org.openrewrite.TreeVisitor; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.JavaTemplate; +import org.openrewrite.java.MethodMatcher; +import org.openrewrite.java.tree.Expression; import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.TypeUtils; public class ReplaceSystemOutWithIOPrint extends Recipe { @@ -34,6 +37,8 @@ public String getDescription() { return "Replace System.out.print(), System.out.println() with IO.print() and IO.println(). Migrates to the new IO utility class introduced in Java 25."; } + private static final MethodMatcher SYSTEM_OUT_PRINT = new MethodMatcher("java.io.PrintStream print*(..)"); + @Override public TreeVisitor getVisitor() { return new JavaIsoVisitor() { @@ -60,24 +65,29 @@ public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, Execu return m; } - return applyTemplate(tpl, m); - } - - private J.MethodInvocation applyTemplate(JavaTemplate tpl, J.MethodInvocation m) { + maybeRemoveImport("java.lang.System.out"); return m.getArguments().isEmpty() ? tpl.apply(getCursor(), m.getCoordinates().replace()) : tpl.apply(getCursor(), m.getCoordinates().replace(), m.getArguments().get(0)); } private boolean isSystemOutMethod(J.MethodInvocation mi) { - if (!(mi.getSelect() instanceof J.FieldAccess)) { - return false; + if (SYSTEM_OUT_PRINT.matches(mi)) { + Expression expression = mi.getSelect(); + if (expression instanceof J.FieldAccess) { + return isSystemOut(((J.FieldAccess) expression).getName()); + } + if (expression instanceof J.Identifier) { + return isSystemOut((J.Identifier) expression); + } } + return false; + } - J.FieldAccess fieldAccess = (J.FieldAccess) mi.getSelect(); - return fieldAccess.getTarget() instanceof J.Identifier && - "System".equals(((J.Identifier) fieldAccess.getTarget()).getSimpleName()) && "out".equals(fieldAccess.getName().getSimpleName()) && - ("print".equals(mi.getName().getSimpleName()) || "println".equals(mi.getName().getSimpleName())); + private boolean isSystemOut(J.Identifier identifier) { + return "out".equals(identifier.getSimpleName()) && + identifier.getFieldType() != null && + TypeUtils.isAssignableTo("java.lang.System", identifier.getFieldType().getOwner()); } }; } diff --git a/src/test/java/org/openrewrite/java/migrate/io/ReplaceSystemOutWithIOPrintTest.java b/src/test/java/org/openrewrite/java/migrate/io/ReplaceSystemOutWithIOPrintTest.java index 1a9a432aeb..8c88b91a25 100644 --- a/src/test/java/org/openrewrite/java/migrate/io/ReplaceSystemOutWithIOPrintTest.java +++ b/src/test/java/org/openrewrite/java/migrate/io/ReplaceSystemOutWithIOPrintTest.java @@ -85,6 +85,30 @@ void test() { ); } + @Test + void replaceSystemOutPrintlnWithStaticImport() { + rewriteRun( + java( + """ + import static java.lang.System.out; + + class Example { + void test() { + out.println("Hello"); + } + } + """, + """ + class Example { + void test() { + IO.println("Hello"); + } + } + """ + ) + ); + } + @Test void replaceSystemOutPrintWithVariable() { rewriteRun( @@ -187,7 +211,6 @@ void test() { ); } - @Test void doesNotReplaceSystemErrCalls() { rewriteRun( From 6c085e6eb24945f8bf3da5e06417df99847ad47b Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Tue, 16 Sep 2025 18:12:56 +0200 Subject: [PATCH 4/7] Inline templates --- .../io/ReplaceSystemOutWithIOPrint.java | 29 ++++++------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/src/main/java/org/openrewrite/java/migrate/io/ReplaceSystemOutWithIOPrint.java b/src/main/java/org/openrewrite/java/migrate/io/ReplaceSystemOutWithIOPrint.java index b3d227442b..dc71f9618c 100644 --- a/src/main/java/org/openrewrite/java/migrate/io/ReplaceSystemOutWithIOPrint.java +++ b/src/main/java/org/openrewrite/java/migrate/io/ReplaceSystemOutWithIOPrint.java @@ -16,11 +16,13 @@ package org.openrewrite.java.migrate.io; import org.openrewrite.ExecutionContext; +import org.openrewrite.Preconditions; import org.openrewrite.Recipe; import org.openrewrite.TreeVisitor; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.JavaTemplate; import org.openrewrite.java.MethodMatcher; +import org.openrewrite.java.search.UsesMethod; import org.openrewrite.java.tree.Expression; import org.openrewrite.java.tree.J; import org.openrewrite.java.tree.TypeUtils; @@ -41,34 +43,20 @@ public String getDescription() { @Override public TreeVisitor getVisitor() { - return new JavaIsoVisitor() { - private final JavaTemplate printTemplate = JavaTemplate.builder("IO.print(#{any()})").build(); - private final JavaTemplate printlnTemplate = JavaTemplate.builder("IO.println(#{any()})").build(); - private final JavaTemplate printEmptyTemplate = JavaTemplate.builder("IO.print()").build(); - private final JavaTemplate printlnEmptyTemplate = JavaTemplate.builder("IO.println()").build(); + return Preconditions.check(new UsesMethod<>(SYSTEM_OUT_PRINT), new JavaIsoVisitor() { @Override public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { J.MethodInvocation m = super.visitMethodInvocation(method, ctx); - if (!isSystemOutMethod(m)) { return m; } String methodName = m.getName().getSimpleName(); - JavaTemplate tpl; - - if ("print".equals(methodName)) { - tpl = m.getArguments().isEmpty() ? printEmptyTemplate : printTemplate; - } else if ("println".equals(methodName)) { - tpl = m.getArguments().isEmpty() ? printlnEmptyTemplate : printlnTemplate; - } else { - return m; - } - - maybeRemoveImport("java.lang.System.out"); return m.getArguments().isEmpty() ? - tpl.apply(getCursor(), m.getCoordinates().replace()) : - tpl.apply(getCursor(), m.getCoordinates().replace(), m.getArguments().get(0)); + JavaTemplate.builder("IO.#{}()").build() + .apply(getCursor(), m.getCoordinates().replace(), methodName) : + JavaTemplate.builder("IO.#{}(#{any()})").build() + .apply(getCursor(), m.getCoordinates().replace(), methodName, m.getArguments().get(0)); } private boolean isSystemOutMethod(J.MethodInvocation mi) { @@ -78,6 +66,7 @@ private boolean isSystemOutMethod(J.MethodInvocation mi) { return isSystemOut(((J.FieldAccess) expression).getName()); } if (expression instanceof J.Identifier) { + maybeRemoveImport("java.lang.System.out"); return isSystemOut((J.Identifier) expression); } } @@ -89,6 +78,6 @@ private boolean isSystemOut(J.Identifier identifier) { identifier.getFieldType() != null && TypeUtils.isAssignableTo("java.lang.System", identifier.getFieldType().getOwner()); } - }; + }); } } From 2e1c69f5ed47d08d277745231119dc945282710f Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Tue, 16 Sep 2025 18:16:11 +0200 Subject: [PATCH 5/7] Use markdown --- .../java/migrate/io/ReplaceSystemOutWithIOPrint.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/openrewrite/java/migrate/io/ReplaceSystemOutWithIOPrint.java b/src/main/java/org/openrewrite/java/migrate/io/ReplaceSystemOutWithIOPrint.java index dc71f9618c..3b2358057a 100644 --- a/src/main/java/org/openrewrite/java/migrate/io/ReplaceSystemOutWithIOPrint.java +++ b/src/main/java/org/openrewrite/java/migrate/io/ReplaceSystemOutWithIOPrint.java @@ -31,12 +31,13 @@ public class ReplaceSystemOutWithIOPrint extends Recipe { @Override public String getDisplayName() { - return "Migrate System.out.print to Java 25 IO utility class"; + return "Migrate `System.out.print` to Java 25 IO utility class"; } @Override public String getDescription() { - return "Replace System.out.print(), System.out.println() with IO.print() and IO.println(). Migrates to the new IO utility class introduced in Java 25."; + return "Replace `System.out.print()`, `System.out.println()` with `IO.print()` and `IO.println()`. " + + "Migrates to the new IO utility class introduced in Java 25."; } private static final MethodMatcher SYSTEM_OUT_PRINT = new MethodMatcher("java.io.PrintStream print*(..)"); @@ -44,7 +45,6 @@ public String getDescription() { @Override public TreeVisitor getVisitor() { return Preconditions.check(new UsesMethod<>(SYSTEM_OUT_PRINT), new JavaIsoVisitor() { - @Override public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { J.MethodInvocation m = super.visitMethodInvocation(method, ctx); From 9f76b5799848fb87b5499f31049ef34b008d0b5d Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Tue, 16 Sep 2025 18:16:29 +0200 Subject: [PATCH 6/7] Include with Java 25 upgrade --- src/main/resources/META-INF/rewrite/java-version-25.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/resources/META-INF/rewrite/java-version-25.yml b/src/main/resources/META-INF/rewrite/java-version-25.yml index 7a531e94b0..2e848c35ec 100644 --- a/src/main/resources/META-INF/rewrite/java-version-25.yml +++ b/src/main/resources/META-INF/rewrite/java-version-25.yml @@ -28,6 +28,7 @@ recipeList: - org.openrewrite.java.migrate.UpgradeToJava21 - org.openrewrite.java.migrate.UpgradeJavaVersion: version: 25 + - org.openrewrite.java.migrate.io.ReplaceSystemOutWithIOPrint - org.openrewrite.java.migrate.lang.MigrateProcessWaitForDuration - org.openrewrite.java.migrate.lang.ReplaceUnusedVariablesWithUnderscore - org.openrewrite.java.migrate.util.MigrateInflaterDeflaterToClose From 79a7eb37c390f275b7f32fabfbb655aa7c6fe6d4 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Tue, 16 Sep 2025 18:26:50 +0200 Subject: [PATCH 7/7] Do not replace `printf` --- .../io/ReplaceSystemOutWithIOPrint.java | 72 ++++++++++--------- .../io/ReplaceSystemOutWithIOPrintTest.java | 15 ++++ 2 files changed, 54 insertions(+), 33 deletions(-) diff --git a/src/main/java/org/openrewrite/java/migrate/io/ReplaceSystemOutWithIOPrint.java b/src/main/java/org/openrewrite/java/migrate/io/ReplaceSystemOutWithIOPrint.java index 3b2358057a..d4c7a28d54 100644 --- a/src/main/java/org/openrewrite/java/migrate/io/ReplaceSystemOutWithIOPrint.java +++ b/src/main/java/org/openrewrite/java/migrate/io/ReplaceSystemOutWithIOPrint.java @@ -40,44 +40,50 @@ public String getDescription() { "Migrates to the new IO utility class introduced in Java 25."; } - private static final MethodMatcher SYSTEM_OUT_PRINT = new MethodMatcher("java.io.PrintStream print*(..)"); + private static final MethodMatcher SYSTEM_OUT_PRINT = new MethodMatcher("java.io.PrintStream print(..)"); + private static final MethodMatcher SYSTEM_OUT_PRINTLN = new MethodMatcher("java.io.PrintStream println(..)"); @Override public TreeVisitor getVisitor() { - return Preconditions.check(new UsesMethod<>(SYSTEM_OUT_PRINT), new JavaIsoVisitor() { - @Override - public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { - J.MethodInvocation m = super.visitMethodInvocation(method, ctx); - if (!isSystemOutMethod(m)) { - return m; - } - String methodName = m.getName().getSimpleName(); - return m.getArguments().isEmpty() ? - JavaTemplate.builder("IO.#{}()").build() - .apply(getCursor(), m.getCoordinates().replace(), methodName) : - JavaTemplate.builder("IO.#{}(#{any()})").build() - .apply(getCursor(), m.getCoordinates().replace(), methodName, m.getArguments().get(0)); - } - - private boolean isSystemOutMethod(J.MethodInvocation mi) { - if (SYSTEM_OUT_PRINT.matches(mi)) { - Expression expression = mi.getSelect(); - if (expression instanceof J.FieldAccess) { - return isSystemOut(((J.FieldAccess) expression).getName()); + return Preconditions.check( + Preconditions.or( + new UsesMethod<>(SYSTEM_OUT_PRINT), + new UsesMethod<>(SYSTEM_OUT_PRINTLN) + ), + new JavaIsoVisitor() { + @Override + public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { + J.MethodInvocation m = super.visitMethodInvocation(method, ctx); + if (!isSystemOutMethod(m)) { + return m; + } + String methodName = m.getName().getSimpleName(); + return m.getArguments().isEmpty() ? + JavaTemplate.builder("IO.#{}()").build() + .apply(getCursor(), m.getCoordinates().replace(), methodName) : + JavaTemplate.builder("IO.#{}(#{any()})").build() + .apply(getCursor(), m.getCoordinates().replace(), methodName, m.getArguments().get(0)); } - if (expression instanceof J.Identifier) { - maybeRemoveImport("java.lang.System.out"); - return isSystemOut((J.Identifier) expression); + + private boolean isSystemOutMethod(J.MethodInvocation mi) { + if (SYSTEM_OUT_PRINT.matches(mi) || SYSTEM_OUT_PRINTLN.matches(mi)) { + Expression expression = mi.getSelect(); + if (expression instanceof J.FieldAccess) { + return isSystemOut(((J.FieldAccess) expression).getName()); + } + if (expression instanceof J.Identifier) { + maybeRemoveImport("java.lang.System.out"); + return isSystemOut((J.Identifier) expression); + } + } + return false; } - } - return false; - } - private boolean isSystemOut(J.Identifier identifier) { - return "out".equals(identifier.getSimpleName()) && - identifier.getFieldType() != null && - TypeUtils.isAssignableTo("java.lang.System", identifier.getFieldType().getOwner()); - } - }); + private boolean isSystemOut(J.Identifier identifier) { + return "out".equals(identifier.getSimpleName()) && + identifier.getFieldType() != null && + TypeUtils.isAssignableTo("java.lang.System", identifier.getFieldType().getOwner()); + } + }); } } diff --git a/src/test/java/org/openrewrite/java/migrate/io/ReplaceSystemOutWithIOPrintTest.java b/src/test/java/org/openrewrite/java/migrate/io/ReplaceSystemOutWithIOPrintTest.java index 8c88b91a25..b483b34475 100644 --- a/src/test/java/org/openrewrite/java/migrate/io/ReplaceSystemOutWithIOPrintTest.java +++ b/src/test/java/org/openrewrite/java/migrate/io/ReplaceSystemOutWithIOPrintTest.java @@ -245,4 +245,19 @@ void test() { ) ); } + + @Test + void doesNotReplacePrintf() { + rewriteRun( + java( + """ + class Example { + void test() { + System.out.printf("Hello%n"); + } + } + """ + ) + ); + } }