Skip to content

Commit

Permalink
Merge branch 'openrewrite:main' into lombok/convert-equals-negligently
Browse files Browse the repository at this point in the history
  • Loading branch information
timo-a authored Jan 15, 2025
2 parents 4194a9d + ef63ef1 commit 4d5d678
Show file tree
Hide file tree
Showing 28 changed files with 1,689 additions and 90 deletions.
23 changes: 19 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,25 @@
![Logo](https://github.com/openrewrite/rewrite/raw/main/doc/logo-oss.png)

### Migrate to later Java versions. Automatically.

<p align="center">
<a href="https://docs.openrewrite.org">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/openrewrite/rewrite/raw/main/doc/logo-oss-dark.svg">
<source media="(prefers-color-scheme: light)" srcset="https://github.com/openrewrite/rewrite/raw/main/doc/logo-oss-light.svg">
<img alt="OpenRewrite Logo" src="https://github.com/openrewrite/rewrite/raw/main/doc/logo-oss-light.svg" width='600px'>
</picture>
</a>
</p>

<div align="center">
<h1>rewrite-migrate-java</h1>
</div>

<div align="center">

<!-- Keep the gap above this line, otherwise they won't render correctly! -->
[![ci](https://github.com/openrewrite/rewrite-migrate-java/actions/workflows/ci.yml/badge.svg)](https://github.com/openrewrite/rewrite-migrate-java/actions/workflows/ci.yml)
[![Maven Central](https://img.shields.io/maven-central/v/org.openrewrite.recipe/rewrite-migrate-java.svg)](https://mvnrepository.com/artifact/org.openrewrite.recipe/rewrite-migrate-java)
[![Revved up by Develocity](https://img.shields.io/badge/Revved%20up%20by-Develocity-06A0CE?logo=Gradle&labelColor=02303A)](https://ge.openrewrite.org/scans)
[![Contributing Guide](https://img.shields.io/badge/Contributing-Guide-informational)](https://github.com/openrewrite/.github/blob/main/CONTRIBUTING.md)
</div>

### What is this?

Expand Down
7 changes: 7 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ dependencies {
runtimeOnly("org.openrewrite:rewrite-java-17")
runtimeOnly("org.openrewrite:rewrite-java-21")

runtimeOnly("tech.picnic.error-prone-support:error-prone-contrib:latest.release:recipes")

testImplementation("org.junit.jupiter:junit-jupiter-api:latest.release")
testImplementation("org.junit.jupiter:junit-jupiter-params:latest.release")
testImplementation("org.junit-pioneer:junit-pioneer:2.0.0")
Expand All @@ -54,8 +56,13 @@ dependencies {
testRuntimeOnly("com.fasterxml.jackson.datatype:jackson-datatype-jsr353")
testRuntimeOnly("com.fasterxml.jackson.core:jackson-core")
testRuntimeOnly("com.fasterxml.jackson.core:jackson-databind")
testRuntimeOnly("com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider")
testRuntimeOnly("com.fasterxml.jackson.module:jackson-module-jaxb-annotations")
testRuntimeOnly("commons-logging:commons-logging:1.3.2")
testRuntimeOnly("org.apache.logging.log4j:log4j-api:2.23.1")
testRuntimeOnly("org.apache.johnzon:johnzon-core:1.2.18")
testRuntimeOnly("org.codehaus.groovy:groovy:latest.release")
testRuntimeOnly("org.jboss.logging:jboss-logging:3.6.0.Final")
testRuntimeOnly("jakarta.annotation:jakarta.annotation-api:2.1.1")
testRuntimeOnly("org.springframework:spring-core:6.1.13")
testRuntimeOnly("com.google.code.findbugs:jsr305:3.0.2")
Expand Down
24 changes: 12 additions & 12 deletions src/main/java/org/openrewrite/java/migrate/UpdateSdkMan.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,17 @@
import org.openrewrite.text.PlainText;
import org.openrewrite.text.PlainTextParser;

import java.io.BufferedReader;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.toList;

@Value
@EqualsAndHashCode(callSuper = false)
Expand Down Expand Up @@ -102,15 +103,14 @@ public Tree visit(@Nullable Tree tree, ExecutionContext ctx) {
}

private List<String> readSdkmanJavaCandidates() {
URL resource = getClass().getResource("/sdkman-java.csv");
if (resource != null) {
try {
return Files.readAllLines(Paths.get(resource.toURI()));
} catch (IOException | URISyntaxException e) {
throw new RuntimeException(e);
}
try (InputStream resourceAsStream = UpdateSdkMan.class.getResourceAsStream("/sdkman-java.csv");
InputStreamReader inputStreamReader = new InputStreamReader(resourceAsStream, StandardCharsets.UTF_8);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) {
return bufferedReader.lines().collect(toList());
} catch (IOException e) {
throw new RuntimeException(e);
}
throw new IllegalStateException("Could not find /sdkman-java.csv file");

}
};
return Preconditions.check(new FindSourceFiles(".sdkmanrc"), visitor);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* Copyright 2025 the original author or authors.
* <p>
* 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
* <p>
* https://docs.moderne.io/licensing/moderne-source-available-license
* <p>
* 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.guava;

import org.openrewrite.ExecutionContext;
import org.openrewrite.Preconditions;
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;
import org.openrewrite.java.JavaTemplate;
import org.openrewrite.java.JavaVisitor;
import org.openrewrite.java.MethodMatcher;
import org.openrewrite.java.search.UsesJavaVersion;
import org.openrewrite.java.search.UsesMethod;
import org.openrewrite.java.tree.J;
import org.openrewrite.java.tree.JavaCoordinates;

public class NoMapsAndSetsWithExpectedSize extends Recipe {

private static final MethodMatcher NEW_HASHMAP = new MethodMatcher("com.google.common.collect.Maps newHashMapWithExpectedSize(int)", false);
private static final MethodMatcher NEW_LINKED_HASHMAP = new MethodMatcher("com.google.common.collect.Maps newLinkedHashMapWithExpectedSize(int)", false);
private static final MethodMatcher NEW_HASHSET = new MethodMatcher("com.google.common.collect.Sets newHashSetWithExpectedSize(int)", false);
private static final MethodMatcher NEW_LINKED_HASHSET = new MethodMatcher("com.google.common.collect.Sets newLinkedHashSetWithExpectedSize(int)", false);

@Override
public String getDisplayName() {
return "Prefer JDK methods for Maps and Sets of an expected size";
}

@Override
public String getDescription() {
return "Prefer Java 19+ methods to create Maps and Sets of an expected size instead of using Guava methods.";
}

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return Preconditions.check(
Preconditions.and(
new UsesJavaVersion<>(19),
Preconditions.or(
new UsesMethod<>(NEW_HASHMAP),
new UsesMethod<>(NEW_LINKED_HASHMAP),
new UsesMethod<>(NEW_HASHSET),
new UsesMethod<>(NEW_LINKED_HASHSET)
)
),
new JavaVisitor<ExecutionContext>() {
@Override
public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) {
J.MethodInvocation j = (J.MethodInvocation) super.visitMethodInvocation(method, ctx);
if (NEW_HASHMAP.matches(j)) {
maybeRemoveImport("com.google.common.collect.Maps");
maybeAddImport("java.util.HashMap");
JavaCoordinates coordinates = j.getCoordinates().replace();
return JavaTemplate.builder("new HashMap<>(#{any()})")
.imports("java.util.HashMap")
.build()
.apply(getCursor(), coordinates, j.getArguments().toArray());
} else if (NEW_LINKED_HASHMAP.matches(j)) {
maybeRemoveImport("com.google.common.collect.Maps");
maybeAddImport("java.util.LinkedHashMap");
JavaCoordinates coordinates = j.getCoordinates().replace();
return JavaTemplate.builder("new LinkedHashMap<>(#{any()})")
.imports("java.util.LinkedHashMap")
.build()
.apply(getCursor(), coordinates, j.getArguments().toArray());
} else if (NEW_HASHSET.matches(j)) {
maybeRemoveImport("com.google.common.collect.Sets");
maybeAddImport("java.util.HashSet");
JavaCoordinates coordinates = j.getCoordinates().replace();
return JavaTemplate.builder("new HashSet<>(#{any()})")
.imports("java.util.HashSet")
.build()
.apply(getCursor(), coordinates, j.getArguments().toArray());
} else if (NEW_LINKED_HASHSET.matches(j)) {
maybeRemoveImport("com.google.common.collect.Sets");
maybeAddImport("java.util.LinkedHashSet");
JavaCoordinates coordinates = j.getCoordinates().replace();
return JavaTemplate.builder("new LinkedHashSet<>(#{any()})")
.imports("java.util.LinkedHashSet")
.build()
.apply(getCursor(), coordinates, j.getArguments().toArray());
}
return j;
}
}
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations m

// Update existing @Column annotation
J.VariableDeclarations updatedVariable = (J.VariableDeclarations) new AddOrUpdateAnnotationAttribute(
"javax.persistence.Column", "name", "element", true, null)
"javax.persistence.Column", "name", "element", null, true, null)
.getVisitor().visit(multiVariable, ctx, getCursor().getParentTreeCursor());
return super.visitVariableDeclarations(updatedVariable, ctx);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* Copyright 2025 the original author or authors.
* <p>
* 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
* <p>
* https://docs.moderne.io/licensing/moderne-source-available-license
* <p>
* 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.lombok;

import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.Value;
import org.jspecify.annotations.Nullable;
import org.openrewrite.ExecutionContext;
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.JavaParser;
import org.openrewrite.java.JavaTemplate;
import org.openrewrite.java.tree.J;
import org.openrewrite.java.tree.TypeUtils;

import static java.util.Comparator.comparing;

@Value
@EqualsAndHashCode(callSuper = false)
public class UseNoArgsConstructor extends Recipe {

@Override
public String getDisplayName() {
//language=markdown
return "Use `@NoArgsConstructor` where applicable";
}

@Override
public String getDescription() {
//language=markdown
return "Prefer the Lombok `@NoArgsConstructor` annotation over explicitly written out constructors.";
}

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return new JavaIsoVisitor<ExecutionContext>() {
@Override
public J.@Nullable MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) {
if (method.isConstructor() &&
method.getParameters().get(0) instanceof J.Empty &&
method.getBody() != null && method.getBody().getStatements().isEmpty()) {
J.ClassDeclaration enclosing = getCursor().firstEnclosing(J.ClassDeclaration.class);
AccessLevel accessLevel = LombokUtils.getAccessLevel(method);
doAfterVisit(new JavaIsoVisitor<ExecutionContext>() {
@Override
public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) {
if (TypeUtils.isOfType(classDecl.getType(), enclosing.getType())) {
String template = "@NoArgsConstructor" + (accessLevel == AccessLevel.PUBLIC ?
"" : "(access = AccessLevel." + accessLevel.name() + ")");
maybeAddImport("lombok.AccessLevel");
maybeAddImport("lombok.NoArgsConstructor");
return JavaTemplate.builder(template)
.imports("lombok.*")
.javaParser(JavaParser.fromJavaVersion().classpath("lombok"))
.build()
.apply(getCursor(), classDecl.getCoordinates().addAnnotation(comparing(J.Annotation::getSimpleName)));
}
return super.visitClassDeclaration(classDecl, ctx);
}
});
return null;
}
return super.visitMethodDeclaration(method, ctx);
}
};
}
}
106 changes: 106 additions & 0 deletions src/main/java/org/openrewrite/java/migrate/lombok/log/LogVisitor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* Copyright 2025 the original author or authors.
* <p>
* 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
* <p>
* https://docs.moderne.io/licensing/moderne-source-available-license
* <p>
* 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.lombok.log;

import lombok.EqualsAndHashCode;
import org.jspecify.annotations.Nullable;
import org.openrewrite.ExecutionContext;
import org.openrewrite.java.*;
import org.openrewrite.java.tree.J;
import org.openrewrite.java.tree.TypeUtils;

import static java.util.Comparator.comparing;

@EqualsAndHashCode(callSuper = false)
class LogVisitor extends JavaIsoVisitor<ExecutionContext> {

private final String logType;
private final String factoryType;
private final MethodMatcher factoryMethodMatcher;
private final String logAnnotation;
@Nullable
private final String fieldName;

LogVisitor(String logType, String factoryMethodPattern, String logAnnotation, @Nullable String fieldName) {
this.logType = logType;
this.factoryType = factoryMethodPattern.substring(0, factoryMethodPattern.indexOf(' '));
this.factoryMethodMatcher = new MethodMatcher(factoryMethodPattern);
this.logAnnotation = logAnnotation;
this.fieldName = fieldName;
}

@Override
public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) {
J.ClassDeclaration visitClassDeclaration = super.visitClassDeclaration(classDecl, ctx);
if (visitClassDeclaration != classDecl) {
maybeRemoveImport(logType);
maybeRemoveImport(factoryType);
maybeAddImport(logAnnotation);
return JavaTemplate
.builder("@" + logAnnotation.substring(logAnnotation.lastIndexOf('.') + 1) + "\n")
.javaParser(JavaParser.fromJavaVersion().classpath("lombok"))
.imports(logAnnotation)
.build()
.apply(
updateCursor(visitClassDeclaration),
visitClassDeclaration.getCoordinates().addAnnotation(comparing(J.Annotation::getSimpleName)));
}
return classDecl;
}

@Override
public J.@Nullable VariableDeclarations visitVariableDeclarations(J.VariableDeclarations multiVariable, ExecutionContext ctx) {
if (!multiVariable.hasModifier(J.Modifier.Type.Private) ||
!multiVariable.hasModifier(J.Modifier.Type.Static) ||
!multiVariable.hasModifier(J.Modifier.Type.Final) ||
multiVariable.getVariables().size() != 1 ||
!TypeUtils.isAssignableTo(logType, multiVariable.getType())) {
return multiVariable;
}

// name needs to match the name of the field that lombok creates
J.VariableDeclarations.NamedVariable var = multiVariable.getVariables().get(0);
if (fieldName != null && !fieldName.equals(var.getSimpleName())) {
return multiVariable;
}

if (!factoryMethodMatcher.matches(var.getInitializer())) {
return multiVariable;
}

J.ClassDeclaration classDeclaration = getCursor().firstEnclosing(J.ClassDeclaration.class);
if (classDeclaration == null || classDeclaration.getType() == null) {
return multiVariable;
}

J.MethodInvocation methodCall = (J.MethodInvocation) var.getInitializer();
if (methodCall.getArguments().size() != 1 ||
!getFactoryParameter(classDeclaration.getSimpleName())
.equals(methodCall.getArguments().get(0).toString())) {
return multiVariable;
}

if (!"log".equals(var.getSimpleName())) {
doAfterVisit(new ChangeFieldName<>(classDeclaration.getType().getFullyQualifiedName(), var.getSimpleName(), "log"));
}

return null;
}

protected String getFactoryParameter(String className) {
return className + ".class";
}
}
Loading

0 comments on commit 4d5d678

Please sign in to comment.