Small tips for implementing recipes
Small tips for implementing recipes
Let’s zoom in on a few practices to help you, or your users
openrewrite (5 Parts Series)
- 1 OpenRewrite: Refactoring as code
- 2 Technique Avancée d'OpenRewrite : Utiliser les messages pour implémenter des logiques complexes
- 3 Technique Avancée d'OpenRewrite : Orchestrer des refactorings complexes avec les `ScanningRecipe`
- 4 Générer une vue aérienne de votre projet avec OpenRewrite
- 5 Petits tips pour implémenter des recettes
I’ve previously talked about my Project Graph Generator here. I wrote a dedicated post on using Scanning Recipes, and another on its use cases.
It turns out I was invited to discuss the topic on the Code Remix Weekly: Unlocking insights with your project LST graph.
During the show, we discussed other use cases for scanning recipes, as well as some neat tricks I picked up along the way.
Starting with the good news, 🎉 V1.0.8 🐲 is out and includes a recipe to count the number of invocations for each of your public methods we talked about on the Code Remix Weekly.
For the rest of this post, I’ll share a few tricks and good practices I learned while developing recipes.
Provide default values
Building a configurable recipe? Try to provide default values wherever possible. Users should be able to run your recipes without asking themselves a ton of questions—minimize their workload.
Accessing the scanned project
Whenever you handle a org.openrewrite.Tree instance—which is almost always the case when dealing with Java—you can use Tree#getMarkers#findFirst or Tree#getMarkers#findAll to retrieve the JavaProject currently being analyzed. This is particularly useful for finding the <groupId, artifactId, version> of the project.
So you would write:
javaSourceFile.getMarkers().findFirst(JavaProject.class).ifPresent(jp -> { (1)
if (jp.getPublication() != null) {
var publication = jp.getPublication() ;
System.out.println("Your projet is <%s:%s:%>. Impressive, isn't it ?".formatted(
publication.getGroupId(),
publication.getArtifactId(),
publication.getVersion()
))
}
});
| 1 | See that findFirst takes an argument class for the marker that you want to retrieve. Which means the result is typed. |
Finding types used in a source file
The org.openrewrite.java.tree.JavaSourceFile#getTypesInUse method gives you access to all the types used within a given source file.
Furthermore, the JavaVisitor class features a visitType method, which is invoked every time a type is used in your code (class declarations, method declarations, method invocations, property accesses, variable declarations…).
Please note, that it’s a helpful performance optimization for preconditions, as it allows you to skip traversal when evaluating whether a type is used. Thats what’s used under the hood by the UsesType visitor.
@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return new Preconditions.Check(
new UsesType<>("bar.foo.fighter.dave.Grohl", true),
new ToStringFormattedVisitor());
}
Knowing your position in the code
You can pinpoint exactly where your recipe is currently processing the code. To do this, use the SourcePositionService:
Span span = service(SourcePositionService.class).positionOf(getCursor());
Sytem.out.println("Your problem lies here : start line %s, end line %s"
.formatted(span.getStartLine(), span.getEndLine()));
There are obviously other methods available. Go dig around.
Accessing the DataTable
If you have an ExecutionContext on hand, you can always access any data tables currently in use.
Stream<@NotNull Node> nodes = DataTableExecutionContextView
.view(executionContext)
.getDataTableStore()
.getRows(NodesReport.class);
Be aware that datatables are completed during the graph traversal so they might not be here when you invoke that method.
If you want to be sure the recipe has finished its job, you certainly want to invoke this in your onComplete(ExecutionContext ctx) method implementation.
Nullable or not
The OpenRewrite maintainers use JSpecify, meaning zero surprises. You always know whether your API calls are null-safe or not. And that’s a joy.
Wrapping up
Implement the recipes that solve your problems if they don’t exist yet, and share them if you think others might find them useful.
Give the Project Graph Generator a try. Let me know what you think, and help me improve it!
Illustration par Sam Truong
Jérôme Tama
Techlead/Architecte/Compagnon du devoir