Initial commit
This commit is contained in:
186
skills/recipe-writer/examples/example-say-hello-recipe.java
Normal file
186
skills/recipe-writer/examples/example-say-hello-recipe.java
Normal file
@@ -0,0 +1,186 @@
|
||||
package com.yourorg;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Value;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
import org.openrewrite.*;
|
||||
import org.openrewrite.java.JavaIsoVisitor;
|
||||
import org.openrewrite.java.JavaTemplate;
|
||||
import org.openrewrite.java.tree.J;
|
||||
|
||||
/**
|
||||
* A simple recipe that adds a hello() method to a specified class.
|
||||
*
|
||||
* This example demonstrates:
|
||||
* - Basic recipe structure with @Value and @EqualsAndHashCode
|
||||
* - Using @Option for configurable parameters
|
||||
* - Using JavaTemplate for code generation
|
||||
* - Checking preconditions before making changes
|
||||
* - Following the "do no harm" principle
|
||||
*
|
||||
* Based on the official OpenRewrite tutorial:
|
||||
* https://docs.openrewrite.org/authoring-recipes/writing-a-java-refactoring-recipe
|
||||
*/
|
||||
@Value
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class SayHelloRecipe extends Recipe {
|
||||
|
||||
@Option(
|
||||
displayName = "Fully Qualified Class Name",
|
||||
description = "A fully qualified class name indicating which class to add a hello() method to.",
|
||||
example = "com.yourorg.FooBar"
|
||||
)
|
||||
@NonNull
|
||||
String fullyQualifiedClassName;
|
||||
|
||||
@JsonCreator
|
||||
public SayHelloRecipe(@NonNull @JsonProperty("fullyQualifiedClassName") String fullyQualifiedClassName) {
|
||||
this.fullyQualifiedClassName = fullyQualifiedClassName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return "Say 'Hello'";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Adds a \"hello\" method to the specified class.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public TreeVisitor<?, ExecutionContext> getVisitor() {
|
||||
// Always return a new instance - never cache visitors
|
||||
return new JavaIsoVisitor<ExecutionContext>() {
|
||||
|
||||
@Override
|
||||
public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) {
|
||||
// Step 1: Traverse the subtree first
|
||||
J.ClassDeclaration cd = super.visitClassDeclaration(classDecl, ctx);
|
||||
|
||||
// Step 2: Check if this is the class we're looking for
|
||||
// Do no harm: return unchanged if this isn't the target class
|
||||
if (cd.getType() == null || !cd.getType().getFullyQualifiedName().equals(fullyQualifiedClassName)) {
|
||||
return cd;
|
||||
}
|
||||
|
||||
// Step 3: Check if the class already has a hello() method
|
||||
// Do no harm: don't add if it already exists
|
||||
boolean helloMethodExists = cd.getBody().getStatements().stream()
|
||||
.filter(statement -> statement instanceof J.MethodDeclaration)
|
||||
.map(J.MethodDeclaration.class::cast)
|
||||
.anyMatch(methodDeclaration -> "hello".equals(methodDeclaration.getName().getSimpleName()));
|
||||
|
||||
if (helloMethodExists) {
|
||||
return cd;
|
||||
}
|
||||
|
||||
// Step 4: Add the hello() method using JavaTemplate
|
||||
// The template uses #{} for parameter substitution
|
||||
J.Block body = JavaTemplate.apply(
|
||||
"public String hello() { return \"Hello from #{}!\"; }",
|
||||
new Cursor(getCursor(), cd.getBody()),
|
||||
cd.getBody().getCoordinates().lastStatement(),
|
||||
fullyQualifiedClassName
|
||||
);
|
||||
|
||||
return cd.withBody(body);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// TEST CLASS
|
||||
// ============================================================
|
||||
|
||||
/*
|
||||
package com.yourorg;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openrewrite.DocumentExample;
|
||||
import org.openrewrite.test.RecipeSpec;
|
||||
import org.openrewrite.test.RewriteTest;
|
||||
|
||||
import static org.openrewrite.java.Assertions.java;
|
||||
|
||||
class SayHelloRecipeTest implements RewriteTest {
|
||||
|
||||
@Override
|
||||
public void defaults(RecipeSpec spec) {
|
||||
spec.recipe(new SayHelloRecipe("com.yourorg.FooBar"));
|
||||
}
|
||||
|
||||
@DocumentExample
|
||||
@Test
|
||||
void addsHelloToFooBar() {
|
||||
rewriteRun(
|
||||
java(
|
||||
"""
|
||||
package com.yourorg;
|
||||
|
||||
class FooBar {
|
||||
}
|
||||
""",
|
||||
"""
|
||||
package com.yourorg;
|
||||
|
||||
class FooBar {
|
||||
public String hello() {
|
||||
return "Hello from com.yourorg.FooBar!";
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void doesNotChangeExistingHello() {
|
||||
rewriteRun(
|
||||
java(
|
||||
"""
|
||||
package com.yourorg;
|
||||
|
||||
class FooBar {
|
||||
public String hello() { return ""; }
|
||||
}
|
||||
"""
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void doesNotChangeOtherClasses() {
|
||||
rewriteRun(
|
||||
java(
|
||||
"""
|
||||
package com.yourorg;
|
||||
|
||||
class OtherClass {
|
||||
}
|
||||
"""
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// ============================================================
|
||||
// YAML USAGE
|
||||
// ============================================================
|
||||
|
||||
/*
|
||||
Save this in src/main/resources/META-INF/rewrite/say-hello.yml:
|
||||
|
||||
---
|
||||
type: specs.openrewrite.org/v1beta/recipe
|
||||
name: com.yourorg.SayHelloToFooBar
|
||||
displayName: Say Hello to FooBar
|
||||
description: Adds a hello() method to the FooBar class.
|
||||
recipeList:
|
||||
- com.yourorg.SayHelloRecipe:
|
||||
fullyQualifiedClassName: com.yourorg.FooBar
|
||||
*/
|
||||
Reference in New Issue
Block a user