Skip to content

Commit

Permalink
Prefer JDK methods for Maps and Sets of an expected size (#651)
Browse files Browse the repository at this point in the history
* Prefer JDK methods for Maps and Sets of an expected size

* Also include Picnic's `InputStreamRulesRecipes` with `NoGuava`

* Rename test and move mismatched test

* Correctly set minimum Java versions in preconditions

* Tag first test as document example
  • Loading branch information
timtebeek authored Jan 2, 2025
1 parent 11ddbd7 commit 8428e3a
Show file tree
Hide file tree
Showing 7 changed files with 242 additions and 69 deletions.
2 changes: 2 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 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;
}
}
);
}
}
14 changes: 12 additions & 2 deletions src/main/resources/META-INF/rewrite/no-guava.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ description: >-
tags:
- guava
recipeList:
- org.openrewrite.java.migrate.guava.NoGuavaJava11
- org.openrewrite.java.migrate.guava.NoGuavaJava21
- org.openrewrite.java.migrate.guava.NoGuavaCreateTempDir
- org.openrewrite.java.migrate.guava.NoGuavaDirectExecutor
- org.openrewrite.java.migrate.guava.NoGuavaListsNewArrayList
Expand Down Expand Up @@ -62,6 +64,8 @@ recipeList:
- org.openrewrite.java.migrate.guava.PreferMathMultiplyExact
- org.openrewrite.java.migrate.guava.NoGuavaAtomicsNewReference

- tech.picnic.errorprone.refasterrules.InputStreamRulesRecipes

---
type: specs.openrewrite.org/v1beta/recipe
name: org.openrewrite.java.migrate.guava.NoGuavaJava11
Expand All @@ -73,8 +77,10 @@ description: >-
tags:
- guava
- java11
preconditions:
- org.openrewrite.java.search.HasJavaVersion:
version: "[11,)"
recipeList:
- org.openrewrite.java.migrate.guava.NoGuava
- org.openrewrite.java.migrate.guava.NoGuavaImmutableListOf
- org.openrewrite.java.migrate.guava.NoGuavaImmutableMapOf
- org.openrewrite.java.migrate.guava.NoGuavaImmutableSetOf
Expand All @@ -95,9 +101,13 @@ description: >-
tags:
- guava
- java21
preconditions:
- org.openrewrite.java.search.HasJavaVersion:
version: "[21,)"
recipeList:
- org.openrewrite.java.migrate.guava.NoGuavaJava11
- org.openrewrite.java.migrate.guava.NoMapsAndSetsWithExpectedSize
- org.openrewrite.java.migrate.guava.PreferMathClamp

---
type: specs.openrewrite.org/v1beta/recipe
name: org.openrewrite.java.migrate.guava.PreferJavaNioCharsetStandardCharsets
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,17 @@

import org.junit.jupiter.api.Test;
import org.openrewrite.Issue;
import org.openrewrite.config.Environment;
import org.openrewrite.java.JavaParser;
import org.openrewrite.test.RecipeSpec;
import org.openrewrite.test.RewriteTest;

import static org.openrewrite.java.Assertions.java;
import static org.openrewrite.java.Assertions.version;
import static org.openrewrite.java.Assertions.*;

class NoGuavaJava21Test implements RewriteTest {
@Override
public void defaults(RecipeSpec spec) {
spec.recipe(
Environment.builder()
.scanRuntimeClasspath("org.openrewrite.java.migrate.guava")
.build()
.activateRecipes("org.openrewrite.java.migrate.guava.NoGuavaJava21")
)
spec
.recipeFromResource("/META-INF/rewrite/no-guava.yml", "org.openrewrite.java.migrate.guava.NoGuava")
.parser(JavaParser.fromJavaVersion().classpath("guava"));
}

Expand Down Expand Up @@ -122,32 +116,30 @@ public float testMethod() {
@Test
void noGuavaImmutableOfException() {
rewriteRun(
version(
//language=java
java(
"""
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableMap;
//language=java
java(
"""
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableMap;
class A {
public Object getMap() {
return ImmutableMap.of("key", ImmutableSet.of("value1", "value2"));
}
}
""",
"""
import com.google.common.collect.ImmutableSet;
class A {
public Object getMap() {
return ImmutableMap.of("key", ImmutableSet.of("value1", "value2"));
}
}
""",
"""
import com.google.common.collect.ImmutableSet;
import java.util.Map;
import java.util.Map;
class A {
public Object getMap() {
return Map.of("key", ImmutableSet.of("value1", "value2"));
}
}
"""
),
21
class A {
public Object getMap() {
return Map.of("key", ImmutableSet.of("value1", "value2"));
}
}
""",
spec -> spec.markers(javaVersion(21))
)
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

import static org.openrewrite.java.Assertions.java;

class PreferJavaUtilObjectsTest implements RewriteTest {
class NoGuavaRefasterTest implements RewriteTest {
@Override
public void defaults(RecipeSpec spec) {
spec.recipe(new NoGuavaRefasterRecipes())
Expand Down Expand Up @@ -184,31 +184,4 @@ Object foo(Object obj) {
)
);
}

@Test
void moreObjectsFirstNonNullToObjectsRequireNonNullElse() {
rewriteRun(spec -> spec.recipeFromResource("/META-INF/rewrite/no-guava.yml", "org.openrewrite.java.migrate.guava.NoGuavaJava11"),
//language=java
java(
"""
import com.google.common.base.MoreObjects;
class A {
Object foo(Object obj) {
return MoreObjects.firstNonNull(obj, "default");
}
}
""",
"""
import java.util.Objects;
class A {
Object foo(Object obj) {
return Objects.requireNonNullElse(obj, "default");
}
}
"""
)
);
}
}
40 changes: 33 additions & 7 deletions src/test/java/org/openrewrite/java/migrate/guava/NoGuavaTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,52 @@
package org.openrewrite.java.migrate.guava;

import org.junit.jupiter.api.Test;
import org.openrewrite.DocumentExample;
import org.openrewrite.Issue;
import org.openrewrite.config.Environment;
import org.openrewrite.java.JavaParser;
import org.openrewrite.test.RecipeSpec;
import org.openrewrite.test.RewriteTest;

import static org.openrewrite.java.Assertions.java;
import static org.openrewrite.java.Assertions.javaVersion;

class NoGuavaTest implements RewriteTest {
@Override
public void defaults(RecipeSpec spec) {
spec.recipe(
Environment.builder()
.scanRuntimeClasspath("org.openrewrite.java.migrate.guava")
.build()
.activateRecipes("org.openrewrite.java.migrate.guava.NoGuava")
)
spec
.recipeFromResource("/META-INF/rewrite/no-guava.yml", "org.openrewrite.java.migrate.guava.NoGuava")
.parser(JavaParser.fromJavaVersion().classpath("guava"));
}

@DocumentExample
@Test
void moreObjectsFirstNonNullToObjectsRequireNonNullElse() {
rewriteRun(
//language=java
java(
"""
import com.google.common.base.MoreObjects;
class A {
Object foo(Object obj) {
return MoreObjects.firstNonNull(obj, "default");
}
}
""",
"""
import java.util.Objects;
class A {
Object foo(Object obj) {
return Objects.requireNonNullElse(obj, "default");
}
}
""",
spec -> spec.markers(javaVersion(11))
)
);
}

@Test
@Issue("https://github.com/openrewrite/rewrite-migrate-java/issues/39#issuecomment-910673213")
void preferJavaUtilObjectsHashCode() {
Expand Down
Loading

0 comments on commit 8428e3a

Please sign in to comment.