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, ExecutionContext> 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, ExecutionContext> 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, ExecutionContext> 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, ExecutionContext> 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, ExecutionContext> 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");
+ }
+ }
+ """
+ )
+ );
+ }
}