Skip to content

Commit

Permalink
Change HasSourcePath from a Visitor into a Recipe for use in yaml pre…
Browse files Browse the repository at this point in the history
…conditions (#3749)

* ChangeText needs a relativePath to be of use

Or so I'm told.

* Replace ChangeText `relativePath` with hint about preconditions

* Turn HasSourcePath into a Recipe

* Apply suggestions from code review
  • Loading branch information
timtebeek authored Dec 1, 2023
1 parent 9884314 commit 777a0f3
Show file tree
Hide file tree
Showing 11 changed files with 74 additions and 99 deletions.
73 changes: 50 additions & 23 deletions rewrite-core/src/main/java/org/openrewrite/HasSourcePath.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,17 @@
import java.nio.file.PathMatcher;
import java.nio.file.Paths;

public class HasSourcePath<P> extends TreeVisitor<Tree, P> {
public class HasSourcePath extends Recipe {
@Option(displayName = "Syntax",
description = "Either `glob` or `regex`. Defaults to `glob`.",
example = "glob",
required = false)
private final String syntax;
@Option(displayName = "File pattern",
description = "A glob or regex pattern to match against the source path of a source file.",
example = "foo/**/bar/*.txt",
required = false)
@Nullable
private final String filePattern;

public HasSourcePath(@Nullable String filePattern) {
Expand All @@ -35,34 +44,52 @@ public HasSourcePath(@Nullable String filePattern) {
* @param syntax one of "glob" or "regex".
* @param filePattern the file pattern.
*/
public HasSourcePath(String syntax, @Nullable String filePattern) {
this.syntax = syntax;
public HasSourcePath(@Nullable String syntax, @Nullable String filePattern) {
this.syntax = syntax == null ? "glob" : syntax;
this.filePattern = filePattern;
}

@Nullable
@Override
public Tree preVisit(Tree tree, P p) {
stopAfterPreVisit();
if (StringUtils.isBlank(filePattern)) {
return SearchResult.found(tree, "has file");
}
public String getDisplayName() {
return "Has source path";
}

if (tree instanceof SourceFile) {
SourceFile sourceFile = (SourceFile) tree;
Path sourcePath;
if ("glob".equals(syntax) && filePattern.startsWith("**")) {
sourcePath = Paths.get(".").resolve(sourceFile.getSourcePath().normalize());
} else {
sourcePath = sourceFile.getSourcePath().normalize();
}
@Override
public String getDescription() {
return "Matches a source file based on its path, for use as precondition. " +
"The path can be matched using either glob or regex syntax. " +
"If no syntax is specified, glob syntax is used by default. " +
"If no file pattern is specified, all source files are matched. " +
"The path is relative to the root of the project.";
}

PathMatcher pathMatcher = sourcePath.getFileSystem().getPathMatcher(syntax + ":" + filePattern);
if (pathMatcher.matches(sourcePath)) {
return SearchResult.found(sourceFile,"has file");
}
}
@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return new TreeVisitor<Tree, ExecutionContext>() {
@Override
public Tree preVisit(Tree tree, ExecutionContext p) {
stopAfterPreVisit();
if (StringUtils.isBlank(filePattern)) {
return SearchResult.found(tree, "has file");
}

if (tree instanceof SourceFile) {
SourceFile sourceFile = (SourceFile) tree;
Path sourcePath;
if ("glob".equals(syntax) && filePattern.startsWith("**")) {
sourcePath = Paths.get(".").resolve(sourceFile.getSourcePath().normalize());
} else {
sourcePath = sourceFile.getSourcePath().normalize();
}

return tree;
PathMatcher pathMatcher = sourcePath.getFileSystem().getPathMatcher(syntax + ":" + filePattern);
if (pathMatcher.matches(sourcePath)) {
return SearchResult.found(sourceFile, "has file");
}
}

return tree;
}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
@Value
@EqualsAndHashCode(callSuper = false)
public class ChangeText extends Recipe {

@Option(displayName = "Text after change",
description = "The text file will have only this text after the change.",
example = "Some text.")
Expand All @@ -48,14 +47,15 @@ public String getDisplayName() {

@Override
public String getDescription() {
return "Completely replaces the contents of the text file with other text.";
return "Completely replaces the contents of the text file with other text. " +
"Use together with a `HasSourcePath` precondition to limit which files are changed.";
}

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return new PlainTextVisitor<ExecutionContext>() {
@Override
public PlainText visitText(PlainText text, ExecutionContext executionContext) {
public PlainText visitText(PlainText text, ExecutionContext ctx) {
return text
.withSnippets(emptyList())
.withText(toText);
Expand Down
11 changes: 6 additions & 5 deletions rewrite-core/src/main/java/org/openrewrite/text/Find.java
Original file line number Diff line number Diff line change
Expand Up @@ -105,13 +105,13 @@ public Tree visit(@Nullable Tree tree, ExecutionContext ctx) {
searchStr = Pattern.quote(searchStr);
}
int patternOptions = 0;
if(!Boolean.TRUE.equals(caseSensitive)) {
if (!Boolean.TRUE.equals(caseSensitive)) {
patternOptions |= Pattern.CASE_INSENSITIVE;
}
if(Boolean.TRUE.equals(multiline)) {
if (Boolean.TRUE.equals(multiline)) {
patternOptions |= Pattern.MULTILINE;
}
if(Boolean.TRUE.equals(dotAll)) {
if (Boolean.TRUE.equals(dotAll)) {
patternOptions |= Pattern.DOTALL;
}
Pattern pattern = Pattern.compile(searchStr, patternOptions);
Expand All @@ -134,10 +134,11 @@ public Tree visit(@Nullable Tree tree, ExecutionContext ctx) {
}
};
//noinspection DuplicatedCode
if(filePattern != null) {
if (filePattern != null) {
//noinspection unchecked
TreeVisitor<?, ExecutionContext> check = Preconditions.or(Arrays.stream(filePattern.split(";"))
.map(HasSourcePath<ExecutionContext>::new)
.map(HasSourcePath::new)
.map(Recipe::getVisitor)
.toArray(TreeVisitor[]::new));

visitor = Preconditions.check(check, visitor);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,8 @@ public Tree visit(@Nullable Tree tree, ExecutionContext ctx) {
if(filePattern != null) {
//noinspection unchecked
TreeVisitor<?, ExecutionContext> check = Preconditions.or(Arrays.stream(filePattern.split(";"))
.map(HasSourcePath<ExecutionContext>::new)
.map(HasSourcePath::new)
.map(Recipe::getVisitor)
.toArray(TreeVisitor[]::new));

visitor = Preconditions.check(check, visitor);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public String getDescription() {
"* Dot all - Allows `.` to match line terminators.",
valid = {"Case-sensitive", "Multiline", "Dot all"},
required = false)
@Nullable
@Nullable
Set<String> regexOptions;

@Option(displayName = "File pattern",
Expand All @@ -86,7 +86,7 @@ public TreeVisitor<?, ExecutionContext> getVisitor() {
Boolean caseSensitive;
Boolean multiline;
Boolean dotAll;
if(regexOptions != null) {
if (regexOptions != null) {
Set<String> lowerCaseOptions = regexOptions.stream()
.map(String::toLowerCase)
.collect(toSet());
Expand All @@ -112,13 +112,13 @@ public Tree visit(@Nullable Tree tree, ExecutionContext ctx) {
searchStr = Pattern.quote(searchStr);
}
int patternOptions = 0;
if(!Boolean.TRUE.equals(caseSensitive)) {
if (!Boolean.TRUE.equals(caseSensitive)) {
patternOptions |= Pattern.CASE_INSENSITIVE;
}
if(Boolean.TRUE.equals(multiline)) {
if (Boolean.TRUE.equals(multiline)) {
patternOptions |= Pattern.MULTILINE;
}
if(Boolean.TRUE.equals(dotAll)) {
if (Boolean.TRUE.equals(dotAll)) {
patternOptions |= Pattern.DOTALL;
}
Pattern pattern = Pattern.compile(searchStr, patternOptions);
Expand All @@ -141,10 +141,11 @@ public Tree visit(@Nullable Tree tree, ExecutionContext ctx) {
}
};
//noinspection DuplicatedCode
if(filePattern != null) {
if (filePattern != null) {
//noinspection unchecked
TreeVisitor<?, ExecutionContext> check = Preconditions.or(Arrays.stream(filePattern.split(";"))
.map(HasSourcePath<ExecutionContext>::new)
.map(HasSourcePath::new)
.map(Recipe::getVisitor)
.toArray(TreeVisitor[]::new));

visitor = Preconditions.check(check, visitor);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
import static org.assertj.core.api.Assertions.assertThat;

class RecipeBasicsTest {

@Test
void cloneRecipe() throws JsonMappingException {
ChangeText ct = new ChangeText("hi");
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -589,7 +589,7 @@ public String getDisplayName() {
@Override
protected TreeVisitor<?, ExecutionContext> getSingleSourceApplicableTest() {
if (fileMatcher != null) {
return new HasSourcePath<>(fileMatcher);
return new HasSourcePath(fileMatcher).getVisitor();
} else {
return null;
}
Expand Down Expand Up @@ -633,7 +633,7 @@ public String getDisplayName() {
@Override
protected TreeVisitor<?, ExecutionContext> getSingleSourceApplicableTest() {
if (fileMatcher != null) {
return new HasSourcePath<>(fileMatcher);
return new HasSourcePath(fileMatcher).getVisitor();
} else {
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ public String getDescription() {

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return Preconditions.check(new HasSourcePath<>(syntax, filePattern),
return Preconditions.check(new HasSourcePath(syntax, filePattern),
new ChangeText(toText).getVisitor());
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public String getDescription() {

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return Preconditions.check(fileMatcher != null ? new HasSourcePath<>(fileMatcher) : new HasSourcePath<>(""), new XmlIsoVisitor<ExecutionContext>() {
return Preconditions.check(fileMatcher != null ? new HasSourcePath(fileMatcher) : new HasSourcePath(""), new XmlIsoVisitor<ExecutionContext>() {
private final XPathMatcher xPathMatcher = new XPathMatcher(xPath);

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ void insertCopyValueAndRemoveSource() {
void changeOnlyMatchingFile() {
rewriteRun(
spec -> spec.recipe(
toRecipe(() -> Preconditions.check(new HasSourcePath<>("**/a.yml"), new YamlIsoVisitor<>() {
toRecipe(() -> Preconditions.check(new HasSourcePath("**/a.yml"), new YamlIsoVisitor<>() {
@Override
public Yaml.Documents visitDocuments(Yaml.Documents documents, ExecutionContext executionContext) {
doAfterVisit(new CopyValue(".source", ".destination").getVisitor());
Expand Down

0 comments on commit 777a0f3

Please sign in to comment.