org.openrewrite.apache.commons.io.ApacheIOUtilsUseExplicitCharset Maven / Gradle / Ivy
/*
* Copyright 2022 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.apache.commons.io;
import lombok.EqualsAndHashCode;
import lombok.Value;
import org.jspecify.annotations.Nullable;
import org.openrewrite.*;
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.JavaParser;
import org.openrewrite.java.JavaTemplate;
import org.openrewrite.java.MethodMatcher;
import org.openrewrite.java.search.UsesType;
import org.openrewrite.java.tree.J;
import java.util.*;
@Value
@EqualsAndHashCode(callSuper = false)
public class ApacheIOUtilsUseExplicitCharset extends Recipe {
private static final Map MATCHER_TEMPLATES = new HashMap<>();
// `IOUtils.toByteArray(java.lang.String)` is unique It's converted to String#getBytes()
private static final MethodMatcher STRING_GET_BYTES = new MethodMatcher("org.apache.commons.io.IOUtils toByteArray(java.lang.String)");
static {
MATCHER_TEMPLATES.put(new MethodMatcher("org.apache.commons.io.IOUtils copy(java.io.InputStream, java.io.Writer)"), "copy(#{any(java.io.InputStream)}, #{any(java.io.Writer)}, StandardCharsets.#{})");
MATCHER_TEMPLATES.put(new MethodMatcher("org.apache.commons.io.IOUtils copy(java.io.Reader, java.io.OutputStream)"), "copy(#{any(java.io.InputStream)}, #{any(java.io.OutputStream)}, StandardCharsets.#{})");
MATCHER_TEMPLATES.put(new MethodMatcher("org.apache.commons.io.IOUtils readLines(java.io.InputStream)"), "readLines(#{any(java.io.InputStream)}, StandardCharsets.#{})");
MATCHER_TEMPLATES.put(new MethodMatcher("org.apache.commons.io.IOUtils toByteArray(java.io.Reader)"), "toByteArray(#{any(java.io.Reader)}, StandardCharsets.#{})");
MATCHER_TEMPLATES.put(new MethodMatcher("org.apache.commons.io.IOUtils toCharArray(java.io.InputStream)"), "toCharArray(#{any(java.io.InputStream)}, StandardCharsets.#{})");
MATCHER_TEMPLATES.put(new MethodMatcher("org.apache.commons.io.IOUtils toInputStream(java.lang.CharSequence)"), "toInputStream(#{any(java.lang.CharSequence)}, StandardCharsets.#{})");
MATCHER_TEMPLATES.put(new MethodMatcher("org.apache.commons.io.IOUtils toInputStream(java.lang.String)"), "toInputStream(#{any(java.lang.String)}, StandardCharsets.#{})");
MATCHER_TEMPLATES.put(new MethodMatcher("org.apache.commons.io.IOUtils toString(byte[])"), "toString(#{anyArray(byte)}, StandardCharsets.#{})");
MATCHER_TEMPLATES.put(new MethodMatcher("org.apache.commons.io.IOUtils toString(java.io.InputStream)"), "toString(#{any(java.io.InputStream)}, StandardCharsets.#{})");
MATCHER_TEMPLATES.put(new MethodMatcher("org.apache.commons.io.IOUtils toString(java.net.URI)"), "toString(#{any(java.net.URI)}, StandardCharsets.#{})");
MATCHER_TEMPLATES.put(new MethodMatcher("org.apache.commons.io.IOUtils toString(java.net.URL)"), "toString(#{any(java.net.URL)}, StandardCharsets.#{})");
MATCHER_TEMPLATES.put(new MethodMatcher("org.apache.commons.io.IOUtils write(byte[], java.io.Writer)"), "write(#{anyArray(byte)}, #{any(java.io.Writer)}, StandardCharsets.#{})");
MATCHER_TEMPLATES.put(new MethodMatcher("org.apache.commons.io.IOUtils write(char[], java.io.OutputStream)"), "write(#{anyArray(char)}, #{any(java.io.OutputStream)}, StandardCharsets.#{})");
MATCHER_TEMPLATES.put(new MethodMatcher("org.apache.commons.io.IOUtils write(java.lang.CharSequence, java.io.OutputStream)"), "write(#{any(java.lang.CharSequence)}, #{any(java.io.OutputStream)}, StandardCharsets.#{})");
MATCHER_TEMPLATES.put(new MethodMatcher("org.apache.commons.io.IOUtils write(java.lang.String, java.io.OutputStream)"), "write(#{any(java.lang.String)}, #{any(java.io.OutputStream)}, StandardCharsets.#{})");
MATCHER_TEMPLATES.put(new MethodMatcher("org.apache.commons.io.IOUtils write(java.lang.StringBuffer, java.io.OutputStream)"), "write(#{any(java.lang.StringBuffer)}, #{any(java.io.OutputStream)}, StandardCharsets.#{})");
}
@Option(displayName = "Default encoding",
description = "The default encoding to use, must be a standard charset.",
example = "UTF_8",
required = false)
@Nullable
String encoding;
@Override
public String getDisplayName() {
return "Use IOUtils method that include their charset encoding";
}
@Override
public String getDescription() {
return "Use `IOUtils` method invocations that include the charset encoding instead of using the deprecated versions that do not include a charset encoding. (e.g. converts `IOUtils.readLines(inputStream)` to `IOUtils.readLines(inputStream, StandardCharsets.UTF_8)`.";
}
@Override
public Set getTags() {
return new HashSet<>(Arrays.asList("apache", "commons"));
}
@Override
public Validated validate() {
return super.validate().and(Validated.test("encoding", "Invalid encoding must be a standard Charset",
encoding, e -> e == null || e.matches("US_ASCII|ISO_8859_1|UTF_8|UTF_16BE|UTF_16LE|UTF_16")));
}
@Override
public TreeVisitor, ExecutionContext> getVisitor() {
return Preconditions.check(new UsesType<>("org.apache.commons.io.IOUtils", false), new JavaIsoVisitor() {
private final JavaParser.Builder, ?> javaParser = JavaParser.fromJavaVersion().classpath("commons-io");
@Override
public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) {
J.MethodInvocation mi = super.visitMethodInvocation(method, ctx);
if (STRING_GET_BYTES.matches(mi)) {
mi = mi.withSelect(method.getArguments().get(0));
//noinspection ConstantConditions
mi = mi.withMethodType(mi.getMethodType().withName("getBytes"));
mi = JavaTemplate.builder("#{any(String)}.getBytes(StandardCharsets.#{})}")
.javaParser(javaParser)
.imports("java.nio.charset.StandardCharsets")
.build()
.apply(updateCursor(mi),
mi.getCoordinates().replaceMethod(),
mi.getArguments().get(0), encoding == null ? "UTF_8" : encoding);
} else {
for (Map.Entry entry : MATCHER_TEMPLATES.entrySet()) {
if (entry.getKey().matches(mi)) {
List