From 997ff79d1ee9720ffdf5bfa68f1e6e5bc91317c2 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Sat, 25 Oct 2025 14:36:55 +0200 Subject: [PATCH 1/5] Only convert Guava Predicate when not used as method argument --- .../java/migrate/guava/NoGuavaPredicate.java | 47 +++++ .../resources/META-INF/rewrite/no-guava.yml | 4 +- .../migrate/guava/NoGuavaPredicateTest.java | 184 ++++++++++++++++++ .../guava/PreferJavaUtilPredicateTest.java | 30 --- 4 files changed, 232 insertions(+), 33 deletions(-) create mode 100644 src/main/java/org/openrewrite/java/migrate/guava/NoGuavaPredicate.java create mode 100644 src/test/java/org/openrewrite/java/migrate/guava/NoGuavaPredicateTest.java diff --git a/src/main/java/org/openrewrite/java/migrate/guava/NoGuavaPredicate.java b/src/main/java/org/openrewrite/java/migrate/guava/NoGuavaPredicate.java new file mode 100644 index 0000000000..76875f2af8 --- /dev/null +++ b/src/main/java/org/openrewrite/java/migrate/guava/NoGuavaPredicate.java @@ -0,0 +1,47 @@ +/* + * Copyright 2025 the original author or authors. + *

+ * 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 + *

+ * https://docs.moderne.io/licensing/moderne-source-available-license + *

+ * 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.ChangeType; +import org.openrewrite.java.search.UsesMethod; + +public class NoGuavaPredicate extends Recipe { + @Override + public String getDisplayName() { + return "Change Guava's `Predicate` into `java.util.function.Predicate` where possible"; + } + + @Override + public String getDescription() { + return "Change the type only where no methods are used that explicitly require a Guava `Predicate`."; + } + + @Override + public TreeVisitor getVisitor() { + return Preconditions.check( + Preconditions.not(new UsesMethod<>("*..* *(.., com.google.common.base.Predicate)")), + new ChangeType( + "com.google.common.base.Predicate", + "java.util.function.Predicate", + false) + .getVisitor() + ); + } +} diff --git a/src/main/resources/META-INF/rewrite/no-guava.yml b/src/main/resources/META-INF/rewrite/no-guava.yml index 002d73ba50..91371b033b 100644 --- a/src/main/resources/META-INF/rewrite/no-guava.yml +++ b/src/main/resources/META-INF/rewrite/no-guava.yml @@ -193,9 +193,7 @@ recipeList: methodPattern: com.google.common.base.Predicate apply(..) newMethodName: test matchOverrides: true - - org.openrewrite.java.ChangeType: - oldFullyQualifiedTypeName: com.google.common.base.Predicate - newFullyQualifiedTypeName: java.util.function.Predicate + - org.openrewrite.java.migrate.guava.NoGuavaPredicate --- type: specs.openrewrite.org/v1beta/recipe diff --git a/src/test/java/org/openrewrite/java/migrate/guava/NoGuavaPredicateTest.java b/src/test/java/org/openrewrite/java/migrate/guava/NoGuavaPredicateTest.java new file mode 100644 index 0000000000..17b878562a --- /dev/null +++ b/src/test/java/org/openrewrite/java/migrate/guava/NoGuavaPredicateTest.java @@ -0,0 +1,184 @@ +/* + * Copyright 2025 the original author or authors. + *

+ * 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 + *

+ * https://docs.moderne.io/licensing/moderne-source-available-license + *

+ * 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.junit.jupiter.api.Test; +import org.openrewrite.DocumentExample; +import org.openrewrite.InMemoryExecutionContext; +import org.openrewrite.Issue; +import org.openrewrite.java.JavaParser; +import org.openrewrite.test.RecipeSpec; +import org.openrewrite.test.RewriteTest; + +import static org.openrewrite.java.Assertions.java; + +class NoGuavaPredicateTest implements RewriteTest { + @Override + public void defaults(RecipeSpec spec) { + spec + .recipe(new NoGuavaPredicate()) + .parser(JavaParser.fromJavaVersion().classpathFromResources(new InMemoryExecutionContext(), "guava")); + } + + @DocumentExample + @Test + void changeGuavaPredicateToJavaUtilPredicate() { + rewriteRun( + //language=java + java( + """ + import com.google.common.base.Predicate; + + class Test { + Predicate predicate = input -> input != null && !input.isEmpty(); + } + """, + """ + import java.util.function.Predicate; + + class Test { + Predicate predicate = input -> input != null && !input.isEmpty(); + } + """ + ) + ); + } + + @Test + void changeWhenMethodOnlyReturnsGuavaPredicate() { + // Recipe only blocks when Predicate is used as a parameter, not return type + rewriteRun( + //language=java + java( + """ + import com.google.common.base.Predicate; + + class Test { + Predicate predicate = input -> input != null; + + public Predicate createPredicate() { + return s -> s.isEmpty(); + } + } + """, + """ + import java.util.function.Predicate; + + class Test { + Predicate predicate = input -> input != null; + + public Predicate createPredicate() { + return s -> s.isEmpty(); + } + } + """ + ) + ); + } + + // Unfortunately also not changed now that we exclude all methods that take a Guava Predicate as precondition + @Test + void doNotChangePredicatesNot() { + rewriteRun( + //language=java + java( + """ + import com.google.common.base.Predicate; + import com.google.common.base.Predicates; + + class A { + public static Predicate notEmptyPredicate() { + Predicate isEmpty = String::isEmpty; + return Predicates.not(isEmpty); + } + } + """ + ) + ); + } + + @Issue("https://github.com/openrewrite/rewrite-migrate-java/issues/897") + @Test + void doNotChangeWhenUsingSetsFilter() { + // Sets.filter requires Guava Predicate as last parameter + rewriteRun( + //language=java + java( + """ + import com.google.common.base.Predicate; + import com.google.common.collect.Sets; + import java.util.Set; + + class Test { + Predicate notEmpty = s -> !s.isEmpty(); + + public Set filterSet(Set input) { + return Sets.filter(input, notEmpty); + } + } + """ + ) + ); + } + + @Issue("https://github.com/openrewrite/rewrite-migrate-java/issues/883") + @Test + void doNotChangeWhenUsingIterablesFilter() { + // Iterables.filter requires Guava Predicate as last parameter + rewriteRun( + //language=java + java( + """ + import com.google.common.base.Predicate; + import com.google.common.collect.Iterables; + import java.util.List; + + class Test { + Predicate isPositive = n -> n > 0; + + public Iterable filterPositive(List numbers) { + return Iterables.filter(numbers, isPositive); + } + } + """ + ) + ); + } + + @Issue("https://github.com/openrewrite/rewrite-migrate-java/issues/899") + @Test + void doNotChangeWhenUsingCollectionsFilter() { + // Collections2.filter requires Guava Predicate as last parameter + rewriteRun( + //language=java + java( + """ + import com.google.common.base.Predicate; + import com.google.common.collect.Collections2; + import java.util.Collection; + + class Test { + Predicate notEmpty = s -> !s.isEmpty(); + + public Collection filterCollection(Collection input) { + return Collections2.filter(input, notEmpty); + } + } + """ + ) + ); + } +} diff --git a/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilPredicateTest.java b/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilPredicateTest.java index 0c2ff6afaa..1383ac8591 100644 --- a/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilPredicateTest.java +++ b/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilPredicateTest.java @@ -69,36 +69,6 @@ public boolean test(String input) { ); } - @Test - void predicatesNotToPredicate() { - rewriteRun( - //language=java - java( - """ - import com.google.common.base.Predicate; - import com.google.common.base.Predicates; - - class A { - public static Predicate notEmptyPredicate() { - Predicate isEmpty = String::isEmpty; - return Predicates.not(isEmpty); - } - } - """, - """ - import java.util.function.Predicate; - - class A { - public static Predicate notEmptyPredicate() { - Predicate isEmpty = String::isEmpty; - return Predicate.not(isEmpty); - } - } - """ - ) - ); - } - @Test void predicatesEqualToToPredicateIsEqual() { rewriteRun( From dba3f1be584acd5d28ed56a61112eb11c5ced95d Mon Sep 17 00:00:00 2001 From: Laurent Redor Date: Sun, 26 Oct 2025 11:40:42 +0100 Subject: [PATCH 2/5] Add more tests (#904) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the Sirius codebase, I had identified other errors of the same kind for which I hadn’t yet created an issue. I’ve added the corresponding cases to the NoGuavaPredicateTest tests. --- .../migrate/guava/NoGuavaPredicateTest.java | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/src/test/java/org/openrewrite/java/migrate/guava/NoGuavaPredicateTest.java b/src/test/java/org/openrewrite/java/migrate/guava/NoGuavaPredicateTest.java index 17b878562a..b621cd1ccd 100644 --- a/src/test/java/org/openrewrite/java/migrate/guava/NoGuavaPredicateTest.java +++ b/src/test/java/org/openrewrite/java/migrate/guava/NoGuavaPredicateTest.java @@ -181,4 +181,112 @@ public Collection filterCollection(Collection input) { ) ); } + + @Test + void doNotChangeWhenUsingIteratorsFilter() { + // Iterators.filter requires Guava Predicate as last parameter + rewriteRun( + //language=java + java( + """ + import com.google.common.base.Predicate; + import com.google.common.collect.Iterators; + + import java.util.Iterator; + + class Test { + Predicate isPositive = n -> n > 0; + + public Iterator filterPositive(Iterator numbers) { + return Iterators.filter(numbers, isPositive); + } + } + """ + ) + ); + } + + @Test + void doNotChangeWhenUsingIterablesAny() { + // Iterables.any requires Guava Predicate as last parameter + rewriteRun( + //language=java + java( + """ + import com.google.common.base.Predicate; + import com.google.common.collect.Iterables; + import java.util.List; + + class Test { + public boolean any(List input, Predicate aPredicate) { + return Iterables.any(input, aPredicate); + } + } + """ + ) + ); + } + + @Test + void doNotChangeWhenUsingMapsFilterEntries() { + // Maps.filterEntries requires Guava Predicate as last parameter + rewriteRun( + //language=java + java( + """ + import com.google.common.base.Predicate; + import com.google.common.collect.Maps; + import java.util.Map; + + class Test { + public Map filterMap(Map input, Predicate> aPredicate) { + return Maps.filterEntries(input, aPredicate); + } + } + """ + ) + ); + } + + @Test + void doNotChangeWhenUsingMapsFilterValues() { + // Maps.filterValues requires Guava Predicate as last parameter + rewriteRun( + //language=java + java( + """ + import com.google.common.base.Predicate; + import com.google.common.collect.Maps; + import java.util.Map; + + class Test { + public Map filterMap(Map input, Predicate aPredicate) { + return Maps.filterValues(input, aPredicate); + } + } + """ + ) + ); + } + + @Test + void doNotChangeWhenUsingMapsFilterKeys() { + // Maps.filterKeys requires Guava Predicate as last parameter + rewriteRun( + //language=java + java( + """ + import com.google.common.base.Predicate; + import com.google.common.collect.Maps; + import java.util.Map; + + class Test { + public Map filterMap(Map input, Predicate aPredicate) { + return Maps.filterKeys(input, aPredicate); + } + } + """ + ) + ); + } } From 387a08c401f6c84a94ee86068a1945ae18a18a4d Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Sun, 26 Oct 2025 12:21:41 +0100 Subject: [PATCH 3/5] Do convert `Predicates.not` to `java.util.function.Predicate.not` --- .../java/migrate/guava/NoGuavaPredicate.java | 30 +++++++++++++++++-- .../migrate/guava/NoGuavaPredicateTest.java | 18 +++++++++-- 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/openrewrite/java/migrate/guava/NoGuavaPredicate.java b/src/main/java/org/openrewrite/java/migrate/guava/NoGuavaPredicate.java index 76875f2af8..b9db662b39 100644 --- a/src/main/java/org/openrewrite/java/migrate/guava/NoGuavaPredicate.java +++ b/src/main/java/org/openrewrite/java/migrate/guava/NoGuavaPredicate.java @@ -20,7 +20,12 @@ import org.openrewrite.Recipe; import org.openrewrite.TreeVisitor; import org.openrewrite.java.ChangeType; -import org.openrewrite.java.search.UsesMethod; +import org.openrewrite.java.JavaIsoVisitor; +import org.openrewrite.java.MethodMatcher; +import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.JavaSourceFile; +import org.openrewrite.java.tree.JavaType; +import org.openrewrite.marker.SearchResult; public class NoGuavaPredicate extends Recipe { @Override @@ -36,7 +41,7 @@ public String getDescription() { @Override public TreeVisitor getVisitor() { return Preconditions.check( - Preconditions.not(new UsesMethod<>("*..* *(.., com.google.common.base.Predicate)")), + Preconditions.not(new UsesPredicateMethod<>()), new ChangeType( "com.google.common.base.Predicate", "java.util.function.Predicate", @@ -44,4 +49,25 @@ public TreeVisitor getVisitor() { .getVisitor() ); } + + private static class UsesPredicateMethod

extends JavaIsoVisitor

{ + private static final MethodMatcher PREDICATE_METHOD_MATCHER = new MethodMatcher("*..* *(.., com.google.common.base.Predicate)"); + private static final MethodMatcher NOT_MATCHER = new MethodMatcher("*..* not(com.google.common.base.Predicate)"); + + @Override + public J preVisit(J tree, P p) { + stopAfterPreVisit(); + if (tree instanceof JavaSourceFile) { + JavaSourceFile cu = (JavaSourceFile) tree; + for (JavaType.Method type : cu.getTypesInUse().getUsedMethods()) { + if (PREDICATE_METHOD_MATCHER.matches(type) && + // Make an exception for `not` methods; those can be safely converted + !NOT_MATCHER.matches(type)) { + return SearchResult.found(cu); + } + } + } + return tree; + } + } } diff --git a/src/test/java/org/openrewrite/java/migrate/guava/NoGuavaPredicateTest.java b/src/test/java/org/openrewrite/java/migrate/guava/NoGuavaPredicateTest.java index b621cd1ccd..c359d3e586 100644 --- a/src/test/java/org/openrewrite/java/migrate/guava/NoGuavaPredicateTest.java +++ b/src/test/java/org/openrewrite/java/migrate/guava/NoGuavaPredicateTest.java @@ -89,10 +89,12 @@ public Predicate createPredicate() { ); } - // Unfortunately also not changed now that we exclude all methods that take a Guava Predicate as precondition @Test - void doNotChangePredicatesNot() { + void changePredicatesNot() { rewriteRun( + spec -> spec.recipeFromResource( + "/META-INF/rewrite/no-guava.yml", + "org.openrewrite.java.migrate.guava.PreferJavaUtilPredicate"), //language=java java( """ @@ -100,11 +102,21 @@ void doNotChangePredicatesNot() { import com.google.common.base.Predicates; class A { - public static Predicate notEmptyPredicate() { + Predicate notEmptyPredicate() { Predicate isEmpty = String::isEmpty; return Predicates.not(isEmpty); } } + """, + """ + import java.util.function.Predicate; + + class A { + Predicate notEmptyPredicate() { + Predicate isEmpty = String::isEmpty; + return Predicate.not(isEmpty); + } + } """ ) ); From e4262b687ad24906510c9d8cc8f3b6cbf700f536 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Sun, 26 Oct 2025 12:24:19 +0100 Subject: [PATCH 4/5] Restore original test --- .../guava/PreferJavaUtilPredicateTest.java | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilPredicateTest.java b/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilPredicateTest.java index 1383ac8591..34017f90cb 100644 --- a/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilPredicateTest.java +++ b/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilPredicateTest.java @@ -27,7 +27,9 @@ class PreferJavaUtilPredicateTest implements RewriteTest { @Override public void defaults(RecipeSpec spec) { - spec.recipeFromResources("org.openrewrite.java.migrate.guava.PreferJavaUtilPredicate") + spec.recipeFromResource( + "/META-INF/rewrite/no-guava.yml", + "org.openrewrite.java.migrate.guava.PreferJavaUtilPredicate") .parser(JavaParser.fromJavaVersion().classpathFromResources(new InMemoryExecutionContext(), "guava")); } @@ -69,6 +71,34 @@ public boolean test(String input) { ); } + @Test + void predicatesNotToPredicate() { + rewriteRun( + //language=java + java( + """ + import com.google.common.base.Predicate; + import com.google.common.base.Predicates; + class A { + public static Predicate notEmptyPredicate() { + Predicate isEmpty = String::isEmpty; + return Predicates.not(isEmpty); + } + } + """, + """ + import java.util.function.Predicate; + class A { + public static Predicate notEmptyPredicate() { + Predicate isEmpty = String::isEmpty; + return Predicate.not(isEmpty); + } + } + """ + ) + ); + } + @Test void predicatesEqualToToPredicateIsEqual() { rewriteRun( From 528bab81da245aab61b07784db15e2794926a0c4 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Sun, 26 Oct 2025 12:25:22 +0100 Subject: [PATCH 5/5] Fix formatting --- .../java/migrate/guava/PreferJavaUtilPredicateTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilPredicateTest.java b/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilPredicateTest.java index 34017f90cb..083d9838f0 100644 --- a/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilPredicateTest.java +++ b/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilPredicateTest.java @@ -79,6 +79,7 @@ void predicatesNotToPredicate() { """ import com.google.common.base.Predicate; import com.google.common.base.Predicates; + class A { public static Predicate notEmptyPredicate() { Predicate isEmpty = String::isEmpty; @@ -88,6 +89,7 @@ public static Predicate notEmptyPredicate() { """, """ import java.util.function.Predicate; + class A { public static Predicate notEmptyPredicate() { Predicate isEmpty = String::isEmpty;