From cdba5a27ac6927d12763934bab7540697698082e Mon Sep 17 00:00:00 2001 From: Anu Ramamoorthy <93149514+ranuradh@users.noreply.github.com> Date: Fri, 9 Aug 2024 14:26:14 -0400 Subject: [PATCH] Recipe ReplaceAWTGetPeerMethod in Java 11 Migration (#524) * getPeer() recipe * updated with fixes * small updates * Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Apply suggestions from code review * Apply formatter & order imports * Align before and after code blocks again * updated recipe name, updated tests and formatted code * removing extra file commit and test update * adding comments, Test file cleanup * Demonstrate the need for `super.visitControlParentheses(cp, ctx)` * Only override `visitBinary` and `visitInstanceOf` * Update src/test/java/org/openrewrite/java/migrate/ReplaceAWTGetPeerMethodTest.java * update to code and adding missing arguments to .yml * right format for java-version.yml * Remove import after instanceof replacement * Also handle flipped case * Replace `checkClassNameIsEqualToFQCN` with TypeUtils * Remove duplication in arguments * Document options and shorten description * fixed small typo in description --------- Co-authored-by: anuram Co-authored-by: Tim te Beek Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .../java/migrate/ReplaceAWTGetPeerMethod.java | 119 +++++++++++++++ .../META-INF/rewrite/java-version-11.yml | 6 +- .../migrate/ReplaceAWTGetPeerMethodTest.java | 141 ++++++++++++++++++ 3 files changed, 264 insertions(+), 2 deletions(-) create mode 100644 src/main/java/org/openrewrite/java/migrate/ReplaceAWTGetPeerMethod.java create mode 100644 src/test/java/org/openrewrite/java/migrate/ReplaceAWTGetPeerMethodTest.java diff --git a/src/main/java/org/openrewrite/java/migrate/ReplaceAWTGetPeerMethod.java b/src/main/java/org/openrewrite/java/migrate/ReplaceAWTGetPeerMethod.java new file mode 100644 index 0000000000..6b4e490452 --- /dev/null +++ b/src/main/java/org/openrewrite/java/migrate/ReplaceAWTGetPeerMethod.java @@ -0,0 +1,119 @@ +/* + * Copyright 2024 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; + +import lombok.EqualsAndHashCode; +import lombok.Value; +import org.openrewrite.*; +import org.openrewrite.internal.lang.Nullable; +import org.openrewrite.java.ChangeMethodName; +import org.openrewrite.java.JavaVisitor; +import org.openrewrite.java.MethodMatcher; +import org.openrewrite.java.search.UsesMethod; +import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.TypeUtils; +import org.openrewrite.java.tree.TypedTree; + +@Value +@EqualsAndHashCode(callSuper = false) +class ReplaceAWTGetPeerMethod extends Recipe { + + @Option(displayName = "Method pattern to replace", + description = "The method pattern to match and replace.", + example = "java.awt.* getPeer()") + String getPeerMethodPattern; + + @Option(displayName = "The LightweightPeer interface FQCN", + description = "The fully qualified class name of the LightweightPeer interface to replace in `instanceof`.", + example = "java.awt.peer.LightweightPeer") + String lightweightPeerFQCN; + + @Override + public String getDisplayName() { + return "Replace AWT `getPeer()` method"; + } + + @Override + public String getDescription() { + return "This recipe replaces the use of `getPeer()` method in `java.awt.*` classes. " + + "`component.getPeer() != null` is replaced with `component.isDisplayable()` and " + + "`component.getPeer() instanceof LightweightPeer` is replaced with `component.isLightweight()`."; + } + + @Override + public TreeVisitor getVisitor() { + MethodMatcher methodMatcherGetPeer = new MethodMatcher(getPeerMethodPattern); + return Preconditions.check(new UsesMethod<>(methodMatcherGetPeer), new JavaVisitor() { + @Override + public J visitBinary(J.Binary binary, ExecutionContext ctx) { + J.Binary bi = (J.Binary) super.visitBinary(binary, ctx); + + J.MethodInvocation mi = findMatchingMethodInvocation(bi); + if (mi != null) { + mi = (J.MethodInvocation) new ChangeMethodName( + getPeerMethodPattern, "isDisplayable", true, null) + .getVisitor().visit(mi, ctx); + mi = (J.MethodInvocation) new ChangeMethodInvocationReturnType( + getPeerMethodPattern.split(" ")[0] + " isDisplayable()", "boolean") + .getVisitor().visit(mi, ctx); + assert mi != null; + return mi.withPrefix(bi.getPrefix()); + } + + return bi; + } + + private @Nullable J.MethodInvocation findMatchingMethodInvocation(J.Binary binaryCondition) { + J.MethodInvocation mi = null; + if (binaryCondition.getOperator() == J.Binary.Type.NotEqual) { + if (binaryCondition.getLeft() instanceof J.MethodInvocation && + binaryCondition.getRight() instanceof J.Literal) { + mi = (J.MethodInvocation) binaryCondition.getLeft(); + } else if (binaryCondition.getLeft() instanceof J.Literal && + binaryCondition.getRight() instanceof J.MethodInvocation) { + mi = (J.MethodInvocation) binaryCondition.getRight(); + } + } + if (methodMatcherGetPeer.matches(mi)) { + return mi; + } + return null; + } + + @Override + public J visitInstanceOf(J.InstanceOf instOf, ExecutionContext ctx) { + J.InstanceOf instanceOfVar = (J.InstanceOf) super.visitInstanceOf(instOf, ctx); + + if (instanceOfVar.getExpression() instanceof J.MethodInvocation) { + J.MethodInvocation mi = ((J.MethodInvocation) instanceOfVar.getExpression()); + if (methodMatcherGetPeer.matches(mi) && TypeUtils.isAssignableTo(lightweightPeerFQCN, ((TypedTree) instanceOfVar.getClazz()).getType())) { + mi = (J.MethodInvocation) new ChangeMethodName(getPeerMethodPattern, "isLightweight", true, null) + .getVisitor().visit(mi, ctx); + mi = (J.MethodInvocation) new ChangeMethodInvocationReturnType( + getPeerMethodPattern.split(" ")[0] + " isLightweight()", "boolean") + .getVisitor().visit(mi, ctx); + assert mi != null; + maybeRemoveImport(lightweightPeerFQCN); + return mi.withPrefix(instanceOfVar.getPrefix()); + } + } + + return instanceOfVar; + } + }); + } +} diff --git a/src/main/resources/META-INF/rewrite/java-version-11.yml b/src/main/resources/META-INF/rewrite/java-version-11.yml index 8bee063e3c..7f61cda364 100644 --- a/src/main/resources/META-INF/rewrite/java-version-11.yml +++ b/src/main/resources/META-INF/rewrite/java-version-11.yml @@ -70,7 +70,9 @@ recipeList: - org.openrewrite.java.migrate.RemovedPolicy - org.openrewrite.java.migrate.ReferenceCloneMethod - org.openrewrite.java.migrate.ThreadStopDestroy - + - org.openrewrite.java.migrate.ReplaceAWTGetPeerMethod: + getPeerMethodPattern: java.awt.* getPeer() + lightweightPeerFQCN: java.awt.peer.LightweightPeer --- type: specs.openrewrite.org/v1beta/recipe name: org.openrewrite.java.migrate.UpgradeBuildToJava11 @@ -281,4 +283,4 @@ recipeList: fullyQualifiedTargetTypeName: java.nio.file.Path - org.openrewrite.java.ChangeMethodName: methodPattern: java.nio.file.Path get(..) - newMethodName: of \ No newline at end of file + newMethodName: of diff --git a/src/test/java/org/openrewrite/java/migrate/ReplaceAWTGetPeerMethodTest.java b/src/test/java/org/openrewrite/java/migrate/ReplaceAWTGetPeerMethodTest.java new file mode 100644 index 0000000000..a861795077 --- /dev/null +++ b/src/test/java/org/openrewrite/java/migrate/ReplaceAWTGetPeerMethodTest.java @@ -0,0 +1,141 @@ +/* + * Copyright 2024 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; + +import org.junit.jupiter.api.Test; +import org.openrewrite.DocumentExample; +import org.openrewrite.java.JavaParser; +import org.openrewrite.test.RecipeSpec; +import org.openrewrite.test.RewriteTest; + +import static org.openrewrite.java.Assertions.java; + +@SuppressWarnings("StatementWithEmptyBody") +class ReplaceAWTGetPeerMethodTest implements RewriteTest { + @Override + public void defaults(RecipeSpec spec) { + spec.recipe(new ReplaceAWTGetPeerMethod("com.test.Component1 getPeer()", "com.test.TestDummy")) + .parser(JavaParser.fromJavaVersion() + //language=java + .dependsOn( + """ + package com.test; + public class Component1 { + public String getPeer() { + return "x"; + } + public boolean isDisplayable() { + return true; + } + public boolean isLightweight() { + return true; + } + } + """, + """ + package com.test; + public class TestDummy { + } + """ + ) + ); + } + + @Test + @DocumentExample + void instanceAndGetPeerMethodControlParentheses() { + rewriteRun( + //language=java + java( + """ + import com.test.Component1; + import com.test.TestDummy; + class Test { + void foo() { + Test t1 = new Test(); + Component1 c = new Component1(); + if (c.getPeer() instanceof com.test.TestDummy) { + } + if (c.getPeer() instanceof TestDummy) { + } + Component1 y = new Component1(); + if (y.getPeer() != null) { + } + if (null != y.getPeer()) { + } + } + } + """, + """ + import com.test.Component1; + class Test { + void foo() { + Test t1 = new Test(); + Component1 c = new Component1(); + if (c.isLightweight()) { + } + if (c.isLightweight()) { + } + Component1 y = new Component1(); + if (y.isDisplayable()) { + } + if (y.isDisplayable()) { + } + } + } + """ + ) + ); + } + + @Test + void instanceAndGetPeerMethodNoParenthesis() { + rewriteRun( + //language=java + java( + """ + import com.test.Component1; + import com.test.TestDummy; + class Test { + void foo() { + Component1 y = new Component1(); + boolean instance = y.getPeer() instanceof TestDummy; + if (instance){ + } + boolean instance1 = y.getPeer() != null; + if (instance1){ + } + } + } + """, + """ + import com.test.Component1; + class Test { + void foo() { + Component1 y = new Component1(); + boolean instance = y.isLightweight(); + if (instance){ + } + boolean instance1 = y.isDisplayable(); + if (instance1){ + } + } + } + """ + ) + ); + } +}