diff --git a/src/main/java/org/openrewrite/java/template/processor/RefasterTemplateProcessor.java b/src/main/java/org/openrewrite/java/template/processor/RefasterTemplateProcessor.java index 83f01148..154dfd68 100644 --- a/src/main/java/org/openrewrite/java/template/processor/RefasterTemplateProcessor.java +++ b/src/main/java/org/openrewrite/java/template/processor/RefasterTemplateProcessor.java @@ -353,82 +353,95 @@ private String newAbstractRefasterJavaVisitor(Map be } visitor.append("\n"); - List lstTypes = LST_TYPE_MAP.get(getType(descriptor.beforeTemplates.get(0).method)); - for (String lstType : lstTypes) { - String methodSuffix = lstType.startsWith("J.") ? lstType.substring(2) : lstType; - visitor.append(" @Override\n"); - visitor.append(" public J visit").append(methodSuffix).append("(").append(lstType).append(" elem, ExecutionContext ctx) {\n"); - if (lstType.equals("Statement")) { - visitor.append(" if (elem instanceof J.Block) {\n"); - visitor.append(" // FIXME workaround\n"); - visitor.append(" return elem;\n"); - visitor.append(" }\n"); - } + SortedSet visitMethods = new TreeSet<>(); + beforeTemplates.entrySet().stream() + .collect(groupingBy( + entry -> getType(entry.getValue().method), + toMap(Map.Entry::getKey, Map.Entry::getValue))) + .forEach((type, templates) -> { + for (String lstType : LST_TYPE_MAP.get(type)) { + visitMethods.add(generateVisitMethod(templates, after, descriptor, lstType)); + } + }); + visitMethods.forEach(visitor::append); + visitor.append(" }"); + return visitor.toString(); + } - visitor.append(" JavaTemplate.Matcher matcher;\n"); - for (Map.Entry entry : beforeTemplates.entrySet()) { - int arity = entry.getValue().getArity(); - for (int i = 0; i < arity; i++) { - visitor.append(" if (" + "(matcher = ").append(entry.getKey()).append(arity > 1 ? "$" + i : "").append(".matcher(getCursor())).find()").append(") {\n"); - com.sun.tools.javac.util.List jcVariableDecls = entry.getValue().method.getParameters(); - for (int j = 0; j < jcVariableDecls.size(); j++) { - JCTree.JCVariableDecl param = jcVariableDecls.get(j); - com.sun.tools.javac.util.List annotations = param.getModifiers().getAnnotations(); - for (JCTree.JCAnnotation jcAnnotation : annotations) { - String annotationType = jcAnnotation.attribute.type.tsym.getQualifiedName().toString(); - if (annotationType.equals("org.openrewrite.java.template.NotMatches")) { - String matcher = ((Type.ClassType) jcAnnotation.attribute.getValue().values.get(0).snd.getValue()).tsym.getQualifiedName().toString(); - visitor.append(" if (new ").append(matcher).append("().matches((Expression) matcher.parameter(").append(j).append("))) {\n"); - visitor.append(" return super.visit").append(methodSuffix).append("(elem, ctx);\n"); - visitor.append(" }\n"); - } else if (annotationType.equals("org.openrewrite.java.template.Matches")) { - String matcher = ((Type.ClassType) jcAnnotation.attribute.getValue().values.get(0).snd.getValue()).tsym.getQualifiedName().toString(); - visitor.append(" if (!new ").append(matcher).append("().matches((Expression) matcher.parameter(").append(j).append("))) {\n"); - visitor.append(" return super.visit").append(methodSuffix).append("(elem, ctx);\n"); - visitor.append(" }\n"); - } + private String generateVisitMethod(Map beforeTemplates, String after, RuleDescriptor descriptor, String lstType) { + StringBuilder visitMethod = new StringBuilder(); + String methodSuffix = lstType.startsWith("J.") ? lstType.substring(2) : lstType; + visitMethod.append(" @Override\n"); + visitMethod.append(" public J visit").append(methodSuffix).append("(").append(lstType).append(" elem, ExecutionContext ctx) {\n"); + if (lstType.equals("Statement")) { + visitMethod.append(" if (elem instanceof J.Block) {\n"); + visitMethod.append(" // FIXME workaround\n"); + visitMethod.append(" return elem;\n"); + visitMethod.append(" }\n"); + } + + visitMethod.append(" JavaTemplate.Matcher matcher;\n"); + for (Map.Entry entry : beforeTemplates.entrySet()) { + int arity = entry.getValue().getArity(); + for (int i = 0; i < arity; i++) { + visitMethod.append(" if (" + "(matcher = ").append(entry.getKey()).append(arity > 1 ? "$" + i : "").append(".matcher(getCursor())).find()").append(") {\n"); + com.sun.tools.javac.util.List jcVariableDecls = entry.getValue().method.getParameters(); + for (int j = 0; j < jcVariableDecls.size(); j++) { + JCTree.JCVariableDecl param = jcVariableDecls.get(j); + com.sun.tools.javac.util.List annotations = param.getModifiers().getAnnotations(); + for (JCTree.JCAnnotation jcAnnotation : annotations) { + String annotationType = jcAnnotation.attribute.type.tsym.getQualifiedName().toString(); + if (annotationType.equals("org.openrewrite.java.template.NotMatches")) { + String matcher = ((Type.ClassType) jcAnnotation.attribute.getValue().values.get(0).snd.getValue()).tsym.getQualifiedName().toString(); + visitMethod.append(" if (new ").append(matcher).append("().matches((Expression) matcher.parameter(").append(j).append("))) {\n"); + visitMethod.append(" return super.visit").append(methodSuffix).append("(elem, ctx);\n"); + visitMethod.append(" }\n"); + } else if (annotationType.equals("org.openrewrite.java.template.Matches")) { + String matcher = ((Type.ClassType) jcAnnotation.attribute.getValue().values.get(0).snd.getValue()).tsym.getQualifiedName().toString(); + visitMethod.append(" if (!new ").append(matcher).append("().matches((Expression) matcher.parameter(").append(j).append("))) {\n"); + visitMethod.append(" return super.visit").append(methodSuffix).append("(elem, ctx);\n"); + visitMethod.append(" }\n"); } } + } - if (descriptor.afterTemplate == null) { - visitor.append(" return SearchResult.found(elem);\n"); - } else { - maybeRemoveImports(imports, visitor, entry.getValue(), i, descriptor.afterTemplate); - maybeRemoveStaticImports(staticImports, visitor, entry.getValue(), i, descriptor.afterTemplate); - - List embedOptions = new ArrayList<>(); - JCTree.JCExpression afterReturn = getReturnExpression(descriptor.afterTemplate.method); - if (afterReturn instanceof JCTree.JCParens || - afterReturn instanceof JCTree.JCUnary && ((JCTree.JCUnary) afterReturn).getExpression() instanceof JCTree.JCParens) { - embedOptions.add("REMOVE_PARENS"); - } - // TODO check if after template contains type or member references - embedOptions.add("SHORTEN_NAMES"); - if (simplifyBooleans(descriptor.afterTemplate.method)) { - embedOptions.add("SIMPLIFY_BOOLEANS"); - } + if (descriptor.afterTemplate == null) { + visitMethod.append(" return SearchResult.found(elem);\n"); + } else { + maybeRemoveImports(imports, visitMethod, entry.getValue(), i, descriptor.afterTemplate); + maybeRemoveStaticImports(staticImports, visitMethod, entry.getValue(), i, descriptor.afterTemplate); + + List embedOptions = new ArrayList<>(); + JCTree.JCExpression afterReturn = getReturnExpression(descriptor.afterTemplate.method); + if (afterReturn instanceof JCTree.JCParens || + afterReturn instanceof JCTree.JCUnary && ((JCTree.JCUnary) afterReturn).getExpression() instanceof JCTree.JCParens) { + embedOptions.add("REMOVE_PARENS"); + } + // TODO check if after template contains type or member references + embedOptions.add("SHORTEN_NAMES"); + if (simplifyBooleans(descriptor.afterTemplate.method)) { + embedOptions.add("SIMPLIFY_BOOLEANS"); + } - visitor.append(" return embed(\n"); - visitor.append(" ").append(after).append(".apply(getCursor(), elem.getCoordinates().replace()"); - String parameters = parameters(entry.getValue(), descriptor); - if (!parameters.isEmpty()) { - visitor.append(", ").append(parameters); - } - visitor.append("),\n"); - visitor.append(" getCursor(),\n"); - visitor.append(" ctx,\n"); - visitor.append(" ").append(String.join(", ", embedOptions)).append("\n"); - visitor.append(" );\n"); + visitMethod.append(" return embed(\n"); + visitMethod.append(" ").append(after).append(".apply(getCursor(), elem.getCoordinates().replace()"); + String parameters = parameters(entry.getValue(), descriptor); + if (!parameters.isEmpty()) { + visitMethod.append(", ").append(parameters); } - visitor.append(" }\n"); + visitMethod.append("),\n"); + visitMethod.append(" getCursor(),\n"); + visitMethod.append(" ctx,\n"); + visitMethod.append(" ").append(String.join(", ", embedOptions)).append("\n"); + visitMethod.append(" );\n"); } + visitMethod.append(" }\n"); } - visitor.append(" return super.visit").append(methodSuffix).append("(elem, ctx);\n"); - visitor.append(" }\n"); - visitor.append("\n"); } - visitor.append(" }"); - return visitor.toString(); + visitMethod.append(" return super.visit").append(methodSuffix).append("(elem, ctx);\n"); + visitMethod.append(" }\n"); + visitMethod.append("\n"); + return visitMethod.toString(); } private boolean simplifyBooleans(JCTree.JCMethodDecl template) { diff --git a/src/test/java/org/openrewrite/java/template/RefasterTemplateProcessorTest.java b/src/test/java/org/openrewrite/java/template/RefasterTemplateProcessorTest.java index b4d48199..c659e2d3 100644 --- a/src/test/java/org/openrewrite/java/template/RefasterTemplateProcessorTest.java +++ b/src/test/java/org/openrewrite/java/template/RefasterTemplateProcessorTest.java @@ -51,6 +51,7 @@ class RefasterTemplateProcessorTest { "NewBufferedWriter", "UseStringIsEmpty", "SimplifyBooleans", + "TwoVisitMethods", "FindListAdd", }) void generateRecipe(String recipeName) { diff --git a/src/test/resources/refaster/ParametersRecipes.java b/src/test/resources/refaster/ParametersRecipes.java index a08f7a01..34f3b184 100644 --- a/src/test/resources/refaster/ParametersRecipes.java +++ b/src/test/resources/refaster/ParametersRecipes.java @@ -153,17 +153,17 @@ public TreeVisitor getVisitor() { @Override public J visitBinary(J.Binary elem, ExecutionContext ctx) { JavaTemplate.Matcher matcher; - if ((matcher = before1.matcher(getCursor())).find()) { + if ((matcher = before2.matcher(getCursor())).find()) { return embed( - after.apply(getCursor(), elem.getCoordinates().replace(), matcher.parameter(0), matcher.parameter(1)), + after.apply(getCursor(), elem.getCoordinates().replace(), matcher.parameter(1), matcher.parameter(0)), getCursor(), ctx, SHORTEN_NAMES, SIMPLIFY_BOOLEANS ); } - if ((matcher = before2.matcher(getCursor())).find()) { + if ((matcher = before1.matcher(getCursor())).find()) { return embed( - after.apply(getCursor(), elem.getCoordinates().replace(), matcher.parameter(1), matcher.parameter(0)), + after.apply(getCursor(), elem.getCoordinates().replace(), matcher.parameter(0), matcher.parameter(1)), getCursor(), ctx, SHORTEN_NAMES, SIMPLIFY_BOOLEANS diff --git a/src/test/resources/refaster/ShouldAddImportsRecipes.java b/src/test/resources/refaster/ShouldAddImportsRecipes.java index e17d7b2d..99bc90b1 100644 --- a/src/test/resources/refaster/ShouldAddImportsRecipes.java +++ b/src/test/resources/refaster/ShouldAddImportsRecipes.java @@ -159,10 +159,9 @@ public TreeVisitor getVisitor() { .build(); @Override - public J visitMethodInvocation(J.MethodInvocation elem, ExecutionContext ctx) { + public J visitBinary(J.Binary elem, ExecutionContext ctx) { JavaTemplate.Matcher matcher; - if ((matcher = equals.matcher(getCursor())).find()) { - maybeRemoveImport("java.util.Objects"); + if ((matcher = compareZero.matcher(getCursor())).find()) { return embed( isis.apply(getCursor(), elem.getCoordinates().replace(), matcher.parameter(0), matcher.parameter(1)), getCursor(), @@ -170,7 +169,14 @@ public J visitMethodInvocation(J.MethodInvocation elem, ExecutionContext ctx) { SHORTEN_NAMES, SIMPLIFY_BOOLEANS ); } - if ((matcher = compareZero.matcher(getCursor())).find()) { + return super.visitBinary(elem, ctx); + } + + @Override + public J visitMethodInvocation(J.MethodInvocation elem, ExecutionContext ctx) { + JavaTemplate.Matcher matcher; + if ((matcher = equals.matcher(getCursor())).find()) { + maybeRemoveImport("java.util.Objects"); return embed( isis.apply(getCursor(), elem.getCoordinates().replace(), matcher.parameter(0), matcher.parameter(1)), getCursor(), diff --git a/src/test/resources/refaster/TwoVisitMethods.java b/src/test/resources/refaster/TwoVisitMethods.java new file mode 100644 index 00000000..ccf81f0c --- /dev/null +++ b/src/test/resources/refaster/TwoVisitMethods.java @@ -0,0 +1,38 @@ +/* + * 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 foo; + +import com.google.errorprone.refaster.annotation.AfterTemplate; +import com.google.errorprone.refaster.annotation.BeforeTemplate; + +public class TwoVisitMethods { + + @BeforeTemplate + boolean lengthIsZero(String s) { + return s.length() == 0; + } + + @BeforeTemplate + boolean equalsEmptyString(String s) { + return s.equals(""); + } + + @AfterTemplate + boolean isEmpty(String s) { + return s.isEmpty(); + } + +} diff --git a/src/test/resources/refaster/TwoVisitMethodsRecipe.java b/src/test/resources/refaster/TwoVisitMethodsRecipe.java new file mode 100644 index 00000000..e5032f9d --- /dev/null +++ b/src/test/resources/refaster/TwoVisitMethodsRecipe.java @@ -0,0 +1,110 @@ +/* + * 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 foo; + +import org.jspecify.annotations.NullMarked; +import org.openrewrite.ExecutionContext; +import org.openrewrite.Preconditions; +import org.openrewrite.Recipe; +import org.openrewrite.TreeVisitor; +import org.openrewrite.java.JavaParser; +import org.openrewrite.java.JavaTemplate; +import org.openrewrite.java.JavaVisitor; +import org.openrewrite.java.search.*; +import org.openrewrite.java.template.Primitive; +import org.openrewrite.java.template.function.*; +import org.openrewrite.java.template.internal.AbstractRefasterJavaVisitor; +import org.openrewrite.java.tree.*; + +import javax.annotation.Generated; +import java.util.*; + +import static org.openrewrite.java.template.internal.AbstractRefasterJavaVisitor.EmbeddingOption.*; + +/** + * OpenRewrite recipe created for Refaster template {@code TwoVisitMethods}. + */ +@SuppressWarnings("all") +@NullMarked +@Generated("org.openrewrite.java.template.processor.RefasterTemplateProcessor") +public class TwoVisitMethodsRecipe extends Recipe { + + /** + * Instantiates a new instance. + */ + public TwoVisitMethodsRecipe() {} + + @Override + public String getDisplayName() { + return "Refaster template `TwoVisitMethods`"; + } + + @Override + public String getDescription() { + return "Recipe created for the following Refaster template:\n```java\npublic class TwoVisitMethods {\n \n @BeforeTemplate()\n boolean lengthIsZero(String s) {\n return s.length() == 0;\n }\n \n @BeforeTemplate()\n boolean equalsEmptyString(String s) {\n return s.equals(\"\");\n }\n \n @AfterTemplate()\n boolean isEmpty(String s) {\n return s.isEmpty();\n }\n}\n```\n."; + } + + @Override + public TreeVisitor getVisitor() { + JavaVisitor javaVisitor = new AbstractRefasterJavaVisitor() { + final JavaTemplate lengthIsZero = JavaTemplate + .builder("#{s:any(java.lang.String)}.length() == 0") + .build(); + final JavaTemplate equalsEmptyString = JavaTemplate + .builder("#{s:any(java.lang.String)}.equals(\"\")") + .build(); + final JavaTemplate isEmpty = JavaTemplate + .builder("#{s:any(java.lang.String)}.isEmpty()") + .build(); + + @Override + public J visitBinary(J.Binary elem, ExecutionContext ctx) { + JavaTemplate.Matcher matcher; + if ((matcher = lengthIsZero.matcher(getCursor())).find()) { + return embed( + isEmpty.apply(getCursor(), elem.getCoordinates().replace(), matcher.parameter(0)), + getCursor(), + ctx, + SHORTEN_NAMES, SIMPLIFY_BOOLEANS + ); + } + return super.visitBinary(elem, ctx); + } + + @Override + public J visitMethodInvocation(J.MethodInvocation elem, ExecutionContext ctx) { + JavaTemplate.Matcher matcher; + if ((matcher = equalsEmptyString.matcher(getCursor())).find()) { + return embed( + isEmpty.apply(getCursor(), elem.getCoordinates().replace(), matcher.parameter(0)), + getCursor(), + ctx, + SHORTEN_NAMES, SIMPLIFY_BOOLEANS + ); + } + return super.visitMethodInvocation(elem, ctx); + } + + }; + return Preconditions.check( + Preconditions.or( + new UsesMethod<>("java.lang.String length(..)", true), + new UsesMethod<>("java.lang.String equals(..)", true) + ), + javaVisitor + ); + } +}