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..b9db662b39
--- /dev/null
+++ b/src/main/java/org/openrewrite/java/migrate/guava/NoGuavaPredicate.java
@@ -0,0 +1,73 @@
+/*
+ * 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.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
+ 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, ExecutionContext> getVisitor() {
+ return Preconditions.check(
+ Preconditions.not(new UsesPredicateMethod<>()),
+ new ChangeType(
+ "com.google.common.base.Predicate",
+ "java.util.function.Predicate",
+ false)
+ .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/main/resources/META-INF/rewrite/no-guava.yml b/src/main/resources/META-INF/rewrite/no-guava.yml
index 036114b02c..8684f621a2 100644
--- a/src/main/resources/META-INF/rewrite/no-guava.yml
+++ b/src/main/resources/META-INF/rewrite/no-guava.yml
@@ -195,9 +195,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..c359d3e586
--- /dev/null
+++ b/src/test/java/org/openrewrite/java/migrate/guava/NoGuavaPredicateTest.java
@@ -0,0 +1,304 @@
+/*
+ * 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();
+ }
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void changePredicatesNot() {
+ rewriteRun(
+ spec -> spec.recipeFromResource(
+ "/META-INF/rewrite/no-guava.yml",
+ "org.openrewrite.java.migrate.guava.PreferJavaUtilPredicate"),
+ //language=java
+ java(
+ """
+ import com.google.common.base.Predicate;
+ import com.google.common.base.Predicates;
+
+ class A {
+ 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);
+ }
+ }
+ """
+ )
+ );
+ }
+
+ @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);
+ }
+ }
+ """
+ )
+ );
+ }
+
+ @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);
+ }
+ }
+ """
+ )
+ );
+ }
+}
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..083d9838f0 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"));
}