When creating new recipes, you may find it desirable to examine multiple source files, potentially of different types, to make key decisions in your visitor. For example, you may want to look for a particular condition to be present in a Maven POM file and, if that condition is met, alter an application property in a YAML file.
In situations like that, you should create a ScanningRecipe. In there, you'll then make an accumulator which visitors can read from and write to as needed. For a more thorough explanation of scanning recipes, please see What is a ScanningRecipe?
Below you can find an example of a ScanningRecipe that does what we described earlier:
importorg.openrewrite.*;importorg.openrewrite.internal.lang.Nullable;importorg.openrewrite.yaml.YamlVisitor;publicclassMavenYamlRecipeextendsScanningRecipe<MavenYamlRecipe.Accumulator> { @OverridepublicStringgetDisplayName() {return"Something with Maven and something with YAML"; } @OverridepublicStringgetDescription() {return"Something with Maven and something with YAML."; } @OverridepublicAccumulatorgetInitialValue(ExecutionContext ctx) {returnnewAccumulator(); } @OverridepublicTreeVisitor<?,ExecutionContext> getScanner(Accumulator acc) {returnnewTreeVisitor<Tree,ExecutionContext>() { @OverridepublicTreevisit(@NullableTree tree,ExecutionContext executionContext) {if (!(tree instanceof SourceFile)) {return tree; }SourceFile sourceFile = (SourceFile) tree;String sourcePath =PathUtils.separatorsToUnix(sourceFile.getSourcePath().toString());if (sourcePath.equals("pom.xml")) {// it's a maven project// have a xml visitor to check whether "pom.xml" meet some condition, assume it's true hereboolean isPomConditionMet =true;// set it in Accumulatoracc.pomConditionMet= isPomConditionMet; }return tree; } }; } @OverridepublicTreeVisitor<?,ExecutionContext> getVisitor(Accumulator acc) {if (!acc.pomConditionMet) {// if pom condition not met, do nothingreturnTreeVisitor.noop(); }// whatever your YAML visitor does can now respond to pomConditionMetreturnnewYamlVisitor<ExecutionContext>(){// some logic }; }// @DatastaticclassAccumulator {boolean pomConditionMet =false;// something else if needed from scanner }}
To write a unit test that tests multiple source file types, use their respective parsers: