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 9a928027..eea35827 100644 --- a/src/main/java/org/openrewrite/java/template/processor/RefasterTemplateProcessor.java +++ b/src/main/java/org/openrewrite/java/template/processor/RefasterTemplateProcessor.java @@ -444,7 +444,8 @@ private String recipeDescriptor(JCTree.JCClassDecl classDecl, String defaultDisp // Extract from the RecipeDescriptor annotation for (JCTree.JCAnnotation annotation : classDecl.getModifiers().getAnnotations()) { - if (annotation.type.toString().equals("org.openrewrite.java.template.RecipeDescriptor")) { + String annotationFqn = annotation.type.toString(); + if ("org.openrewrite.java.template.RecipeDescriptor".equals(annotationFqn)) { for (JCTree.JCExpression argExpr : annotation.getArguments()) { JCTree.JCAssign arg = (JCTree.JCAssign) argExpr; switch (arg.lhs.toString()) { @@ -466,6 +467,10 @@ private String recipeDescriptor(JCTree.JCClassDecl classDecl, String defaultDisp } } break; + } else if ("tech.picnic.errorprone.refaster.annotation.OnlineDocumentation".equals(annotationFqn)) { + if (annotation.getArguments().isEmpty()) { + description += " [Source](https://error-prone.picnic.tech/refasterrules/" + classDecl.name.toString() + ")."; + } } } diff --git a/src/test/java/org/openrewrite/java/template/RefasterTemplateProcessorTest.java b/src/test/java/org/openrewrite/java/template/RefasterTemplateProcessorTest.java index 18f584ab..b1361cbe 100644 --- a/src/test/java/org/openrewrite/java/template/RefasterTemplateProcessorTest.java +++ b/src/test/java/org/openrewrite/java/template/RefasterTemplateProcessorTest.java @@ -81,6 +81,7 @@ void skipRecipeGeneration(String recipeName) { "Generics", "Matching", "MultipleDereferences", + "PicnicRules", "ShouldAddImports", "ShouldSupportNestedClasses", "SimplifyTernary", diff --git a/src/test/java/tech/picnic/errorprone/refaster/annotation/OnlineDocumentation.java b/src/test/java/tech/picnic/errorprone/refaster/annotation/OnlineDocumentation.java new file mode 100644 index 00000000..46e74382 --- /dev/null +++ b/src/test/java/tech/picnic/errorprone/refaster/annotation/OnlineDocumentation.java @@ -0,0 +1,28 @@ +/* + * 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 tech.picnic.errorprone.refaster.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.SOURCE) +public @interface OnlineDocumentation { + String value() default + "https://error-prone.picnic.tech/refasterrules/"; +} diff --git a/src/test/resources/refaster/PicnicRules.java b/src/test/resources/refaster/PicnicRules.java new file mode 100644 index 00000000..0e8b50ff --- /dev/null +++ b/src/test/resources/refaster/PicnicRules.java @@ -0,0 +1,35 @@ +/* + * 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; +import tech.picnic.errorprone.refaster.annotation.OnlineDocumentation; + +@OnlineDocumentation +public class PicnicRules { + public static class NestedRule { + @BeforeTemplate + String before(String s, String s1, String s2) { + return s.replaceAll(s1, s2); + } + + @AfterTemplate + String after(String s, String s1, String s2) { + return s != null ? s.replaceAll(s1, s2) : s; + } + } +} diff --git a/src/test/resources/refaster/PicnicRulesRecipes.java b/src/test/resources/refaster/PicnicRulesRecipes.java new file mode 100644 index 00000000..4883d1eb --- /dev/null +++ b/src/test/resources/refaster/PicnicRulesRecipes.java @@ -0,0 +1,117 @@ +/* + * 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.openrewrite.ExecutionContext; +import org.openrewrite.Preconditions; +import org.openrewrite.Recipe; +import org.openrewrite.TreeVisitor; +import org.openrewrite.internal.lang.NonNullApi; +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 java.util.*; + +import static org.openrewrite.java.template.internal.AbstractRefasterJavaVisitor.EmbeddingOption.*; + +/** + * OpenRewrite recipes created for Refaster template {@code foo.PicnicRules}. + */ +@SuppressWarnings("all") +public class PicnicRulesRecipes extends Recipe { + /** + * Instantiates a new instance. + */ + public PicnicRulesRecipes() {} + + @Override + public String getDisplayName() { + return "`PicnicRules` Refaster recipes"; + } + + @Override + public String getDescription() { + return "Refaster template recipes for `foo.PicnicRules`. [Source](https://error-prone.picnic.tech/refasterrules/PicnicRules)."; + } + + @Override + public List getRecipeList() { + return Arrays.asList( + new NestedRuleRecipe() + ); + } + + /** + * OpenRewrite recipe created for Refaster template {@code PicnicRules.NestedRule}. + */ + @SuppressWarnings("all") + @NonNullApi + public static class NestedRuleRecipe extends Recipe { + + /** + * Instantiates a new instance. + */ + public NestedRuleRecipe() {} + + @Override + public String getDisplayName() { + return "Refaster template `PicnicRules.NestedRule`"; + } + + @Override + public String getDescription() { + return "Recipe created for the following Refaster template:\n```java\npublic static class NestedRule {\n \n @BeforeTemplate()\n String before(String s, String s1, String s2) {\n return s.replaceAll(s1, s2);\n }\n \n @AfterTemplate()\n String after(String s, String s1, String s2) {\n return s != null ? s.replaceAll(s1, s2) : s;\n }\n}\n```\n."; + } + + @Override + public TreeVisitor getVisitor() { + JavaVisitor javaVisitor = new AbstractRefasterJavaVisitor() { + final JavaTemplate before = JavaTemplate + .builder("#{s:any(java.lang.String)}.replaceAll(#{s1:any(java.lang.String)}, #{s2:any(java.lang.String)})") + .build(); + final JavaTemplate after = JavaTemplate + .builder("#{s:any(java.lang.String)} != null ? #{s}.replaceAll(#{s1:any(java.lang.String)}, #{s2:any(java.lang.String)}) : #{s}") + .build(); + + @Override + public J visitMethodInvocation(J.MethodInvocation elem, ExecutionContext ctx) { + JavaTemplate.Matcher matcher; + if ((matcher = before.matcher(getCursor())).find()) { + return embed( + after.apply(getCursor(), elem.getCoordinates().replace(), matcher.parameter(0), matcher.parameter(1), matcher.parameter(2)), + getCursor(), + ctx, + SHORTEN_NAMES, SIMPLIFY_BOOLEANS + ); + } + return super.visitMethodInvocation(elem, ctx); + } + + }; + return Preconditions.check( + new UsesMethod<>("java.lang.String replaceAll(..)"), + javaVisitor + ); + } + } + +}