From a1cc80b5067109cb3efc109a921f89ae02e4543b Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Sat, 25 Mar 2023 21:56:26 +0100 Subject: [PATCH 01/15] Prefer java.util.Optional over Guava Optional --- .sdkmanrc | 2 +- build.gradle.kts | 1 + .../resources/META-INF/rewrite/no-guava.yml | 25 ++++++ .../guava/PreferJavaUtilOptionalTest.java | 85 +++++++++++++++++++ 4 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalTest.java diff --git a/.sdkmanrc b/.sdkmanrc index f5270ad9..71123388 100644 --- a/.sdkmanrc +++ b/.sdkmanrc @@ -1,3 +1,3 @@ # Enable auto-env through the sdkman_auto_env config # Add key=value pairs of SDKs to use below -java=11.0.16-tem +java=11.0.18-tem diff --git a/build.gradle.kts b/build.gradle.kts index 7c3f2674..ff907a99 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -26,6 +26,7 @@ dependencies { 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") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:latest.release") testImplementation("org.openrewrite:rewrite-test") diff --git a/src/main/resources/META-INF/rewrite/no-guava.yml b/src/main/resources/META-INF/rewrite/no-guava.yml index f9a4ed54..c405459a 100644 --- a/src/main/resources/META-INF/rewrite/no-guava.yml +++ b/src/main/resources/META-INF/rewrite/no-guava.yml @@ -33,6 +33,7 @@ recipeList: - org.openrewrite.java.migrate.guava.NoGuavaSetsNewConcurrentHashSet - org.openrewrite.java.migrate.guava.NoGuavaSetsNewLinkedHashSet - org.openrewrite.java.migrate.guava.PreferJavaUtilFunction + - org.openrewrite.java.migrate.guava.PreferJavaUtilOptional - org.openrewrite.java.migrate.guava.PreferJavaUtilPredicate - org.openrewrite.java.migrate.guava.PreferJavaUtilSupplier - org.openrewrite.java.migrate.guava.PreferJavaUtilObjectsEquals @@ -70,6 +71,30 @@ recipeList: oldFullyQualifiedTypeName: com.google.common.base.Function newFullyQualifiedTypeName: java.util.function.Function +--- +type: specs.openrewrite.org/v1beta/recipe +name: org.openrewrite.java.migrate.guava.PreferJavaUtilOptional +displayName: Prefer `java.util.Optional` +description: Prefer `java.util.Optional` instead of using `com.google.common.base.Optional`. +tags: + - guava, RSPEC-4738 +recipeList: + - org.openrewrite.java.ChangeMethodName: + methodPattern: com.google.common.base.Optional absent() + newMethodName: empty + - org.openrewrite.java.ChangeMethodName: + methodPattern: com.google.common.base.Optional fromNullable(..) + newMethodName: ofNullable + - org.openrewrite.java.ChangeMethodName: + methodPattern: com.google.common.base.Optional or(..) + newMethodName: orElse + - org.openrewrite.java.ChangeMethodName: + methodPattern: com.google.common.base.Optional or(..) + newMethodName: orElse + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: com.google.common.base.Optional + newFullyQualifiedTypeName: java.util.Optional + --- type: specs.openrewrite.org/v1beta/recipe name: org.openrewrite.java.migrate.guava.PreferJavaUtilPredicate diff --git a/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalTest.java b/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalTest.java new file mode 100644 index 00000000..a4c0ee4d --- /dev/null +++ b/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalTest.java @@ -0,0 +1,85 @@ +/* + * Copyright 2021 the original author or authors. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * https://www.apache.org/licenses/LICENSE-2.0 + *

