Skip to content

Commit 76925b9

Browse files
authored
Add RedundantUtf8Charset recipe to drop redundant UTF-8 from Files calls (#1167)
* Add RedundantUtf8Charset recipe to remove UTF-8 from java.nio.file.Files calls The character-based java.nio.file.Files methods always default to UTF-8, so passing StandardCharsets.UTF_8 explicitly is redundant. Wire the recipe into java-version-11.yml, the lowest version where every targeted no-charset overload (readString/writeString are @SInCE 11) compiles. * Apply review feedback: 2026 headers, 'method calls' wording, drop redundant guard - Bump copyright headers to 2026 - End the display name with 'method calls' and regenerate recipes.csv - Remove the parameterNames size guard; it always matches parameterTypes length
1 parent c0d0f67 commit 76925b9

5 files changed

Lines changed: 401 additions & 5 deletions

File tree

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/*
2+
* Copyright 2026 the original author or authors.
3+
* <p>
4+
* Licensed under the Moderne Source Available License (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* <p>
8+
* https://docs.moderne.io/licensing/moderne-source-available-license
9+
* <p>
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.openrewrite.java.migrate.nio.file;
17+
18+
import lombok.Getter;
19+
import org.openrewrite.ExecutionContext;
20+
import org.openrewrite.Preconditions;
21+
import org.openrewrite.Recipe;
22+
import org.openrewrite.TreeVisitor;
23+
import org.openrewrite.java.JavaIsoVisitor;
24+
import org.openrewrite.java.MethodMatcher;
25+
import org.openrewrite.java.search.UsesType;
26+
import org.openrewrite.java.tree.Expression;
27+
import org.openrewrite.java.tree.J;
28+
import org.openrewrite.java.tree.JavaType;
29+
import org.openrewrite.java.tree.TypeUtils;
30+
31+
import java.util.ArrayList;
32+
import java.util.List;
33+
34+
public class RedundantUtf8Charset extends Recipe {
35+
36+
private static final List<MethodMatcher> MATCHERS = new ArrayList<>();
37+
38+
static {
39+
for (String signature : new String[]{
40+
"java.nio.file.Files readString(java.nio.file.Path, java.nio.charset.Charset)",
41+
"java.nio.file.Files writeString(java.nio.file.Path, java.lang.CharSequence, java.nio.charset.Charset, ..)",
42+
"java.nio.file.Files readAllLines(java.nio.file.Path, java.nio.charset.Charset)",
43+
"java.nio.file.Files write(java.nio.file.Path, java.lang.Iterable, java.nio.charset.Charset, ..)",
44+
"java.nio.file.Files lines(java.nio.file.Path, java.nio.charset.Charset)",
45+
"java.nio.file.Files newBufferedReader(java.nio.file.Path, java.nio.charset.Charset)",
46+
"java.nio.file.Files newBufferedWriter(java.nio.file.Path, java.nio.charset.Charset, ..)"}) {
47+
MATCHERS.add(new MethodMatcher(signature));
48+
}
49+
}
50+
51+
@Getter
52+
final String displayName = "Remove redundant `StandardCharsets.UTF_8` from `java.nio.file.Files` method calls";
53+
54+
@Getter
55+
final String description = "The character based `java.nio.file.Files` methods always default to UTF-8, so passing " +
56+
"`StandardCharsets.UTF_8` explicitly is redundant and can be removed.";
57+
58+
@Override
59+
public TreeVisitor<?, ExecutionContext> getVisitor() {
60+
return Preconditions.check(new UsesType<>("java.nio.charset.StandardCharsets", false), new JavaIsoVisitor<ExecutionContext>() {
61+
@Override
62+
public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) {
63+
J.MethodInvocation mi = super.visitMethodInvocation(method, ctx);
64+
if (MATCHERS.stream().noneMatch(m -> m.matches(mi))) {
65+
return mi;
66+
}
67+
List<Expression> arguments = mi.getArguments();
68+
for (int i = 0; i < arguments.size(); i++) {
69+
if (isUtf8(arguments.get(i))) {
70+
List<Expression> newArguments = new ArrayList<>(arguments);
71+
newArguments.remove(i);
72+
maybeRemoveImport("java.nio.charset.StandardCharsets");
73+
74+
JavaType.Method methodType = mi.getMethodType();
75+
if (methodType != null && i < methodType.getParameterTypes().size()) {
76+
List<JavaType> parameterTypes = new ArrayList<>(methodType.getParameterTypes());
77+
List<String> parameterNames = new ArrayList<>(methodType.getParameterNames());
78+
parameterTypes.remove(i);
79+
parameterNames.remove(i);
80+
methodType = methodType.withParameterTypes(parameterTypes).withParameterNames(parameterNames);
81+
}
82+
return mi.withArguments(newArguments)
83+
.withMethodType(methodType)
84+
.withName(mi.getName().withType(methodType));
85+
}
86+
}
87+
return mi;
88+
}
89+
90+
private boolean isUtf8(Expression argument) {
91+
if (!TypeUtils.isOfClassType(argument.getType(), "java.nio.charset.Charset")) {
92+
return false;
93+
}
94+
if (argument instanceof J.FieldAccess) {
95+
J.FieldAccess fieldAccess = (J.FieldAccess) argument;
96+
return "UTF_8".equals(fieldAccess.getSimpleName()) &&
97+
TypeUtils.isOfClassType(fieldAccess.getTarget().getType(), "java.nio.charset.StandardCharsets");
98+
}
99+
if (argument instanceof J.Identifier) {
100+
J.Identifier identifier = (J.Identifier) argument;
101+
JavaType.Variable fieldType = identifier.getFieldType();
102+
return "UTF_8".equals(identifier.getSimpleName()) && fieldType != null &&
103+
TypeUtils.isOfClassType(fieldType.getOwner(), "java.nio.charset.StandardCharsets");
104+
}
105+
return false;
106+
}
107+
});
108+
}
109+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
* Copyright 2026 the original author or authors.
3+
* <p>
4+
* Licensed under the Moderne Source Available License (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* <p>
8+
* https://docs.moderne.io/licensing/moderne-source-available-license
9+
* <p>
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
@NullMarked
17+
@NonNullFields
18+
package org.openrewrite.java.migrate.nio.file;
19+
20+
import org.jspecify.annotations.NullMarked;
21+
import org.openrewrite.internal.lang.NonNullFields;

src/main/resources/META-INF/rewrite/java-version-11.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ recipeList:
5757
- org.openrewrite.java.migrate.lombok.UpdateLombokToJava11
5858
- org.openrewrite.java.migrate.net.JavaNetAPIs
5959
- org.openrewrite.java.migrate.nio.file.PathsGetToPathOf
60+
- org.openrewrite.java.migrate.nio.file.RedundantUtf8Charset
6061
- org.openrewrite.java.migrate.sql.JavaSqlAPIs
6162
- org.openrewrite.java.migrate.javax.JavaxLangModelUtil
6263
- org.openrewrite.java.migrate.javax.JavaxManagementMonitorAPIs

0 commit comments

Comments
 (0)