Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ dependencies {
implementation("org.openrewrite:rewrite-gradle")
implementation("org.openrewrite.recipe:rewrite-github-actions:$rewriteVersion")
implementation("org.openrewrite.recipe:rewrite-java-dependencies:$rewriteVersion")
implementation("org.openrewrite.recipe:rewrite-static-analysis:$rewriteVersion")
implementation("org.openrewrite.gradle.tooling:model:$rewriteVersion")

runtimeOnly("org.openrewrite:rewrite-java-8")
Expand Down
29 changes: 10 additions & 19 deletions src/main/java/org/openrewrite/java/migrate/AddJDeprScanPlugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,14 @@

import lombok.Getter;
import lombok.RequiredArgsConstructor;

import org.openrewrite.*;
import org.openrewrite.internal.ListUtils;
import org.openrewrite.internal.StringUtils;
import org.openrewrite.internal.lang.Nullable;
import org.openrewrite.maven.AddPlugin;
import org.openrewrite.maven.MavenIsoVisitor;
import org.openrewrite.maven.MavenVisitor;
import org.openrewrite.maven.tree.MavenResolutionResult;
import org.openrewrite.xml.tree.Xml;

import java.time.Duration;
import java.util.List;

/**
* This imperative recipe will add the jdeprscan plugin to a maven project. In the case of a multi-module project,
* this recipe will attempt to add the plugin to only the top level project.
Expand All @@ -52,19 +48,14 @@ public String getDescription() {
}

@Override
public Duration getEstimatedEffortPerOccurrence() {
return Duration.ofMinutes(5);
}

@Override
protected List<SourceFile> visit(List<SourceFile> before, ExecutionContext ctx) {
return ListUtils.map(before, s -> {
if ("pom.xml".equals(s.getSourcePath().toString())
&& s.getMarkers().findFirst(MavenResolutionResult.class).isPresent()) {
return (SourceFile) new AddJDeprScanPluginVisitor().visit(s, ctx);
public TreeVisitor<?, ExecutionContext> getVisitor() {
return new MavenIsoVisitor<ExecutionContext>() {
@Override
public @Nullable Xml preVisit(Xml tree, ExecutionContext ctx) {
stopAfterPreVisit();
return new AddJDeprScanPluginVisitor().visit(tree, ctx);
}
return s;
});
};
}

private class AddJDeprScanPluginVisitor extends MavenVisitor<ExecutionContext> {
Expand All @@ -78,7 +69,7 @@ public Xml visitDocument(Xml.Document document, ExecutionContext ctx) {
String.format("<configuration>%n <release>%s</release>%n</configuration>",
StringUtils.isNullOrEmpty(getRelease()) ? "11" : getRelease()),
null,
null));
null).getVisitor());
return document;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@
import org.openrewrite.xml.XPathMatcher;
import org.openrewrite.xml.tree.Xml;

import java.time.Duration;

/**
* This imperative recipe will add the maven jar plugin to a maven project. The maven jar plugin will be configured to suppress
* Illegal Reflection Warnings. In the case of a multi-module project, this recipe will attempt to add the plugin to only the top level project.
Expand Down Expand Up @@ -56,12 +54,7 @@ public String getDescription() {
}

@Override
public Duration getEstimatedEffortPerOccurrence() {
return Duration.ofMinutes(5);
}

@Override
protected TreeVisitor<?, ExecutionContext> getVisitor() {
public TreeVisitor<?, ExecutionContext> getVisitor() {
return new MavenIsoVisitor<ExecutionContext>() {
@Override
public Xml.Tag visitTag(Xml.Tag tag, ExecutionContext ctx) {
Expand All @@ -82,7 +75,7 @@ public Xml.Tag visitTag(Xml.Tag tag, ExecutionContext ctx) {
" </archive>\n" +
"</configuration>";

doAfterVisit(new AddPlugin(groupId, artifactId, version, configuration, null, null));
doAfterVisit(new AddPlugin(groupId, artifactId, version, configuration, null, null).getVisitor());
}
}
return t;
Expand Down
93 changes: 51 additions & 42 deletions src/main/java/org/openrewrite/java/migrate/UpgradeJavaVersion.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
import org.openrewrite.*;
import org.openrewrite.gradle.IsBuildGradle;
import org.openrewrite.gradle.UpdateJavaCompatibility;
import org.openrewrite.internal.ListUtils;
import org.openrewrite.groovy.tree.G;
import org.openrewrite.internal.lang.Nullable;
import org.openrewrite.java.marker.JavaVersion;
import org.openrewrite.maven.MavenVisitor;
import org.openrewrite.xml.XPathMatcher;
Expand All @@ -41,9 +42,9 @@ public String getDisplayName() {
@Override
public String getDescription() {
return "Upgrade build plugin configuration to use the specified Java version. " +
"This recipe changes `java.toolchain.languageVersion` in `build.gradle(.kts)` of gradle projects, " +
"or maven-compiler-plugin target version and related settings. " +
"Will not downgrade if the version is newer than the specified version.";
"This recipe changes `java.toolchain.languageVersion` in `build.gradle(.kts)` of gradle projects, " +
"or maven-compiler-plugin target version and related settings. " +
"Will not downgrade if the version is newer than the specified version.";
}

@Option(displayName = "Java version",
Expand All @@ -52,47 +53,55 @@ public String getDescription() {
Integer version;

@Override
protected List<SourceFile> visit(List<SourceFile> before, ExecutionContext ctx) {
String newVersion = this.version.toString();
TreeVisitor<?, ExecutionContext> gradleUpdateJavaVersionVisitor = new UpdateJavaCompatibility(version, null, null).getVisitor();
MavenUpdateJavaVersionVisitor mavenUpdateVisitor = new MavenUpdateJavaVersionVisitor();

public TreeVisitor<?, ExecutionContext> getVisitor() {
String newVersion = version.toString();
Map<JavaVersion, JavaVersion> updatedMarkers = new HashMap<>();
return ListUtils.map(before, s -> {
Optional<JavaVersion> maybeJavaVersion = s.getMarkers().findFirst(JavaVersion.class);
if (!maybeJavaVersion.isPresent()) {
return s;
}

JavaVersion javaVersion = maybeJavaVersion.get();
if (javaVersion.getMajorVersion() >= version) {
return s;
}
return new TreeVisitor<Tree, ExecutionContext>() {
@Override
public @Nullable Tree visit(@Nullable Tree tree, ExecutionContext ctx) {
if (!(tree instanceof SourceFile)) {
return tree;
}
SourceFile source = (SourceFile) tree;

Optional<JavaVersion> maybeJavaVersion = source.getMarkers().findFirst(JavaVersion.class);
if (maybeJavaVersion.isPresent() && maybeJavaVersion.get().getMajorVersion() >= version) {
// No change if try to downgrade java version, or on same java version.
return source;
}

if (source instanceof G.CompilationUnit && new IsBuildGradle<ExecutionContext>().visit(source, ctx) != source) {
source = (SourceFile) new UpdateJavaCompatibility(version, null, null).getVisitor().visitNonNull(source, ctx);
} else if (source instanceof Xml.Document) {
source = (SourceFile) new MavenUpdateJavaVersionVisitor().visitNonNull(source, ctx);
}

if (maybeJavaVersion.isPresent()) {
source =
source.withMarkers(source.getMarkers().setByType(updatedMarkers.computeIfAbsent(maybeJavaVersion.get(),
m -> m.withSourceCompatibility(newVersion).withTargetCompatibility(newVersion))));
}

s = (SourceFile) mavenUpdateVisitor.visitNonNull(s, ctx);
if (new IsBuildGradle<ExecutionContext>().visit(s, ctx) != s) {
s = (SourceFile) gradleUpdateJavaVersionVisitor.visitNonNull(s, ctx);
return source;
}
s = s.withMarkers(s.getMarkers().setByType(updatedMarkers.computeIfAbsent(javaVersion,
m -> m.withSourceCompatibility(newVersion).withTargetCompatibility(newVersion))));
return s;
});
};
}

private static final List<String> JAVA_VERSION_XPATHS = Arrays.asList(
"/project/properties/java.version",
"/project/properties/jdk.version",
"/project/properties/javaVersion",
"/project/properties/jdkVersion",
"/project/properties/maven.compiler.source",
"/project/properties/maven.compiler.target",
"/project/properties/maven.compiler.release",
"/project/build/plugins/plugin[artifactId='maven-compiler-plugin']/configuration/source",
"/project/build/plugins/plugin[artifactId='maven-compiler-plugin']/configuration/target",
"/project/build/plugins/plugin[artifactId='maven-compiler-plugin']/configuration/release");
"/project/properties/java.version",
"/project/properties/jdk.version",
"/project/properties/javaVersion",
"/project/properties/jdkVersion",
"/project/properties/maven.compiler.source",
"/project/properties/maven.compiler.target",
"/project/properties/maven.compiler.release",
"/project/build/plugins/plugin[artifactId='maven-compiler-plugin']/configuration/source",
"/project/build/plugins/plugin[artifactId='maven-compiler-plugin']/configuration/target",
"/project/build/plugins/plugin[artifactId='maven-compiler-plugin']/configuration/release");

private static final List<XPathMatcher> JAVA_VERSION_XPATH_MATCHERS =
JAVA_VERSION_XPATHS.stream().map(XPathMatcher::new).collect(Collectors.toList());
JAVA_VERSION_XPATHS.stream().map(XPathMatcher::new).collect(Collectors.toList());


private class MavenUpdateJavaVersionVisitor extends MavenVisitor<ExecutionContext> {
Expand All @@ -102,13 +111,13 @@ public Xml visitTag(Xml.Tag tag, ExecutionContext ctx) {

if (JAVA_VERSION_XPATH_MATCHERS.stream().anyMatch(matcher -> matcher.matches(getCursor()))) {
Optional<Float> maybeVersion = tag.getValue().flatMap(
value -> {
try {
return Optional.of(Float.parseFloat(value));
} catch (NumberFormatException e) {
return Optional.empty();
value -> {
try {
return Optional.of(Float.parseFloat(value));
} catch (NumberFormatException e) {
return Optional.empty();
}
}
}
);

if (!maybeVersion.isPresent()) {
Expand Down
69 changes: 36 additions & 33 deletions src/main/java/org/openrewrite/java/migrate/UseJavaUtilBase64.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,20 @@
package org.openrewrite.java.migrate;

import com.fasterxml.jackson.annotation.JsonCreator;
import org.openrewrite.*;
import org.openrewrite.java.*;
import org.openrewrite.java.cleanup.UnnecessaryCatch;
import org.openrewrite.ExecutionContext;
import org.openrewrite.Preconditions;
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;
import org.openrewrite.java.ChangeType;
import org.openrewrite.java.JavaTemplate;
import org.openrewrite.java.JavaVisitor;
import org.openrewrite.java.MethodMatcher;
import org.openrewrite.java.search.UsesType;
import org.openrewrite.java.tree.J;
import org.openrewrite.java.tree.JavaSourceFile;
import org.openrewrite.java.tree.JavaType;
import org.openrewrite.marker.Markup;
import org.openrewrite.staticanalysis.UnnecessaryCatch;

import java.util.Base64;

Expand All @@ -39,15 +45,7 @@ public String getDisplayName() {
public String getDescription() {
//language=markdown
return "Prefer `java.util.Base64` instead of using `sun.misc` in Java 8 or higher. `sun.misc` is not exported " +
"by the Java module system and accessing this class will result in a warning in Java 11 and an error in Java 17.";
}

@Override
protected TreeVisitor<?, ExecutionContext> getSingleSourceApplicableTest() {
return Applicability.or(
new UsesType<>(sunPackage + ".BASE64Encoder", false),
new UsesType<>(sunPackage + ".BASE64Decoder", false)
);
"by the Java module system and accessing this class will result in a warning in Java 11 and an error in Java 17.";
}

public UseJavaUtilBase64(String sunPackage) {
Expand All @@ -60,34 +58,38 @@ public UseJavaUtilBase64() {
}

@Override
protected JavaVisitor<ExecutionContext> getVisitor() {

public TreeVisitor<?, ExecutionContext> getVisitor() {
TreeVisitor<?, ExecutionContext> check = Preconditions.or(
new UsesType<>(sunPackage + ".BASE64Encoder", false),
new UsesType<>(sunPackage + ".BASE64Decoder", false)
);
MethodMatcher base64EncodeMethod = new MethodMatcher(sunPackage + ".CharacterEncoder *(byte[])");
MethodMatcher base64DecodeBuffer = new MethodMatcher(sunPackage + ".CharacterDecoder decodeBuffer(String)");

MethodMatcher newBase64Encoder = new MethodMatcher(sunPackage + ".BASE64Encoder <constructor>()");
MethodMatcher newBase64Decoder = new MethodMatcher(sunPackage + ".BASE64Decoder <constructor>()");

return new JavaVisitor<ExecutionContext>() {
final JavaTemplate getDecoderTemplate = JavaTemplate.builder(this::getCursor, "Base64.getDecoder()")
return Preconditions.check(check, new JavaVisitor<ExecutionContext>() {
final JavaTemplate getDecoderTemplate = JavaTemplate.builder("Base64.getDecoder()")
.context(this::getCursor)
.imports("java.util.Base64")
.build();

final JavaTemplate encodeToString = JavaTemplate.builder(this::getCursor, "Base64.getEncoder().encodeToString(#{anyArray(byte)})")
final JavaTemplate encodeToString = JavaTemplate.builder("Base64.getEncoder().encodeToString(#{anyArray(byte)})")
.imports("java.util.Base64")
.build();

final JavaTemplate decode = JavaTemplate.builder(this::getCursor, "Base64.getDecoder().decode(#{any(String)})")
final JavaTemplate decode = JavaTemplate.builder("Base64.getDecoder().decode(#{any(String)})")
.imports("java.util.Base64")
.build();

@Override
public J visitJavaSourceFile(JavaSourceFile cu, ExecutionContext ctx) {
if(alreadyUsingIncompatibleBase64(cu)) {
public J visitCompilationUnit(J.CompilationUnit cu, ExecutionContext ctx) {
if (alreadyUsingIncompatibleBase64(cu)) {
return Markup.warn(cu, new IllegalStateException(
"Already using a class named Base64 other than java.util.Base64. Manual intervention required."));
}
JavaSourceFile c = (JavaSourceFile) super.visitJavaSourceFile(cu, ctx);
J.CompilationUnit c = (J.CompilationUnit) super.visitCompilationUnit(cu, ctx);

c = (J.CompilationUnit) new ChangeType(sunPackage + ".BASE64Encoder", "java.util.Base64$Encoder", true)
.getVisitor().visitNonNull(c, ctx);
Expand All @@ -100,20 +102,20 @@ public J visitJavaSourceFile(JavaSourceFile cu, ExecutionContext ctx) {
public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) {
J.MethodInvocation m = (J.MethodInvocation) super.visitMethodInvocation(method, ctx);
if (base64EncodeMethod.matches(m) &&
("encode".equals(method.getSimpleName()) || "encodeBuffer".equals(method.getSimpleName()))) {
m = m.withTemplate(encodeToString, m.getCoordinates().replace(), method.getArguments().get(0));
("encode".equals(method.getSimpleName()) || "encodeBuffer".equals(method.getSimpleName()))) {
m = m.withTemplate(encodeToString, getCursor(), m.getCoordinates().replace(), method.getArguments().get(0));
if (method.getSelect() instanceof J.Identifier) {
m = m.withSelect(method.getSelect());
}
} else if (base64DecodeBuffer.matches(method)) {
m = m.withTemplate(decode, m.getCoordinates().replace(), method.getArguments().get(0));
m = m.withTemplate(decode, getCursor(), m.getCoordinates().replace(), method.getArguments().get(0));
if (method.getSelect() instanceof J.Identifier) {
m = m.withSelect(method.getSelect());
}
// Note: The sun.misc.CharacterDecoder#decodeBuffer throws an IOException, whereas the java
// Base64Decoder.decode does not throw a checked exception. If this recipe converts decode, we
// may need to remove the catch or completely unwrap a try/catch.
doAfterVisit(new UnnecessaryCatch(false));
doAfterVisit(new UnnecessaryCatch(false).getVisitor());
}
return m;
}
Expand All @@ -126,23 +128,24 @@ public J visitNewClass(J.NewClass newClass, ExecutionContext ctx) {
return c.withTemplate(
JavaTemplate.compile(this, "getEncoder",
() -> Base64.getEncoder()).build(),
getCursor(),
c.getCoordinates().replace()
);
} else if (newBase64Decoder.matches(c)) {
return c.withTemplate(getDecoderTemplate, c.getCoordinates().replace());
return c.withTemplate(getDecoderTemplate, getCursor(), c.getCoordinates().replace());
}
return c;
}
};
});
}

private boolean alreadyUsingIncompatibleBase64(JavaSourceFile cu) {
return cu.getClasses().stream().anyMatch(it -> "Base64".equals(it.getSimpleName())) ||
cu.getTypesInUse().getTypesInUse().stream()
.filter(it -> it instanceof JavaType.FullyQualified)
.map(JavaType.FullyQualified.class::cast)
.map(JavaType.FullyQualified::getFullyQualifiedName)
.filter(it -> !"java.util.Base64".equals(it))
.anyMatch(it -> it.endsWith(".Base64"));
cu.getTypesInUse().getTypesInUse().stream()
.filter(it -> it instanceof JavaType.FullyQualified)
.map(JavaType.FullyQualified.class::cast)
.map(JavaType.FullyQualified::getFullyQualifiedName)
.filter(it -> !"java.util.Base64".equals(it))
.anyMatch(it -> it.endsWith(".Base64"));
}
}
Loading