+ * 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.junitpioneer.jupiter.ExpectedToFail; +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; + +@Issue("https://github.com/openrewrite/rewrite-migrate-java/issues/197") +class PreferJavaUtilOptionalTest 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.PreferJavaUtilOptional") + ) + .parser(JavaParser.fromJavaVersion().classpath("guava")); + } + + @Test + void optionalAbsent() { + //language=java + rewriteRun(java(""" + import com.google.common.base.Optional; + + class A { + Optional absentToEmpty() { + return Optional.absent(); + } + } + """, """ + import java.util.Optional; + + class A { + Optional absentToEmpty() { + return Optional.empty(); + } + } + """)); + } + + @Test + @ExpectedToFail("Not yet implemented") + void removeToJavaUtil() { + //language=java + rewriteRun(java(""" + import com.google.common.base.Optional; + + class A { + boolean absentToEmpty() { + return Optional.absent().toJavaUtil().isEmpty(); + } + } + """, """ + import java.util.Optional; + + class A { + boolean absentToEmpty() { + return Optional.empty().isEmpty(); + } + } + """)); + } +} From 691f4188bcfab30484be125b554a7a00a0986262 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Sat, 25 Mar 2023 23:29:19 +0100 Subject: [PATCH 02/15] Implement and document a few more cases --- .../resources/META-INF/rewrite/no-guava.yml | 7 +- .../guava/PreferJavaUtilOptionalTest.java | 245 +++++++++++++++++- 2 files changed, 240 insertions(+), 12 deletions(-) diff --git a/src/main/resources/META-INF/rewrite/no-guava.yml b/src/main/resources/META-INF/rewrite/no-guava.yml index c405459a..d5e45165 100644 --- a/src/main/resources/META-INF/rewrite/no-guava.yml +++ b/src/main/resources/META-INF/rewrite/no-guava.yml @@ -86,11 +86,14 @@ recipeList: methodPattern: com.google.common.base.Optional fromNullable(..) newMethodName: ofNullable - org.openrewrite.java.ChangeMethodName: - methodPattern: com.google.common.base.Optional or(..) - newMethodName: orElse + methodPattern: com.google.common.base.Optional or(com.google.common.base.Supplier) + newMethodName: orElseGet - org.openrewrite.java.ChangeMethodName: methodPattern: com.google.common.base.Optional or(..) newMethodName: orElse + - org.openrewrite.java.ChangeMethodName: + methodPattern: com.google.common.base.Optional transform(com.google.common.base.Function) + newMethodName: map - org.openrewrite.java.ChangeType: oldFullyQualifiedTypeName: com.google.common.base.Optional newFullyQualifiedTypeName: java.util.Optional diff --git a/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalTest.java b/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalTest.java index a4c0ee4d..a527aaff 100644 --- a/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalTest.java +++ b/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalTest.java @@ -15,6 +15,7 @@ */ package org.openrewrite.java.migrate.guava; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junitpioneer.jupiter.ExpectedToFail; import org.openrewrite.Issue; @@ -39,13 +40,13 @@ public void defaults(RecipeSpec spec) { } @Test - void optionalAbsent() { + void absentToEmpty() { //language=java rewriteRun(java(""" import com.google.common.base.Optional; class A { - Optional absentToEmpty() { + Optional foo() { return Optional.absent(); } } @@ -53,7 +54,7 @@ Optional absentToEmpty() { import java.util.Optional; class A { - Optional absentToEmpty() { + Optional foo() { return Optional.empty(); } } @@ -61,25 +62,249 @@ Optional absentToEmpty() { } @Test - @ExpectedToFail("Not yet implemented") - void removeToJavaUtil() { + void orToOrElse() { //language=java rewriteRun(java(""" import com.google.common.base.Optional; class A { - boolean absentToEmpty() { - return Optional.absent().toJavaUtil().isEmpty(); + Optional foo(Optional optional) { + return optional.or("other"); } } """, """ import java.util.Optional; class A { - boolean absentToEmpty() { - return Optional.empty().isEmpty(); + Optional foo(Optional optional) { + return optional.orElse("other"); } } """)); } -} + + @Test + void orSupplierToOrElseGet() { + //language=java + rewriteRun(java(""" + import com.google.common.base.Optional; + + class A { + Optional foo(Optional optional) { + return optional.or(() -> "other"); + } + } + """, """ + import java.util.Optional; + + class A { + Optional foo(Optional optional) { + return optional.orElseGet(() -> "other"); + } + } + """)); + } + + @Test + void transformToMap() { + //language=java + rewriteRun(java(""" + import com.google.common.base.Optional; + + class A { + Optional foo(Optional optional) { + return optional.transform(String::toUpperCase); + } + } + """, """ + import java.util.Optional; + + class A { + Optional foo(Optional optional) { + return optional.map(String::toUpperCase); + } + } + """)); + } + + @Nested + class NotYetImplemented { + @Test + @ExpectedToFail("Not yet implemented") + void orOptionalToTernary() { + // Comparison to java.util.Optional: this method has no equivalent in Java 8's Optional class; write thisOptional.isPresent() ? thisOptional : secondChoice instead. + //language=java + rewriteRun(java(""" + import com.google.common.base.Optional; + + class A { + Optional foo(Optional firstChoice, Optional secondChoice) { + return firstChoice.or(secondChoice); + } + } + """, """ + import java.util.Optional; + + class A { + Optional foo(Optional firstChoice, Optional secondChoice) { + return a.isPresent()? firstChoice : secondChoice; + } + } + """)); + } + + @Test + @ExpectedToFail("Not yet implemented") + void removeToJavaUtil() { + //language=java + rewriteRun(java(""" + import com.google.common.base.Optional; + + class A { + boolean foo() { + return Optional.absent().toJavaUtil().isEmpty(); + } + } + """, """ + import java.util.Optional; + + class A { + boolean foo() { + return Optional.empty().isEmpty(); + } + } + """)); + } + + @Test + @ExpectedToFail("Not yet implemented") + void removeFromJavaUtil() { + //language=java + rewriteRun(java(""" + import com.google.common.base.Optional; + + class A { + Optional foo(java.util.Optional optional) { + return Optional.fromJavaUtil(optional); + } + } + """, """ + import java.util.Optional; + + class A { + Optional foo(java.util.Optional optional) { + return optional; + } + } + """)); + } + + @Test + @ExpectedToFail("Not yet implemented") + void getCatchIllegalStateExceptionToNoSuchElementException() { + // > when the value is absent, this method throws IllegalStateException, whereas the Java 8 counterpart throws NoSuchElementException. + // Sure hope no one actually does this, but you never know. + //language=java + rewriteRun(java(""" + import com.google.common.base.Optional; + + class A { + String foo(Optional optional) { + try { + return optional.get(); + } catch (IllegalStateException e) { + return ""; + } + } + } + """, """ + import java.util.Optional; + + class A { + String foo(Optional optional) { + try { + return optional.get(); + } catch (NoSuchElementException e) { + return ""; + } + } + } + """)); + } + + @Test + @ExpectedToFail("Not yet implemented") + void orNullToOrElseNull() { + // Comparison to java.util.Optional: this method is equivalent to Java 8's Optional.orElse(null). + //language=java + rewriteRun(java(""" + import com.google.common.base.Optional; + + class A { + String foo(Optional optional) { + return optional.orNull(); + } + } + """, """ + import java.util.Optional; + + class A { + String foo(Optional optional) { + return optional.orElse(null); + } + } + """)); + } + + @Test + @ExpectedToFail("Not yet implemented") + void asSetToStreamCollectToSet() { + // Comparison to java.util.Optional: this method has no equivalent in Java 8's Optional class. However, some use cases can be written with calls to optional.stream(). + //language=java + rewriteRun(java(""" + import com.google.common.base.Optional; + + class A { + Set foo(Optional optional) { + return optional.asSet(); + } + } + """, """ + import java.util.Optional; + import java.util.Set; + import java.util.stream.Collectors; + + class A { + Set foo(Optional optional) { + return optional.stream().collect(Collectors.toSet()); + } + } + """)); + } + + @Test + @ExpectedToFail("Not yet implemented") + void presentInstances() { + // Comparison to java.util.Optional: this method has no equivalent in Java 8's Optional class; use optionals.stream().filter(Optional::isPresent).map(Optional::get) instead. + //language=java + rewriteRun(java(""" + import com.google.common.base.Optional; + + class A { + Iterable foo(Iterable> optionals) { + return Optional.presentInstances(optionals); + } + } + """, """ + import java.util.Optional; + import java.util.stream.Collectors; + + class A { + Iterable foo(Iterable> optionals) { + return optionals.stream().flatMap(Optional::stream).collect(Collectors.toList()); + } + } + """)); + } + } +} \ No newline at end of file From ccdd601396e4d9ff75af2b7ff177eb70ffbef12f Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Sat, 25 Mar 2023 23:31:15 +0100 Subject: [PATCH 03/15] Implement and document a few more cases --- .../guava/PreferJavaUtilOptionalTest.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalTest.java b/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalTest.java index a527aaff..cfcc50cb 100644 --- a/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalTest.java +++ b/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalTest.java @@ -46,7 +46,7 @@ void absentToEmpty() { import com.google.common.base.Optional; class A { - Optional foo() { + Optional foo() { return Optional.absent(); } } @@ -54,7 +54,7 @@ Optional foo() { import java.util.Optional; class A { - Optional foo() { + Optional foo() { return Optional.empty(); } } @@ -68,7 +68,7 @@ void orToOrElse() { import com.google.common.base.Optional; class A { - Optional foo(Optional optional) { + Optional foo(Optional optional) { return optional.or("other"); } } @@ -76,7 +76,7 @@ Optional foo(Optional optional) { import java.util.Optional; class A { - Optional foo(Optional optional) { + Optional foo(Optional optional) { return optional.orElse("other"); } } @@ -90,7 +90,7 @@ void orSupplierToOrElseGet() { import com.google.common.base.Optional; class A { - Optional foo(Optional optional) { + Optional foo(Optional optional) { return optional.or(() -> "other"); } } @@ -98,7 +98,7 @@ Optional foo(Optional optional) { import java.util.Optional; class A { - Optional foo(Optional optional) { + Optional foo(Optional optional) { return optional.orElseGet(() -> "other"); } } @@ -112,7 +112,7 @@ void transformToMap() { import com.google.common.base.Optional; class A { - Optional foo(Optional optional) { + Optional foo(Optional optional) { return optional.transform(String::toUpperCase); } } @@ -120,7 +120,7 @@ Optional foo(Optional optional) { import java.util.Optional; class A { - Optional foo(Optional optional) { + Optional foo(Optional optional) { return optional.map(String::toUpperCase); } } @@ -138,7 +138,7 @@ void orOptionalToTernary() { import com.google.common.base.Optional; class A { - Optional foo(Optional firstChoice, Optional secondChoice) { + Optional foo(Optional firstChoice, Optional secondChoice) { return firstChoice.or(secondChoice); } } @@ -146,7 +146,7 @@ Optional foo(Optional firstChoice, Optional secondChoice) { import java.util.Optional; class A { - Optional foo(Optional firstChoice, Optional secondChoice) { + Optional foo(Optional firstChoice, Optional secondChoice) { return a.isPresent()? firstChoice : secondChoice; } } From 5ecbb2487a2f1fc6dcb0abdd6af7b380ce072de8 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Sat, 25 Mar 2023 23:45:49 +0100 Subject: [PATCH 04/15] Implement PreferJavaUtilOptionalOrElseNull --- .../PreferJavaUtilOptionalOrElseNull.java | 89 +++++++++++++++++++ .../resources/META-INF/rewrite/no-guava.yml | 1 + .../guava/PreferJavaUtilOptionalTest.java | 47 +++++----- 3 files changed, 113 insertions(+), 24 deletions(-) create mode 100644 src/main/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalOrElseNull.java diff --git a/src/main/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalOrElseNull.java b/src/main/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalOrElseNull.java new file mode 100644 index 00000000..b31ac897 --- /dev/null +++ b/src/main/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalOrElseNull.java @@ -0,0 +1,89 @@ +/* + * Copyright 2023 the original author or authors. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * https://www.apache.org/licenses/LICENSE-2.0 + *

+ * 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.Recipe; +import org.openrewrite.java.JavaIsoVisitor; +import org.openrewrite.java.JavaTemplate; +import org.openrewrite.java.MethodMatcher; +import org.openrewrite.java.search.UsesMethod; +import org.openrewrite.java.search.UsesType; +import org.openrewrite.java.tree.J; + +import java.time.Duration; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +public class PreferJavaUtilOptionalOrElseNull extends Recipe { + @Override + public String getDisplayName() { + return "Prefer `java.util.Optional#orElse(null)` over `com.google.common.base.Optional#orNull()`"; + } + + @Override + public String getDescription() { + return "Replaces `com.google.common.base.Optional#orNull()` with `java.util.Optional#orElse(null)`."; + } + + @Override + public Duration getEstimatedEffortPerOccurrence() { + return Duration.ofMinutes(5); + } + + @Override + public Set getTags() { + return new HashSet<>(Arrays.asList("RSPEC-4738", "guava")); + } + + @Override + protected UsesType getApplicableTest() { + return new UsesType<>("com.google.common.base.Optional"); + } + + @Override + protected UsesMethod getSingleSourceApplicableTest() { + return new UsesMethod<>("com.google.common.base.Optional orNull()"); + } + + @Override + protected JavaIsoVisitor getVisitor() { + return new PreferJavaUtilOptionalOrElseNullVisitor(); + } + + private static class PreferJavaUtilOptionalOrElseNullVisitor extends JavaIsoVisitor { + private static final MethodMatcher guavaCreateTempDirMatcher = new MethodMatcher("com.google.common.base.Optional orNull()"); + + @Override + public J.CompilationUnit visitCompilationUnit(J.CompilationUnit cu, ExecutionContext executionContext) { + J.CompilationUnit c = super.visitCompilationUnit(cu, executionContext); + maybeAddImport("java.util.Optional"); + maybeRemoveImport("com.google.common.base.Optional"); + return c; + } + + @Override + public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext executionContext) { + J.MethodInvocation mi = super.visitMethodInvocation(method, executionContext); + if (guavaCreateTempDirMatcher.matches(mi)) { + return mi.withName(mi.getName().withSimpleName("orElse")) + .withTemplate(JavaTemplate.builder(this::getCursor, "null").build(), mi.getCoordinates().replaceArguments()); + } + return mi; + } + } +} diff --git a/src/main/resources/META-INF/rewrite/no-guava.yml b/src/main/resources/META-INF/rewrite/no-guava.yml index d5e45165..2f72dbdf 100644 --- a/src/main/resources/META-INF/rewrite/no-guava.yml +++ b/src/main/resources/META-INF/rewrite/no-guava.yml @@ -79,6 +79,7 @@ description: Prefer `java.util.Optional` instead of using `com.google.common.bas tags: - guava, RSPEC-4738 recipeList: + - org.openrewrite.java.migrate.guava.PreferJavaUtilOptionalOrElseNull - org.openrewrite.java.ChangeMethodName: methodPattern: com.google.common.base.Optional absent() newMethodName: empty diff --git a/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalTest.java b/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalTest.java index cfcc50cb..92d37391 100644 --- a/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalTest.java +++ b/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalTest.java @@ -61,6 +61,29 @@ Optional foo() { """)); } + @Test + void orNullToOrElseNull() { + // Comparison to java.util.Optional: this method is equivalent to Java 8's Optional.orElse(null). + //language=java + rewriteRun(java(""" + import com.google.common.base.Optional; + + class A { + String foo(Optional optional) { + return optional.orNull(); + } + } + """, """ + import java.util.Optional; + + class A { + String foo(Optional optional) { + return optional.orElse(null); + } + } + """)); + } + @Test void orToOrElse() { //language=java @@ -232,30 +255,6 @@ String foo(Optional optional) { """)); } - @Test - @ExpectedToFail("Not yet implemented") - void orNullToOrElseNull() { - // Comparison to java.util.Optional: this method is equivalent to Java 8's Optional.orElse(null). - //language=java - rewriteRun(java(""" - import com.google.common.base.Optional; - - class A { - String foo(Optional optional) { - return optional.orNull(); - } - } - """, """ - import java.util.Optional; - - class A { - String foo(Optional optional) { - return optional.orElse(null); - } - } - """)); - } - @Test @ExpectedToFail("Not yet implemented") void asSetToStreamCollectToSet() { From 31323c727529c3398e4301827e4656a8b22f5004 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Sun, 26 Mar 2023 12:27:11 +0200 Subject: [PATCH 05/15] Adopt `firstChoice.or(() -> secondChoice)` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Aurélien Mino <155828+murdos@users.noreply.github.com> --- .../java/migrate/guava/PreferJavaUtilOptionalTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalTest.java b/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalTest.java index 92d37391..519df357 100644 --- a/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalTest.java +++ b/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalTest.java @@ -170,7 +170,7 @@ Optional foo(Optional firstChoice, Optional secondChoice class A { Optional foo(Optional firstChoice, Optional secondChoice) { - return a.isPresent()? firstChoice : secondChoice; + return firstChoice.or(() -> secondChoice); } } """)); From e9b8f5ec3ace6f6b95c30c4e6f71fec3f373834c Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Mon, 27 Mar 2023 11:18:18 +0200 Subject: [PATCH 06/15] Address review comments --- .../guava/PreferJavaUtilOptionalOrElseNull.java | 4 ++-- src/main/resources/META-INF/rewrite/no-guava.yml | 12 ++++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalOrElseNull.java b/src/main/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalOrElseNull.java index b31ac897..19117e7b 100644 --- a/src/main/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalOrElseNull.java +++ b/src/main/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalOrElseNull.java @@ -66,7 +66,7 @@ protected JavaIsoVisitor getVisitor() { } private static class PreferJavaUtilOptionalOrElseNullVisitor extends JavaIsoVisitor { - private static final MethodMatcher guavaCreateTempDirMatcher = new MethodMatcher("com.google.common.base.Optional orNull()"); + private static final MethodMatcher OPTIONAL_OR_NULL_MATCHER = new MethodMatcher("com.google.common.base.Optional orNull()"); @Override public J.CompilationUnit visitCompilationUnit(J.CompilationUnit cu, ExecutionContext executionContext) { @@ -79,7 +79,7 @@ public J.CompilationUnit visitCompilationUnit(J.CompilationUnit cu, ExecutionCon @Override public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext executionContext) { J.MethodInvocation mi = super.visitMethodInvocation(method, executionContext); - if (guavaCreateTempDirMatcher.matches(mi)) { + if (OPTIONAL_OR_NULL_MATCHER.matches(mi)) { return mi.withName(mi.getName().withSimpleName("orElse")) .withTemplate(JavaTemplate.builder(this::getCursor, "null").build(), mi.getCoordinates().replaceArguments()); } diff --git a/src/main/resources/META-INF/rewrite/no-guava.yml b/src/main/resources/META-INF/rewrite/no-guava.yml index 2f72dbdf..79a3878e 100644 --- a/src/main/resources/META-INF/rewrite/no-guava.yml +++ b/src/main/resources/META-INF/rewrite/no-guava.yml @@ -65,7 +65,8 @@ name: org.openrewrite.java.migrate.guava.PreferJavaUtilFunction displayName: Prefer `java.util.function.Function` description: Prefer `java.util.function.Function` instead of using `com.google.common.base.Function`. tags: - - guava, RSPEC-4738 + - guava + - RSPEC-4738 recipeList: - org.openrewrite.java.ChangeType: oldFullyQualifiedTypeName: com.google.common.base.Function @@ -77,7 +78,8 @@ name: org.openrewrite.java.migrate.guava.PreferJavaUtilOptional displayName: Prefer `java.util.Optional` description: Prefer `java.util.Optional` instead of using `com.google.common.base.Optional`. tags: - - guava, RSPEC-4738 + - guava + - RSPEC-4738 recipeList: - org.openrewrite.java.migrate.guava.PreferJavaUtilOptionalOrElseNull - org.openrewrite.java.ChangeMethodName: @@ -105,7 +107,8 @@ name: org.openrewrite.java.migrate.guava.PreferJavaUtilPredicate displayName: Prefer `java.util.function.Predicate` description: Prefer `java.util.function.Predicate` instead of using `com.google.common.base.Predicate`. tags: - - guava, RSPEC-4738 + - guava + - RSPEC-4738 recipeList: - org.openrewrite.java.ChangeType: oldFullyQualifiedTypeName: com.google.common.base.Predicate @@ -117,7 +120,8 @@ name: org.openrewrite.java.migrate.guava.PreferJavaUtilSupplier displayName: Prefer `java.util.function.Supplier` description: Prefer `java.util.function.Supplier` instead of using `com.google.common.base.Supplier`. tags: - - guava, RSPEC-4738 + - guava + - RSPEC-4738 recipeList: - org.openrewrite.java.ChangeType: oldFullyQualifiedTypeName: com.google.common.base.Supplier From 98739f2b4a1dc73bbae096b3dc1c932e72ca82c9 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Mon, 27 Mar 2023 11:35:27 +0200 Subject: [PATCH 07/15] Add applicability test on a minimum of Java 9 --- src/main/resources/META-INF/rewrite/no-guava.yml | 4 ++++ .../java/migrate/guava/PreferJavaUtilOptionalTest.java | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/resources/META-INF/rewrite/no-guava.yml b/src/main/resources/META-INF/rewrite/no-guava.yml index 79a3878e..d43c9f1e 100644 --- a/src/main/resources/META-INF/rewrite/no-guava.yml +++ b/src/main/resources/META-INF/rewrite/no-guava.yml @@ -80,6 +80,10 @@ description: Prefer `java.util.Optional` instead of using `com.google.common.bas tags: - guava - RSPEC-4738 +applicability: + singleSource: + - org.openrewrite.java.search.UsesJavaVersion: + majorVersionMin: 9 recipeList: - org.openrewrite.java.migrate.guava.PreferJavaUtilOptionalOrElseNull - org.openrewrite.java.ChangeMethodName: diff --git a/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalTest.java b/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalTest.java index 519df357..6df4b2dc 100644 --- a/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalTest.java +++ b/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalTest.java @@ -36,7 +36,7 @@ public void defaults(RecipeSpec spec) { .build() .activateRecipes("org.openrewrite.java.migrate.guava.PreferJavaUtilOptional") ) - .parser(JavaParser.fromJavaVersion().classpath("guava")); + .parser(JavaParser.fromJavaVersion().classpath("rewrite-java", "guava")); } @Test From 0366a2616f71125ee69e1ed0197ace95c7a7c5ca Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Thu, 30 Mar 2023 21:49:42 +0200 Subject: [PATCH 08/15] Drop UsesJavaVersion as it's a visitor, not a recipe --- src/main/resources/META-INF/rewrite/no-guava.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/resources/META-INF/rewrite/no-guava.yml b/src/main/resources/META-INF/rewrite/no-guava.yml index d43c9f1e..79a3878e 100644 --- a/src/main/resources/META-INF/rewrite/no-guava.yml +++ b/src/main/resources/META-INF/rewrite/no-guava.yml @@ -80,10 +80,6 @@ description: Prefer `java.util.Optional` instead of using `com.google.common.bas tags: - guava - RSPEC-4738 -applicability: - singleSource: - - org.openrewrite.java.search.UsesJavaVersion: - majorVersionMin: 9 recipeList: - org.openrewrite.java.migrate.guava.PreferJavaUtilOptionalOrElseNull - org.openrewrite.java.ChangeMethodName: From 3e46b325b62c4c555da73c49c0b345fd0cf33061 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Thu, 30 Mar 2023 22:05:07 +0200 Subject: [PATCH 09/15] Add NoGuavaOptionalFromJavaUtil --- .../guava/NoGuavaOptionalFromJavaUtil.java | 92 +++++++++++++++++++ .../resources/META-INF/rewrite/no-guava.yml | 1 + .../guava/PreferJavaUtilOptionalTest.java | 47 +++++----- 3 files changed, 116 insertions(+), 24 deletions(-) create mode 100644 src/main/java/org/openrewrite/java/migrate/guava/NoGuavaOptionalFromJavaUtil.java diff --git a/src/main/java/org/openrewrite/java/migrate/guava/NoGuavaOptionalFromJavaUtil.java b/src/main/java/org/openrewrite/java/migrate/guava/NoGuavaOptionalFromJavaUtil.java new file mode 100644 index 00000000..bb0cbd0c --- /dev/null +++ b/src/main/java/org/openrewrite/java/migrate/guava/NoGuavaOptionalFromJavaUtil.java @@ -0,0 +1,92 @@ +/* + * Copyright 2023 the original author or authors. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * https://www.apache.org/licenses/LICENSE-2.0 + *

+ * 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.Recipe; +import org.openrewrite.java.JavaVisitor; +import org.openrewrite.java.MethodMatcher; +import org.openrewrite.java.search.UsesMethod; +import org.openrewrite.java.search.UsesType; +import org.openrewrite.java.tree.J; + +import java.time.Duration; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +public class NoGuavaOptionalFromJavaUtil extends Recipe { + + static final MethodMatcher METHOD_MATCHER = new MethodMatcher("com.google.common.base.Optional fromJavaUtil(java.util.Optional)"); + + @Override + public String getDisplayName() { + return "Replace `com.google.common.base.Optional#fromJavaUtil(java.util.Optional)` with argument"; + } + + @Override + public String getDescription() { + return "Replaces `com.google.common.base.Optional#fromJavaUtil(java.util.Optional)` with argument."; + } + + @Override + public Duration getEstimatedEffortPerOccurrence() { + return Duration.ofMinutes(5); + } + + @Override + public Set getTags() { + return new HashSet<>(Arrays.asList("RSPEC-4738", "guava")); + } + + @Override + protected UsesType getApplicableTest() { + return new UsesType<>("com.google.common.base.Optional"); + } + + @Override + protected UsesMethod getSingleSourceApplicableTest() { + return new UsesMethod<>(METHOD_MATCHER); + } + + @Override + protected JavaVisitor getVisitor() { + return new PreferJavaUtilOptionalOrElseNullVisitor(); + } + + private static class PreferJavaUtilOptionalOrElseNullVisitor extends JavaVisitor { + + @Override + public J visitCompilationUnit(J.CompilationUnit cu, ExecutionContext executionContext) { + J c = super.visitCompilationUnit(cu, executionContext); + maybeAddImport("java.util.Optional"); + maybeRemoveImport("com.google.common.base.Optional"); + return c; + } + + @Override + public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext executionContext) { + J j = super.visitMethodInvocation(method, executionContext); + if (j instanceof J.MethodInvocation) { + J.MethodInvocation mi = (J.MethodInvocation) j; + if (METHOD_MATCHER.matches(mi)) { + return mi.getArguments().get(0).withPrefix(mi.getPrefix()); + } + } + return j; + } + } +} diff --git a/src/main/resources/META-INF/rewrite/no-guava.yml b/src/main/resources/META-INF/rewrite/no-guava.yml index 79a3878e..b1cf9854 100644 --- a/src/main/resources/META-INF/rewrite/no-guava.yml +++ b/src/main/resources/META-INF/rewrite/no-guava.yml @@ -82,6 +82,7 @@ tags: - RSPEC-4738 recipeList: - org.openrewrite.java.migrate.guava.PreferJavaUtilOptionalOrElseNull + - org.openrewrite.java.migrate.guava.NoGuavaOptionalFromJavaUtil - org.openrewrite.java.ChangeMethodName: methodPattern: com.google.common.base.Optional absent() newMethodName: empty diff --git a/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalTest.java b/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalTest.java index 6df4b2dc..ac7f7a73 100644 --- a/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalTest.java +++ b/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalTest.java @@ -135,7 +135,7 @@ void transformToMap() { import com.google.common.base.Optional; class A { - Optional foo(Optional optional) { + Optional foo(java.util.Optional optional) { return optional.transform(String::toUpperCase); } } @@ -150,50 +150,49 @@ Optional foo(Optional optional) { """)); } - @Nested - class NotYetImplemented { - @Test - @ExpectedToFail("Not yet implemented") - void orOptionalToTernary() { - // Comparison to java.util.Optional: this method has no equivalent in Java 8's Optional class; write thisOptional.isPresent() ? thisOptional : secondChoice instead. - //language=java - rewriteRun(java(""" + @Test + void removeFromJavaUtil() { + //language=java + rewriteRun(java(""" import com.google.common.base.Optional; class A { - Optional foo(Optional firstChoice, Optional secondChoice) { - return firstChoice.or(secondChoice); + Optional foo(java.util.Optional optional) { + return Optional.fromJavaUtil(optional); } } """, """ import java.util.Optional; class A { - Optional foo(Optional firstChoice, Optional secondChoice) { - return firstChoice.or(() -> secondChoice); + Optional foo(java.util.Optional optional) { + return optional; } } """)); - } + } + @Nested + class NotYetImplemented { @Test @ExpectedToFail("Not yet implemented") - void removeToJavaUtil() { + void orOptionalToTernary() { + // Comparison to java.util.Optional: this method has no equivalent in Java 8's Optional class; write thisOptional.isPresent() ? thisOptional : secondChoice instead. //language=java rewriteRun(java(""" import com.google.common.base.Optional; class A { - boolean foo() { - return Optional.absent().toJavaUtil().isEmpty(); + Optional foo(Optional firstChoice, Optional secondChoice) { + return firstChoice.or(secondChoice); } } """, """ import java.util.Optional; class A { - boolean foo() { - return Optional.empty().isEmpty(); + Optional foo(Optional firstChoice, Optional secondChoice) { + return firstChoice.or(() -> secondChoice); } } """)); @@ -201,22 +200,22 @@ boolean foo() { @Test @ExpectedToFail("Not yet implemented") - void removeFromJavaUtil() { + void removeToJavaUtil() { //language=java rewriteRun(java(""" import com.google.common.base.Optional; class A { - Optional foo(java.util.Optional optional) { - return Optional.fromJavaUtil(optional); + boolean foo() { + return Optional.absent().toJavaUtil().isEmpty(); } } """, """ import java.util.Optional; class A { - Optional foo(java.util.Optional optional) { - return optional; + boolean foo() { + return Optional.empty().isEmpty(); } } """)); From 3852663fb99d33c416557a70999f191bfaf0ce9c Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Thu, 30 Mar 2023 22:08:56 +0200 Subject: [PATCH 10/15] Add NoGuavaOptionalToJavaUtil --- .../guava/NoGuavaOptionalToJavaUtil.java | 92 +++++++++++++++++++ .../resources/META-INF/rewrite/no-guava.yml | 1 + .../guava/PreferJavaUtilOptionalTest.java | 35 ++++--- 3 files changed, 110 insertions(+), 18 deletions(-) create mode 100644 src/main/java/org/openrewrite/java/migrate/guava/NoGuavaOptionalToJavaUtil.java diff --git a/src/main/java/org/openrewrite/java/migrate/guava/NoGuavaOptionalToJavaUtil.java b/src/main/java/org/openrewrite/java/migrate/guava/NoGuavaOptionalToJavaUtil.java new file mode 100644 index 00000000..5250ebbb --- /dev/null +++ b/src/main/java/org/openrewrite/java/migrate/guava/NoGuavaOptionalToJavaUtil.java @@ -0,0 +1,92 @@ +/* + * Copyright 2023 the original author or authors. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * https://www.apache.org/licenses/LICENSE-2.0 + *

+ * 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.Recipe; +import org.openrewrite.java.JavaVisitor; +import org.openrewrite.java.MethodMatcher; +import org.openrewrite.java.search.UsesMethod; +import org.openrewrite.java.search.UsesType; +import org.openrewrite.java.tree.J; + +import java.time.Duration; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +public class NoGuavaOptionalToJavaUtil extends Recipe { + + static final MethodMatcher METHOD_MATCHER = new MethodMatcher("com.google.common.base.Optional toJavaUtil()"); + + @Override + public String getDisplayName() { + return "Remove `com.google.common.base.Optional#toJavaUtil()`"; + } + + @Override + public String getDescription() { + return "Remove calls to `com.google.common.base.Optional#toJavaUtil()`."; + } + + @Override + public Duration getEstimatedEffortPerOccurrence() { + return Duration.ofMinutes(5); + } + + @Override + public Set getTags() { + return new HashSet<>(Arrays.asList("RSPEC-4738", "guava")); + } + + @Override + protected UsesType getApplicableTest() { + return new UsesType<>("com.google.common.base.Optional"); + } + + @Override + protected UsesMethod getSingleSourceApplicableTest() { + return new UsesMethod<>(METHOD_MATCHER); + } + + @Override + protected JavaVisitor getVisitor() { + return new PreferJavaUtilOptionalOrElseNullVisitor(); + } + + private static class PreferJavaUtilOptionalOrElseNullVisitor extends JavaVisitor { + + @Override + public J visitCompilationUnit(J.CompilationUnit cu, ExecutionContext executionContext) { + J c = super.visitCompilationUnit(cu, executionContext); + maybeAddImport("java.util.Optional"); + maybeRemoveImport("com.google.common.base.Optional"); + return c; + } + + @Override + public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext executionContext) { + J j = super.visitMethodInvocation(method, executionContext); + if (j instanceof J.MethodInvocation) { + J.MethodInvocation mi = (J.MethodInvocation) j; + if (METHOD_MATCHER.matches(mi)) { + return mi.getSelect(); + } + } + return j; + } + } +} diff --git a/src/main/resources/META-INF/rewrite/no-guava.yml b/src/main/resources/META-INF/rewrite/no-guava.yml index b1cf9854..68ac9777 100644 --- a/src/main/resources/META-INF/rewrite/no-guava.yml +++ b/src/main/resources/META-INF/rewrite/no-guava.yml @@ -83,6 +83,7 @@ tags: recipeList: - org.openrewrite.java.migrate.guava.PreferJavaUtilOptionalOrElseNull - org.openrewrite.java.migrate.guava.NoGuavaOptionalFromJavaUtil + - org.openrewrite.java.migrate.guava.NoGuavaOptionalToJavaUtil - org.openrewrite.java.ChangeMethodName: methodPattern: com.google.common.base.Optional absent() newMethodName: empty diff --git a/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalTest.java b/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalTest.java index ac7f7a73..8ee6f2c5 100644 --- a/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalTest.java +++ b/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalTest.java @@ -172,50 +172,49 @@ Optional foo(java.util.Optional optional) { """)); } - @Nested - class NotYetImplemented { - @Test - @ExpectedToFail("Not yet implemented") - void orOptionalToTernary() { - // Comparison to java.util.Optional: this method has no equivalent in Java 8's Optional class; write thisOptional.isPresent() ? thisOptional : secondChoice instead. - //language=java - rewriteRun(java(""" + @Test + void removeToJavaUtil() { + //language=java + rewriteRun(java(""" import com.google.common.base.Optional; class A { - Optional foo(Optional firstChoice, Optional secondChoice) { - return firstChoice.or(secondChoice); + boolean foo() { + return Optional.absent().toJavaUtil().isEmpty(); } } """, """ import java.util.Optional; class A { - Optional foo(Optional firstChoice, Optional secondChoice) { - return firstChoice.or(() -> secondChoice); + boolean foo() { + return Optional.empty().isEmpty(); } } """)); - } + } + @Nested + class NotYetImplemented { @Test @ExpectedToFail("Not yet implemented") - void removeToJavaUtil() { + void orOptionalToTernary() { + // Comparison to java.util.Optional: this method has no equivalent in Java 8's Optional class; write thisOptional.isPresent() ? thisOptional : secondChoice instead. //language=java rewriteRun(java(""" import com.google.common.base.Optional; class A { - boolean foo() { - return Optional.absent().toJavaUtil().isEmpty(); + Optional foo(Optional firstChoice, Optional secondChoice) { + return firstChoice.or(secondChoice); } } """, """ import java.util.Optional; class A { - boolean foo() { - return Optional.empty().isEmpty(); + Optional foo(Optional firstChoice, Optional secondChoice) { + return firstChoice.or(() -> secondChoice); } } """)); From 2d5f1bd4d86007f4ea08a5fe7d4bd72faae38c34 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Thu, 30 Mar 2023 22:20:55 +0200 Subject: [PATCH 11/15] Use better visitor names --- .../java/migrate/guava/NoGuavaOptionalFromJavaUtil.java | 4 ++-- .../java/migrate/guava/NoGuavaOptionalToJavaUtil.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/openrewrite/java/migrate/guava/NoGuavaOptionalFromJavaUtil.java b/src/main/java/org/openrewrite/java/migrate/guava/NoGuavaOptionalFromJavaUtil.java index bb0cbd0c..d1893ea0 100644 --- a/src/main/java/org/openrewrite/java/migrate/guava/NoGuavaOptionalFromJavaUtil.java +++ b/src/main/java/org/openrewrite/java/migrate/guava/NoGuavaOptionalFromJavaUtil.java @@ -64,10 +64,10 @@ protected UsesMethod getSingleSourceApplicableTest() { @Override protected JavaVisitor getVisitor() { - return new PreferJavaUtilOptionalOrElseNullVisitor(); + return new ReplaceFromJavaUtilVisitor(); } - private static class PreferJavaUtilOptionalOrElseNullVisitor extends JavaVisitor { + private static class ReplaceFromJavaUtilVisitor extends JavaVisitor { @Override public J visitCompilationUnit(J.CompilationUnit cu, ExecutionContext executionContext) { diff --git a/src/main/java/org/openrewrite/java/migrate/guava/NoGuavaOptionalToJavaUtil.java b/src/main/java/org/openrewrite/java/migrate/guava/NoGuavaOptionalToJavaUtil.java index 5250ebbb..cc905235 100644 --- a/src/main/java/org/openrewrite/java/migrate/guava/NoGuavaOptionalToJavaUtil.java +++ b/src/main/java/org/openrewrite/java/migrate/guava/NoGuavaOptionalToJavaUtil.java @@ -64,10 +64,10 @@ protected UsesMethod getSingleSourceApplicableTest() { @Override protected JavaVisitor getVisitor() { - return new PreferJavaUtilOptionalOrElseNullVisitor(); + return new ReplaceToJavaUtilVisitor(); } - private static class PreferJavaUtilOptionalOrElseNullVisitor extends JavaVisitor { + private static class ReplaceToJavaUtilVisitor extends JavaVisitor { @Override public J visitCompilationUnit(J.CompilationUnit cu, ExecutionContext executionContext) { From 968a4bc723ccce7bfbf92cc10edd6c33bc6de6a4 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Thu, 30 Mar 2023 22:21:42 +0200 Subject: [PATCH 12/15] Add PreferJavaUtilOptionalOrSupplier --- .../PreferJavaUtilOptionalOrSupplier.java | 96 +++++++++++++++++++ .../resources/META-INF/rewrite/no-guava.yml | 1 + .../guava/PreferJavaUtilOptionalTest.java | 18 ++-- 3 files changed, 106 insertions(+), 9 deletions(-) create mode 100644 src/main/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalOrSupplier.java diff --git a/src/main/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalOrSupplier.java b/src/main/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalOrSupplier.java new file mode 100644 index 00000000..f1bf73bc --- /dev/null +++ b/src/main/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalOrSupplier.java @@ -0,0 +1,96 @@ +/* + * Copyright 2023 the original author or authors. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * https://www.apache.org/licenses/LICENSE-2.0 + *

+ * 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.Recipe; +import org.openrewrite.java.JavaIsoVisitor; +import org.openrewrite.java.JavaTemplate; +import org.openrewrite.java.MethodMatcher; +import org.openrewrite.java.search.UsesMethod; +import org.openrewrite.java.search.UsesType; +import org.openrewrite.java.tree.J; + +import java.time.Duration; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +public class PreferJavaUtilOptionalOrSupplier extends Recipe { + + static final MethodMatcher METHOD_MATCHER = new MethodMatcher("com.google.common.base.Optional or(com.google.common.base.Optional)"); + + @Override + public String getDisplayName() { + return "Prefer `java.util.Optional#or(Supplier>)`"; + } + + @Override + public String getDescription() { + return "Prefer `java.util.Optional#or(Supplier>)` over `com.google.common.base.Optional#or(com.google.common.base.Optional)."; + } + + @Override + public Duration getEstimatedEffortPerOccurrence() { + return Duration.ofMinutes(5); + } + + @Override + public Set getTags() { + return new HashSet<>(Arrays.asList("RSPEC-4738", "guava")); + } + + @Override + protected UsesType getApplicableTest() { + return new UsesType<>("com.google.common.base.Optional"); + } + + @Override + protected UsesMethod getSingleSourceApplicableTest() { + return new UsesMethod<>(METHOD_MATCHER); + } + + @Override + protected JavaIsoVisitor getVisitor() { + return new PreferJavaUtilOptionalOrSupplierVisitor(); + } + + private static class PreferJavaUtilOptionalOrSupplierVisitor extends JavaIsoVisitor { + @Override + public J.CompilationUnit visitCompilationUnit(J.CompilationUnit cu, ExecutionContext executionContext) { + J.CompilationUnit c = super.visitCompilationUnit(cu, executionContext); + maybeAddImport("java.util.Optional"); + maybeRemoveImport("com.google.common.base.Optional"); + return c; + } + + @Override + public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext executionContext) { + J.MethodInvocation j = super.visitMethodInvocation(method, executionContext); + if (METHOD_MATCHER.matches(method)) { + j = j.withTemplate( + JavaTemplate.builder(this::getCursor, "#{any(java.util.Optional)}.or(() -> #{any(java.util.Optional)})") + .imports("java.util.Optional") + .build(), + method.getCoordinates().replace(), + j.getSelect(), + j.getArguments().get(0) + ); + } + return j; + } + } +} diff --git a/src/main/resources/META-INF/rewrite/no-guava.yml b/src/main/resources/META-INF/rewrite/no-guava.yml index 68ac9777..8162a116 100644 --- a/src/main/resources/META-INF/rewrite/no-guava.yml +++ b/src/main/resources/META-INF/rewrite/no-guava.yml @@ -81,6 +81,7 @@ tags: - guava - RSPEC-4738 recipeList: + - org.openrewrite.java.migrate.guava.PreferJavaUtilOptionalOrSupplier - org.openrewrite.java.migrate.guava.PreferJavaUtilOptionalOrElseNull - org.openrewrite.java.migrate.guava.NoGuavaOptionalFromJavaUtil - org.openrewrite.java.migrate.guava.NoGuavaOptionalToJavaUtil diff --git a/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalTest.java b/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalTest.java index 8ee6f2c5..cdd15ca9 100644 --- a/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalTest.java +++ b/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalTest.java @@ -194,14 +194,11 @@ boolean foo() { """)); } - @Nested - class NotYetImplemented { - @Test - @ExpectedToFail("Not yet implemented") - void orOptionalToTernary() { - // Comparison to java.util.Optional: this method has no equivalent in Java 8's Optional class; write thisOptional.isPresent() ? thisOptional : secondChoice instead. - //language=java - rewriteRun(java(""" + @Test + void orOptionalToOrSupplier() { + // Comparison to java.util.Optional: this method has no equivalent in Java 8's Optional class; write thisOptional.isPresent() ? thisOptional : secondChoice instead. + //language=java + rewriteRun(java(""" import com.google.common.base.Optional; class A { @@ -218,7 +215,10 @@ Optional foo(Optional firstChoice, Optional secondChoice } } """)); - } + } + + @Nested + class NotYetImplemented { @Test @ExpectedToFail("Not yet implemented") From 5913b4dcd041846dbf1d8c36818ea8accfc4230c Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Thu, 30 Mar 2023 22:29:11 +0200 Subject: [PATCH 13/15] Only use or(Supplier) on Java 9+ --- .../migrate/guava/PreferJavaUtilOptionalOrSupplier.java | 9 +++++++-- .../java/migrate/guava/PreferJavaUtilOptionalTest.java | 7 +++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalOrSupplier.java b/src/main/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalOrSupplier.java index f1bf73bc..a85161d9 100644 --- a/src/main/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalOrSupplier.java +++ b/src/main/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalOrSupplier.java @@ -15,11 +15,14 @@ */ package org.openrewrite.java.migrate.guava; +import org.openrewrite.Applicability; import org.openrewrite.ExecutionContext; import org.openrewrite.Recipe; +import org.openrewrite.TreeVisitor; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.JavaTemplate; import org.openrewrite.java.MethodMatcher; +import org.openrewrite.java.search.UsesJavaVersion; import org.openrewrite.java.search.UsesMethod; import org.openrewrite.java.search.UsesType; import org.openrewrite.java.tree.J; @@ -54,8 +57,10 @@ public Set getTags() { } @Override - protected UsesType getApplicableTest() { - return new UsesType<>("com.google.common.base.Optional"); + protected TreeVisitor getApplicableTest() { + return Applicability.and( + new UsesJavaVersion<>(9), + new UsesType<>("com.google.common.base.Optional")); } @Override diff --git a/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalTest.java b/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalTest.java index cdd15ca9..8af126f7 100644 --- a/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalTest.java +++ b/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalTest.java @@ -25,6 +25,7 @@ import org.openrewrite.test.RewriteTest; import static org.openrewrite.java.Assertions.java; +import static org.openrewrite.java.Assertions.javaVersion; @Issue("https://github.com/openrewrite/rewrite-migrate-java/issues/197") class PreferJavaUtilOptionalTest implements RewriteTest { @@ -135,7 +136,7 @@ void transformToMap() { import com.google.common.base.Optional; class A { - Optional foo(java.util.Optional optional) { + Optional foo(Optional optional) { return optional.transform(String::toUpperCase); } } @@ -198,7 +199,9 @@ boolean foo() { void orOptionalToOrSupplier() { // Comparison to java.util.Optional: this method has no equivalent in Java 8's Optional class; write thisOptional.isPresent() ? thisOptional : secondChoice instead. //language=java - rewriteRun(java(""" + rewriteRun( + spec -> spec.allSources(s -> s.markers(javaVersion(9))), + java(""" import com.google.common.base.Optional; class A { From 06a5a8db9744ab67f405031cd476e65b699e3118 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Fri, 31 Mar 2023 09:27:51 +0200 Subject: [PATCH 14/15] Convert Optional before Function, to match transform(Function) --- src/main/resources/META-INF/rewrite/no-guava.yml | 2 +- .../java/migrate/guava/PreferJavaUtilOptionalTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/META-INF/rewrite/no-guava.yml b/src/main/resources/META-INF/rewrite/no-guava.yml index 8162a116..b5b13358 100644 --- a/src/main/resources/META-INF/rewrite/no-guava.yml +++ b/src/main/resources/META-INF/rewrite/no-guava.yml @@ -32,8 +32,8 @@ recipeList: - org.openrewrite.java.migrate.guava.NoGuavaSetsNewHashSet - org.openrewrite.java.migrate.guava.NoGuavaSetsNewConcurrentHashSet - org.openrewrite.java.migrate.guava.NoGuavaSetsNewLinkedHashSet - - org.openrewrite.java.migrate.guava.PreferJavaUtilFunction - org.openrewrite.java.migrate.guava.PreferJavaUtilOptional + - org.openrewrite.java.migrate.guava.PreferJavaUtilFunction - org.openrewrite.java.migrate.guava.PreferJavaUtilPredicate - org.openrewrite.java.migrate.guava.PreferJavaUtilSupplier - org.openrewrite.java.migrate.guava.PreferJavaUtilObjectsEquals diff --git a/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalTest.java b/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalTest.java index 8af126f7..a6dd58b8 100644 --- a/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalTest.java +++ b/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalTest.java @@ -35,7 +35,7 @@ public void defaults(RecipeSpec spec) { Environment.builder() .scanRuntimeClasspath("org.openrewrite.java.migrate.guava") .build() - .activateRecipes("org.openrewrite.java.migrate.guava.PreferJavaUtilOptional") + .activateRecipes("org.openrewrite.java.migrate.guava.NoGuava") ) .parser(JavaParser.fromJavaVersion().classpath("rewrite-java", "guava")); } From bb48f4c31c88b8b03baf01f8615cb1d22e72b2c2 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Fri, 31 Mar 2023 09:40:51 +0200 Subject: [PATCH 15/15] Cover orSupplierToOrElseGetWithSupplierArgument --- .../guava/PreferJavaUtilOptionalTest.java | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalTest.java b/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalTest.java index a6dd58b8..ce6f47d4 100644 --- a/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalTest.java +++ b/src/test/java/org/openrewrite/java/migrate/guava/PreferJavaUtilOptionalTest.java @@ -108,7 +108,7 @@ Optional foo(Optional optional) { } @Test - void orSupplierToOrElseGet() { + void orSupplierToOrElseGetWithLambda() { //language=java rewriteRun(java(""" import com.google.common.base.Optional; @@ -129,6 +129,31 @@ Optional foo(Optional optional) { """)); } + @Test + void orSupplierToOrElseGetWithSupplierArgument() { + //language=java + rewriteRun( + java(""" + import com.google.common.base.Optional; + import com.google.common.base.Supplier; + + class A { + String foo(Optional optional, Supplier supplier) { + return optional.or(supplier); + } + } + """, """ + import java.util.Optional; + import java.util.function.Supplier; + + class A { + String foo(Optional optional, Supplier supplier) { + return optional.orElseGet(supplier); + } + } + """)); + } + @Test void transformToMap() { //language=java