Skip to content

Commit 8428e3a

Browse files
authored
Prefer JDK methods for Maps and Sets of an expected size (#651)
* 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
1 parent 11ddbd7 commit 8428e3a

File tree

7 files changed

+242
-69
lines changed

7 files changed

+242
-69
lines changed

build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ dependencies {
3535
runtimeOnly("org.openrewrite:rewrite-java-17")
3636
runtimeOnly("org.openrewrite:rewrite-java-21")
3737

38+
runtimeOnly("tech.picnic.error-prone-support:error-prone-contrib:latest.release:recipes")
39+
3840
testImplementation("org.junit.jupiter:junit-jupiter-api:latest.release")
3941
testImplementation("org.junit.jupiter:junit-jupiter-params:latest.release")
4042
testImplementation("org.junit-pioneer:junit-pioneer:2.0.0")
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*
2+
* Copyright 2025 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.guava;
17+
18+
import org.openrewrite.ExecutionContext;
19+
import org.openrewrite.Preconditions;
20+
import org.openrewrite.Recipe;
21+
import org.openrewrite.TreeVisitor;
22+
import org.openrewrite.java.JavaTemplate;
23+
import org.openrewrite.java.JavaVisitor;
24+
import org.openrewrite.java.MethodMatcher;
25+
import org.openrewrite.java.search.UsesJavaVersion;
26+
import org.openrewrite.java.search.UsesMethod;
27+
import org.openrewrite.java.tree.J;
28+
import org.openrewrite.java.tree.JavaCoordinates;
29+
30+
public class NoMapsAndSetsWithExpectedSize extends Recipe {
31+
32+
private static final MethodMatcher NEW_HASHMAP = new MethodMatcher("com.google.common.collect.Maps newHashMapWithExpectedSize(int)", false);
33+
private static final MethodMatcher NEW_LINKED_HASHMAP = new MethodMatcher("com.google.common.collect.Maps newLinkedHashMapWithExpectedSize(int)", false);
34+
private static final MethodMatcher NEW_HASHSET = new MethodMatcher("com.google.common.collect.Sets newHashSetWithExpectedSize(int)", false);
35+
private static final MethodMatcher NEW_LINKED_HASHSET = new MethodMatcher("com.google.common.collect.Sets newLinkedHashSetWithExpectedSize(int)", false);
36+
37+
@Override
38+
public String getDisplayName() {
39+
return "Prefer JDK methods for Maps and Sets of an expected size";
40+
}
41+
42+
@Override
43+
public String getDescription() {
44+
return "Prefer Java 19+ methods to create Maps and Sets of an expected size instead of using Guava methods.";
45+
}
46+
47+
@Override
48+
public TreeVisitor<?, ExecutionContext> getVisitor() {
49+
return Preconditions.check(
50+
Preconditions.and(
51+
new UsesJavaVersion<>(19),
52+
Preconditions.or(
53+
new UsesMethod<>(NEW_HASHMAP),
54+
new UsesMethod<>(NEW_LINKED_HASHMAP),
55+
new UsesMethod<>(NEW_HASHSET),
56+
new UsesMethod<>(NEW_LINKED_HASHSET)
57+
)
58+
),
59+
new JavaVisitor<ExecutionContext>() {
60+
@Override
61+
public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) {
62+
J.MethodInvocation j = (J.MethodInvocation) super.visitMethodInvocation(method, ctx);
63+
if (NEW_HASHMAP.matches(j)) {
64+
maybeRemoveImport("com.google.common.collect.Maps");
65+
maybeAddImport("java.util.HashMap");
66+
JavaCoordinates coordinates = j.getCoordinates().replace();
67+
return JavaTemplate.builder("new HashMap<>(#{any()})")
68+
.imports("java.util.HashMap")
69+
.build()
70+
.apply(getCursor(), coordinates, j.getArguments().toArray());
71+
} else if (NEW_LINKED_HASHMAP.matches(j)) {
72+
maybeRemoveImport("com.google.common.collect.Maps");
73+
maybeAddImport("java.util.LinkedHashMap");
74+
JavaCoordinates coordinates = j.getCoordinates().replace();
75+
return JavaTemplate.builder("new LinkedHashMap<>(#{any()})")
76+
.imports("java.util.LinkedHashMap")
77+
.build()
78+
.apply(getCursor(), coordinates, j.getArguments().toArray());
79+
} else if (NEW_HASHSET.matches(j)) {
80+
maybeRemoveImport("com.google.common.collect.Sets");
81+
maybeAddImport("java.util.HashSet");
82+
JavaCoordinates coordinates = j.getCoordinates().replace();
83+
return JavaTemplate.builder("new HashSet<>(#{any()})")
84+
.imports("java.util.HashSet")
85+
.build()
86+
.apply(getCursor(), coordinates, j.getArguments().toArray());
87+
} else if (NEW_LINKED_HASHSET.matches(j)) {
88+
maybeRemoveImport("com.google.common.collect.Sets");
89+
maybeAddImport("java.util.LinkedHashSet");
90+
JavaCoordinates coordinates = j.getCoordinates().replace();
91+
return JavaTemplate.builder("new LinkedHashSet<>(#{any()})")
92+
.imports("java.util.LinkedHashSet")
93+
.build()
94+
.apply(getCursor(), coordinates, j.getArguments().toArray());
95+
}
96+
return j;
97+
}
98+
}
99+
);
100+
}
101+
}

src/main/resources/META-INF/rewrite/no-guava.yml

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ description: >-
2525
tags:
2626
- guava
2727
recipeList:
28+
- org.openrewrite.java.migrate.guava.NoGuavaJava11
29+
- org.openrewrite.java.migrate.guava.NoGuavaJava21
2830
- org.openrewrite.java.migrate.guava.NoGuavaCreateTempDir
2931
- org.openrewrite.java.migrate.guava.NoGuavaDirectExecutor
3032
- org.openrewrite.java.migrate.guava.NoGuavaListsNewArrayList
@@ -62,6 +64,8 @@ recipeList:
6264
- org.openrewrite.java.migrate.guava.PreferMathMultiplyExact
6365
- org.openrewrite.java.migrate.guava.NoGuavaAtomicsNewReference
6466

67+
- tech.picnic.errorprone.refasterrules.InputStreamRulesRecipes
68+
6569
---
6670
type: specs.openrewrite.org/v1beta/recipe
6771
name: org.openrewrite.java.migrate.guava.NoGuavaJava11
@@ -73,8 +77,10 @@ description: >-
7377
tags:
7478
- guava
7579
- java11
80+
preconditions:
81+
- org.openrewrite.java.search.HasJavaVersion:
82+
version: "[11,)"
7683
recipeList:
77-
- org.openrewrite.java.migrate.guava.NoGuava
7884
- org.openrewrite.java.migrate.guava.NoGuavaImmutableListOf
7985
- org.openrewrite.java.migrate.guava.NoGuavaImmutableMapOf
8086
- org.openrewrite.java.migrate.guava.NoGuavaImmutableSetOf
@@ -95,9 +101,13 @@ description: >-
95101
tags:
96102
- guava
97103
- java21
104+
preconditions:
105+
- org.openrewrite.java.search.HasJavaVersion:
106+
version: "[21,)"
98107
recipeList:
99-
- org.openrewrite.java.migrate.guava.NoGuavaJava11
108+
- org.openrewrite.java.migrate.guava.NoMapsAndSetsWithExpectedSize
100109
- org.openrewrite.java.migrate.guava.PreferMathClamp
110+
101111
---
102112
type: specs.openrewrite.org/v1beta/recipe
103113
name: org.openrewrite.java.migrate.guava.PreferJavaNioCharsetStandardCharsets

src/test/java/org/openrewrite/java/migrate/guava/NoGuavaJava21Test.java

Lines changed: 24 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -17,23 +17,17 @@
1717

1818
import org.junit.jupiter.api.Test;
1919
import org.openrewrite.Issue;
20-
import org.openrewrite.config.Environment;
2120
import org.openrewrite.java.JavaParser;
2221
import org.openrewrite.test.RecipeSpec;
2322
import org.openrewrite.test.RewriteTest;
2423

25-
import static org.openrewrite.java.Assertions.java;
26-
import static org.openrewrite.java.Assertions.version;
24+
import static org.openrewrite.java.Assertions.*;
2725

2826
class NoGuavaJava21Test implements RewriteTest {
2927
@Override
3028
public void defaults(RecipeSpec spec) {
31-
spec.recipe(
32-
Environment.builder()
33-
.scanRuntimeClasspath("org.openrewrite.java.migrate.guava")
34-
.build()
35-
.activateRecipes("org.openrewrite.java.migrate.guava.NoGuavaJava21")
36-
)
29+
spec
30+
.recipeFromResource("/META-INF/rewrite/no-guava.yml", "org.openrewrite.java.migrate.guava.NoGuava")
3731
.parser(JavaParser.fromJavaVersion().classpath("guava"));
3832
}
3933

@@ -122,32 +116,30 @@ public float testMethod() {
122116
@Test
123117
void noGuavaImmutableOfException() {
124118
rewriteRun(
125-
version(
126-
//language=java
127-
java(
128-
"""
129-
import com.google.common.collect.ImmutableSet;
130-
import com.google.common.collect.ImmutableMap;
119+
//language=java
120+
java(
121+
"""
122+
import com.google.common.collect.ImmutableSet;
123+
import com.google.common.collect.ImmutableMap;
131124
132-
class A {
133-
public Object getMap() {
134-
return ImmutableMap.of("key", ImmutableSet.of("value1", "value2"));
135-
}
136-
}
137-
""",
138-
"""
139-
import com.google.common.collect.ImmutableSet;
125+
class A {
126+
public Object getMap() {
127+
return ImmutableMap.of("key", ImmutableSet.of("value1", "value2"));
128+
}
129+
}
130+
""",
131+
"""
132+
import com.google.common.collect.ImmutableSet;
140133
141-
import java.util.Map;
134+
import java.util.Map;
142135
143-
class A {
144-
public Object getMap() {
145-
return Map.of("key", ImmutableSet.of("value1", "value2"));
146-
}
147-
}
148-
"""
149-
),
150-
21
136+
class A {
137+
public Object getMap() {
138+
return Map.of("key", ImmutableSet.of("value1", "value2"));
139+
}
140+
}
141+
""",
142+
spec -> spec.markers(javaVersion(21))
151143
)
152144
);
153145
}

src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilObjectsTest.java renamed to src/test/java/org/openrewrite/java/migrate/guava/NoGuavaRefasterTest.java

Lines changed: 1 addition & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323

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

26-
class PreferJavaUtilObjectsTest implements RewriteTest {
26+
class NoGuavaRefasterTest implements RewriteTest {
2727
@Override
2828
public void defaults(RecipeSpec spec) {
2929
spec.recipe(new NoGuavaRefasterRecipes())
@@ -184,31 +184,4 @@ Object foo(Object obj) {
184184
)
185185
);
186186
}
187-
188-
@Test
189-
void moreObjectsFirstNonNullToObjectsRequireNonNullElse() {
190-
rewriteRun(spec -> spec.recipeFromResource("/META-INF/rewrite/no-guava.yml", "org.openrewrite.java.migrate.guava.NoGuavaJava11"),
191-
//language=java
192-
java(
193-
"""
194-
import com.google.common.base.MoreObjects;
195-
196-
class A {
197-
Object foo(Object obj) {
198-
return MoreObjects.firstNonNull(obj, "default");
199-
}
200-
}
201-
""",
202-
"""
203-
import java.util.Objects;
204-
205-
class A {
206-
Object foo(Object obj) {
207-
return Objects.requireNonNullElse(obj, "default");
208-
}
209-
}
210-
"""
211-
)
212-
);
213-
}
214187
}

src/test/java/org/openrewrite/java/migrate/guava/NoGuavaTest.java

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,26 +16,52 @@
1616
package org.openrewrite.java.migrate.guava;
1717

1818
import org.junit.jupiter.api.Test;
19+
import org.openrewrite.DocumentExample;
1920
import org.openrewrite.Issue;
20-
import org.openrewrite.config.Environment;
2121
import org.openrewrite.java.JavaParser;
2222
import org.openrewrite.test.RecipeSpec;
2323
import org.openrewrite.test.RewriteTest;
2424

2525
import static org.openrewrite.java.Assertions.java;
26+
import static org.openrewrite.java.Assertions.javaVersion;
2627

2728
class NoGuavaTest implements RewriteTest {
2829
@Override
2930
public void defaults(RecipeSpec spec) {
30-
spec.recipe(
31-
Environment.builder()
32-
.scanRuntimeClasspath("org.openrewrite.java.migrate.guava")
33-
.build()
34-
.activateRecipes("org.openrewrite.java.migrate.guava.NoGuava")
35-
)
31+
spec
32+
.recipeFromResource("/META-INF/rewrite/no-guava.yml", "org.openrewrite.java.migrate.guava.NoGuava")
3633
.parser(JavaParser.fromJavaVersion().classpath("guava"));
3734
}
3835

36+
@DocumentExample
37+
@Test
38+
void moreObjectsFirstNonNullToObjectsRequireNonNullElse() {
39+
rewriteRun(
40+
//language=java
41+
java(
42+
"""
43+
import com.google.common.base.MoreObjects;
44+
45+
class A {
46+
Object foo(Object obj) {
47+
return MoreObjects.firstNonNull(obj, "default");
48+
}
49+
}
50+
""",
51+
"""
52+
import java.util.Objects;
53+
54+
class A {
55+
Object foo(Object obj) {
56+
return Objects.requireNonNullElse(obj, "default");
57+
}
58+
}
59+
""",
60+
spec -> spec.markers(javaVersion(11))
61+
)
62+
);
63+
}
64+
3965
@Test
4066
@Issue("https://github.com/openrewrite/rewrite-migrate-java/issues/39#issuecomment-910673213")
4167
void preferJavaUtilObjectsHashCode() {

0 commit comments

Comments
 (0)