From a8946ac9c7b6cbe0b6ac1c540d5e5cfc4e2d0919 Mon Sep 17 00:00:00 2001 From: Jacob van Lingen Date: Mon, 30 Dec 2024 14:19:43 +0100 Subject: [PATCH] Add Docker Image reference, to be used for gitlab, docker, etc (#4793) * Basic implementation to find image over multiple sources * Formatting * Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Improvement * Improvement * Add TODO removal comments * Add DockerImageReference * Add DockerImageReference * Add DockerImageReference * Introduce AbstractProvider * Improve DockerImageReference * work on it * first impl * Extra yaml test * working example * working example * working example * revert lost * Fix * Support --from and --platform and ignore comments for dockerfile * Merge branch 'main' into introduce-image-reference-and-recipe * Remove files (will be places in rewrite-docker) * Remove files (will be places in rewrite-docker) * Cleanup `findMatches` * Make SourceFileWithReferences `abstract`, so we can define `getReferences` once * Make SourceFileWithReferences `abstract`, so we can define `getReferences` once * Make SourceFileWithReferences `abstract`, so we can define `getReferences` once * Make SourceFileWithReferences `abstract`, so we can define `getReferences` once * Revert `SourceFileWithReferences` as interface * Make matchers static singletons in the provider classes * Update description * Fix tests * Revert implements of File class * Pattern.asPredicate() acts as `find`, not `matches()` --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Tim te Beek --- .../openrewrite/SourceFileWithReferences.java | 24 +++--- .../main/java/org/openrewrite/text/Find.java | 10 ++- .../java/org/openrewrite/text/PlainText.java | 24 ++++-- .../org/openrewrite/text/PlainTextParser.java | 1 + .../java/org/openrewrite/trait/Reference.java | 18 ++++- .../java/org/openrewrite/text/FindTest.java | 35 +++++--- .../java/org/openrewrite/java/ChangeType.java | 2 +- .../openrewrite/java/search/FindTypes.java | 3 +- .../org/openrewrite/java/search/UsesType.java | 2 +- .../properties/trait/PropertiesReference.java | 50 +++++------- .../properties/tree/Properties.java | 17 +--- .../org/openrewrite/protobuf/ProtoParser.java | 1 + .../xml/trait/SpringXmlReference.java | 81 +++++++++---------- .../java/org/openrewrite/xml/tree/Xml.java | 22 +---- .../xml/trait/SpringXmlReferenceTest.java | 2 +- .../trait/YamlApplicationConfigReference.java | 69 ++++++++++++++++ .../openrewrite/yaml/trait/YamlReference.java | 57 +------------ .../java/org/openrewrite/yaml/tree/Yaml.java | 17 +--- .../org.openrewrite.trait.Reference$Provider | 2 +- .../yaml/trait/YamlReferenceTest.java | 15 ++-- 20 files changed, 234 insertions(+), 218 deletions(-) create mode 100644 rewrite-yaml/src/main/java/org/openrewrite/yaml/trait/YamlApplicationConfigReference.java diff --git a/rewrite-core/src/main/java/org/openrewrite/SourceFileWithReferences.java b/rewrite-core/src/main/java/org/openrewrite/SourceFileWithReferences.java index 0a8bb5faad8..bb1584aaabf 100644 --- a/rewrite-core/src/main/java/org/openrewrite/SourceFileWithReferences.java +++ b/rewrite-core/src/main/java/org/openrewrite/SourceFileWithReferences.java @@ -21,6 +21,7 @@ import org.jspecify.annotations.Nullable; import org.openrewrite.trait.Reference; +import java.lang.ref.SoftReference; import java.util.*; @Incubating(since = "8.39.0") @@ -28,31 +29,32 @@ public interface SourceFileWithReferences extends SourceFile { References getReferences(); + default SoftReference build(@Nullable SoftReference<@Nullable References> references) { + References cache = references == null ? null : references.get(); + if (cache == null || cache.getSourceFile() != this) { + return new SoftReference<>(References.build(this)); + } + return references; + } + @RequiredArgsConstructor(access = AccessLevel.PRIVATE) - @Getter class References { + @Getter(AccessLevel.PRIVATE) private final SourceFile sourceFile; + @Getter private final Set references; public Collection findMatches(Reference.Matcher matcher) { - return findMatchesInternal(matcher, null); - } - - public Collection findMatches(Reference.Matcher matcher, Reference.Kind kind) { - return findMatchesInternal(matcher, kind); - } - - private List findMatchesInternal(Reference.Matcher matcher, Reference.@Nullable Kind kind) { List list = new ArrayList<>(); for (Reference ref : references) { - if ((kind == null || ref.getKind() == kind) && ref.matches(matcher) ) { + if (ref.matches(matcher)) { list.add(ref); } } return list; } - public static References build(SourceFile sourceFile) { + private static References build(SourceFile sourceFile) { Set references = new HashSet<>(); ServiceLoader loader = ServiceLoader.load(Reference.Provider.class); loader.forEach(provider -> { diff --git a/rewrite-core/src/main/java/org/openrewrite/text/Find.java b/rewrite-core/src/main/java/org/openrewrite/text/Find.java index eb8fb30c09a..ad25c20266c 100644 --- a/rewrite-core/src/main/java/org/openrewrite/text/Find.java +++ b/rewrite-core/src/main/java/org/openrewrite/text/Find.java @@ -17,6 +17,7 @@ import lombok.EqualsAndHashCode; import lombok.Value; +import org.apache.commons.lang3.BooleanUtils; import org.jspecify.annotations.Nullable; import org.openrewrite.*; import org.openrewrite.binary.Binary; @@ -91,6 +92,12 @@ public String getDescription() { @Nullable String filePattern; + @Option(displayName = "Description", + description = "Add the matched value(s) as description on the search result marker. Default `false`.", + required = false) + @Nullable + Boolean description; + @Override public TreeVisitor getVisitor() { @@ -135,7 +142,8 @@ public Tree visit(@Nullable Tree tree, ExecutionContext ctx) { do { int matchStart = matcher.start(); snippets.add(snippet(rawText.substring(previousEnd, matchStart))); - snippets.add(SearchResult.found(snippet(rawText.substring(matchStart, matcher.end())))); + String text = rawText.substring(matchStart, matcher.end()); + snippets.add(SearchResult.found(snippet(text), BooleanUtils.isTrue(description) ? text : null)); previousEnd = matcher.end(); // For the first match, search backwards diff --git a/rewrite-core/src/main/java/org/openrewrite/text/PlainText.java b/rewrite-core/src/main/java/org/openrewrite/text/PlainText.java index 6ccab32adf8..cf538bca0ec 100644 --- a/rewrite-core/src/main/java/org/openrewrite/text/PlainText.java +++ b/rewrite-core/src/main/java/org/openrewrite/text/PlainText.java @@ -16,14 +16,17 @@ package org.openrewrite.text; import lombok.*; +import lombok.experimental.NonFinal; import org.jspecify.annotations.Nullable; import org.openrewrite.*; import org.openrewrite.marker.Markers; +import java.lang.ref.SoftReference; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.util.List; +import java.util.Objects; import java.util.UUID; import java.util.function.Predicate; @@ -36,7 +39,7 @@ @Value @Builder @AllArgsConstructor -public class PlainText implements SourceFile, Tree { +public class PlainText implements SourceFileWithReferences, Tree { @Builder.Default @With @@ -79,16 +82,27 @@ public SourceFile withCharset(Charset charset) { @Builder.Default String text = ""; + List snippets; + + @Nullable + @NonFinal + @ToString.Exclude + transient SoftReference references; + + @Override + public References getReferences() { + this.references = build(this.references); + return Objects.requireNonNull(this.references.get()); + } + public PlainText withText(String text) { if (!text.equals(this.text)) { return new PlainText(this.id, this.sourcePath, this.markers, this.charsetName, this.charsetBomMarked, - this.fileAttributes, this.checksum, text, this.snippets); + this.fileAttributes, this.checksum, text, this.snippets, this.references); } return this; } - List snippets; - @Override public

boolean isAcceptable(TreeVisitor v, P p) { return v.isAdaptableTo(PlainTextVisitor.class); @@ -123,7 +137,7 @@ public PlainText withSnippets(@Nullable List snippets) { if (this.snippets == snippets) { return this; } - return new PlainText(id, sourcePath, markers, charsetName, charsetBomMarked, fileAttributes, checksum, text, snippets); + return new PlainText(id, sourcePath, markers, charsetName, charsetBomMarked, fileAttributes, checksum, text, snippets, null); } @Value diff --git a/rewrite-core/src/main/java/org/openrewrite/text/PlainTextParser.java b/rewrite-core/src/main/java/org/openrewrite/text/PlainTextParser.java index d7da58e2be6..eb0d6281f0a 100644 --- a/rewrite-core/src/main/java/org/openrewrite/text/PlainTextParser.java +++ b/rewrite-core/src/main/java/org/openrewrite/text/PlainTextParser.java @@ -82,6 +82,7 @@ public Stream parseInputs(Iterable sources, @Nullable Path re input.getFileAttributes(), null, sourceStr, + null, null ); parsingListener.parsed(input, plainText); diff --git a/rewrite-core/src/main/java/org/openrewrite/trait/Reference.java b/rewrite-core/src/main/java/org/openrewrite/trait/Reference.java index df960a7b412..55cf9f63855 100644 --- a/rewrite-core/src/main/java/org/openrewrite/trait/Reference.java +++ b/rewrite-core/src/main/java/org/openrewrite/trait/Reference.java @@ -17,6 +17,7 @@ import org.openrewrite.*; +import java.util.HashSet; import java.util.Set; @Incubating(since = "8.39.0") @@ -24,7 +25,8 @@ public interface Reference extends Trait { enum Kind { TYPE, - PACKAGE + PACKAGE, + IMAGE } Kind getKind(); @@ -57,6 +59,20 @@ interface Provider { boolean isAcceptable(SourceFile sourceFile); } + abstract class AbstractProvider implements Provider { + protected abstract SimpleTraitMatcher getMatcher(); + + @Override + public Set getReferences(SourceFile sourceFile) { + Set references = new HashSet<>(); + getMatcher().asVisitor(reference -> { + references.add(reference); + return reference.getTree(); + }).visit(sourceFile, 0); + return references; + } + } + interface Matcher { boolean matchesReference(Reference value); diff --git a/rewrite-core/src/test/java/org/openrewrite/text/FindTest.java b/rewrite-core/src/test/java/org/openrewrite/text/FindTest.java index b887986efb1..1e628c1b681 100644 --- a/rewrite-core/src/test/java/org/openrewrite/text/FindTest.java +++ b/rewrite-core/src/test/java/org/openrewrite/text/FindTest.java @@ -29,7 +29,7 @@ class FindTest implements RewriteTest { @Test void dataTable() { rewriteRun( - spec -> spec.recipe(new Find("text", null, null, null, null, null)) + spec -> spec.recipe(new Find("text", null, null, null, null, null, null)) .dataTable(TextMatches.Row.class, rows -> { assertThat(rows).hasSize(1); assertThat(rows.get(0).getMatch()).isEqualTo("This is ~~>text."); @@ -53,7 +53,7 @@ void dataTable() { @Test void regex() { rewriteRun( - spec -> spec.recipe(new Find("[T\\s]", true, true, null, null, null)), + spec -> spec.recipe(new Find("[T\\s]", true, true, null, null, null, null)), text( """ This is\ttext. @@ -68,7 +68,7 @@ void regex() { @Test void plainText() { rewriteRun( - spec -> spec.recipe(new Find("\\s", null, null, null, null, null)), + spec -> spec.recipe(new Find("\\s", null, null, null, null, null, null)), text( """ This i\\s text. @@ -83,7 +83,7 @@ void plainText() { @Test void caseInsensitive() { rewriteRun( - spec -> spec.recipe(new Find("text", null, null, null, null, "**/foo/**;**/baz/**")), + spec -> spec.recipe(new Find("text", null, null, null, null, "**/foo/**;**/baz/**", null)), dir("foo", text( """ @@ -115,7 +115,7 @@ void caseInsensitive() { @Test void regexBasicMultiLine() { rewriteRun( - spec -> spec.recipe(new Find("[T\\s]", true, true, true, null, null)), + spec -> spec.recipe(new Find("[T\\s]", true, true, true, null, null, null)), text( """ This is\ttext. @@ -132,7 +132,7 @@ void regexBasicMultiLine() { @Test void regexWithoutMultilineAndDotall() { rewriteRun( - spec -> spec.recipe(new Find("^This.*below\\.$", true, true, false, false, null)), + spec -> spec.recipe(new Find("^This.*below\\.$", true, true, false, false, null, null)), text( """ This is text. @@ -148,7 +148,7 @@ void regexWithoutMultilineAndDotall() { @Test void regexMatchingWhitespaceWithoutMultilineWithDotall() { rewriteRun( - spec -> spec.recipe(new Find("One.Two$", true, true, false, true, null)), + spec -> spec.recipe(new Find("One.Two$", true, true, false, true, null, null)), //language=csv text( // the `.` above matches the space character on the same line """ @@ -163,7 +163,7 @@ void regexMatchingWhitespaceWithoutMultilineWithDotall() { @Test void regexWithoutMultilineAndWithDotAll() { rewriteRun( - spec -> spec.recipe(new Find("^This.*below\\.$", true, true, false, true, null)), + spec -> spec.recipe(new Find("^This.*below\\.$", true, true, false, true, null, null)), text( """ This is text. @@ -186,7 +186,7 @@ void regexWithoutMultilineAndWithDotAll() { @Test void regexWithMultilineAndWithoutDotall() { rewriteRun( - spec -> spec.recipe(new Find("^This.*below\\.$", true, true, true, false, null)), + spec -> spec.recipe(new Find("^This.*below\\.$", true, true, true, false, null, null)), text( """ This is text. @@ -209,7 +209,7 @@ void regexWithMultilineAndWithoutDotall() { @Test void regexWithBothMultilineAndDotAll() { rewriteRun( - spec -> spec.recipe(new Find("^This.*below\\.$", true, true, true, true, null)), + spec -> spec.recipe(new Find("^This.*below\\.$", true, true, true, true, null, null)), text( """ The first line. @@ -228,4 +228,19 @@ void regexWithBothMultilineAndDotAll() { ) ); } + + @Test + void description() { + rewriteRun( + spec -> spec.recipe(new Find("text", null, null, null, null, null, true)), + text( + """ + This is text. + """, + """ + This is ~~(text)~~>text. + """ + ) + ); + } } diff --git a/rewrite-java/src/main/java/org/openrewrite/java/ChangeType.java b/rewrite-java/src/main/java/org/openrewrite/java/ChangeType.java index 7dfbb7fed68..8cdb8cf4865 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/ChangeType.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/ChangeType.java @@ -114,7 +114,7 @@ public boolean isAcceptable(SourceFile sourceFile, ExecutionContext ctx) { SourceFileWithReferences.References references = sourceFile.getReferences(); TypeMatcher matcher = new TypeMatcher(oldFullyQualifiedTypeName); Map matches = new HashMap<>(); - for (Reference ref : references.findMatches(matcher, Reference.Kind.TYPE)) { + for (Reference ref : references.findMatches(matcher)) { matches.put(ref.getTree(), ref); } return new ReferenceChangeTypeVisitor(matches, matcher.createRenamer(newFullyQualifiedTypeName)).visit(tree, ctx, requireNonNull(getCursor().getParent())); diff --git a/rewrite-java/src/main/java/org/openrewrite/java/search/FindTypes.java b/rewrite-java/src/main/java/org/openrewrite/java/search/FindTypes.java index f9e4a051cf7..7a0dff047c5 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/search/FindTypes.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/search/FindTypes.java @@ -27,7 +27,6 @@ import org.openrewrite.java.table.TypeUses; import org.openrewrite.java.tree.*; import org.openrewrite.marker.SearchResult; -import org.openrewrite.trait.Reference; import org.openrewrite.trait.Trait; import java.util.HashSet; @@ -80,7 +79,7 @@ public boolean isAcceptable(SourceFile sourceFile, ExecutionContext ctx) { SourceFileWithReferences sourceFile = (SourceFileWithReferences) tree; SourceFileWithReferences.References references = sourceFile.getReferences(); TypeMatcher matcher = new TypeMatcher(fullyQualifiedTypeName); - Set matches = references.findMatches(matcher, Reference.Kind.TYPE).stream().map(Trait::getTree).collect(Collectors.toSet()); + Set matches = references.findMatches(matcher).stream().map(Trait::getTree).collect(Collectors.toSet()); return new ReferenceVisitor(matches).visit(tree, ctx); } return tree; diff --git a/rewrite-java/src/main/java/org/openrewrite/java/search/UsesType.java b/rewrite-java/src/main/java/org/openrewrite/java/search/UsesType.java index 2e701f37491..4044dfcbc78 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/search/UsesType.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/search/UsesType.java @@ -123,7 +123,7 @@ public boolean isAcceptable(SourceFile sourceFile, P p) { SourceFileWithReferences sourceFile = (SourceFileWithReferences) tree; SourceFileWithReferences.References references = sourceFile.getReferences(); TypeMatcher matcher = typeMatcher != null ? typeMatcher : new TypeMatcher(fullyQualifiedType); - for (Reference ignored : references.findMatches(matcher, Reference.Kind.TYPE)) { + for (Reference ignored : references.findMatches(matcher)) { return SearchResult.found(sourceFile); } } diff --git a/rewrite-properties/src/main/java/org/openrewrite/properties/trait/PropertiesReference.java b/rewrite-properties/src/main/java/org/openrewrite/properties/trait/PropertiesReference.java index 2d5fd55846a..f2ad9404f93 100644 --- a/rewrite-properties/src/main/java/org/openrewrite/properties/trait/PropertiesReference.java +++ b/rewrite-properties/src/main/java/org/openrewrite/properties/trait/PropertiesReference.java @@ -25,8 +25,6 @@ import org.openrewrite.trait.Reference; import org.openrewrite.trait.SimpleTraitMatcher; -import java.util.HashSet; -import java.util.Set; import java.util.function.Predicate; import java.util.regex.Pattern; @@ -64,29 +62,28 @@ public Tree rename(Renamer renamer, Cursor cursor, ExecutionContext ctx) { return tree; } - private static class Matcher extends SimpleTraitMatcher { - private static final Predicate javaFullyQualifiedTypeMatcher = Pattern.compile( - "\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*\\.\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*(?:\\.\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*)*").asPredicate(); + public static class Provider extends AbstractProvider { + private static final Predicate applicationPropertiesMatcher = Pattern.compile("^application(-\\w+)?\\.properties$").asPredicate(); + private static final SimpleTraitMatcher matcher = new SimpleTraitMatcher() { + private final Predicate javaFullyQualifiedTypeMatcher = Pattern.compile( + "^\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*" + + "\\.\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*" + + "(?:\\.\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*)*$").asPredicate(); - @Override - protected @Nullable PropertiesReference test(Cursor cursor) { - Object value = cursor.getValue(); - if (value instanceof Properties.Entry && - javaFullyQualifiedTypeMatcher.test(((Properties.Entry) value).getValue().getText())) { - return new PropertiesReference(cursor, determineKind(((Properties.Entry) value).getValue().getText())); + @Override + protected @Nullable PropertiesReference test(Cursor cursor) { + Object value = cursor.getValue(); + if (value instanceof Properties.Entry && + javaFullyQualifiedTypeMatcher.test(((Properties.Entry) value).getValue().getText())) { + return new PropertiesReference(cursor, determineKind(((Properties.Entry) value).getValue().getText())); + } + return null; } - return null; - } - private Kind determineKind(String value) { - return Character.isUpperCase(value.charAt(value.lastIndexOf('.') + 1)) ? Kind.TYPE : Kind.PACKAGE; - } - } - - @SuppressWarnings("unused") - public static class Provider implements Reference.Provider { - - private static final Predicate applicationPropertiesMatcher = Pattern.compile("^application(-\\w+)?\\.properties$").asPredicate(); + private Kind determineKind(String value) { + return Character.isUpperCase(value.charAt(value.lastIndexOf('.') + 1)) ? Kind.TYPE : Kind.PACKAGE; + } + }; @Override public boolean isAcceptable(SourceFile sourceFile) { @@ -94,13 +91,8 @@ public boolean isAcceptable(SourceFile sourceFile) { } @Override - public Set getReferences(SourceFile sourceFile) { - Set references = new HashSet<>(); - new Matcher().asVisitor(reference -> { - references.add(reference); - return reference.getTree(); - }).visit(sourceFile, 0); - return references; + public SimpleTraitMatcher getMatcher() { + return matcher; } } } diff --git a/rewrite-properties/src/main/java/org/openrewrite/properties/tree/Properties.java b/rewrite-properties/src/main/java/org/openrewrite/properties/tree/Properties.java index 098ea01dd35..d24b3706fa7 100755 --- a/rewrite-properties/src/main/java/org/openrewrite/properties/tree/Properties.java +++ b/rewrite-properties/src/main/java/org/openrewrite/properties/tree/Properties.java @@ -23,12 +23,12 @@ import org.openrewrite.properties.PropertiesVisitor; import org.openrewrite.properties.internal.PropertiesPrinter; -import java.beans.Transient; import java.lang.ref.SoftReference; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.util.List; +import java.util.Objects; import java.util.UUID; import java.util.regex.Pattern; @@ -117,21 +117,10 @@ public

TreeVisitor> printer(Cursor cursor) { @NonFinal transient SoftReference references; - @Transient @Override public References getReferences() { - References cache; - if (this.references == null) { - cache = References.build(this); - this.references = new SoftReference<>(cache); - } else { - cache = this.references.get(); - if (cache == null || cache.getSourceFile() != this) { - cache = References.build(this); - this.references = new SoftReference<>(cache); - } - } - return cache; + this.references = build(this.references); + return Objects.requireNonNull(this.references.get()); } } diff --git a/rewrite-protobuf/src/main/java/org/openrewrite/protobuf/ProtoParser.java b/rewrite-protobuf/src/main/java/org/openrewrite/protobuf/ProtoParser.java index 0bdb42ad260..d491f8506f4 100644 --- a/rewrite-protobuf/src/main/java/org/openrewrite/protobuf/ProtoParser.java +++ b/rewrite-protobuf/src/main/java/org/openrewrite/protobuf/ProtoParser.java @@ -66,6 +66,7 @@ public Stream parseInputs(Iterable sourceFiles, @Nullable Pat input.getFileAttributes(), null, sourceStr, + null, null ); } diff --git a/rewrite-xml/src/main/java/org/openrewrite/xml/trait/SpringXmlReference.java b/rewrite-xml/src/main/java/org/openrewrite/xml/trait/SpringXmlReference.java index 41523a89b8a..0d0db0a51da 100644 --- a/rewrite-xml/src/main/java/org/openrewrite/xml/trait/SpringXmlReference.java +++ b/rewrite-xml/src/main/java/org/openrewrite/xml/trait/SpringXmlReference.java @@ -15,6 +15,7 @@ */ package org.openrewrite.xml.trait; +import lombok.EqualsAndHashCode; import lombok.Value; import org.jspecify.annotations.Nullable; import org.openrewrite.Cursor; @@ -24,12 +25,11 @@ import org.openrewrite.xml.XPathMatcher; import org.openrewrite.xml.tree.Xml; -import java.util.HashSet; import java.util.Optional; -import java.util.Set; import java.util.regex.Pattern; @Value +@EqualsAndHashCode(callSuper = false) public class SpringXmlReference extends XmlReference { Cursor cursor; @@ -40,54 +40,42 @@ public Kind getKind() { return kind; } - static class Matcher extends SimpleTraitMatcher { - private final Pattern referencePattern = Pattern.compile("\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*(?:\\.\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*)*"); - private final XPathMatcher classXPath = new XPathMatcher("//@class"); - private final XPathMatcher typeXPath = new XPathMatcher("//@type"); - private final XPathMatcher keyTypeXPath = new XPathMatcher("//@key-type"); - private final XPathMatcher valueTypeXPath = new XPathMatcher("//@value-type"); - private final XPathMatcher tags = new XPathMatcher("//value"); + public static class Provider extends AbstractProvider { + private static final SimpleTraitMatcher matcher = new SimpleTraitMatcher() { + private final Pattern referencePattern = Pattern.compile("\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*(?:\\.\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*)*"); + private final XPathMatcher classXPath = new XPathMatcher("//@class"); + private final XPathMatcher typeXPath = new XPathMatcher("//@type"); + private final XPathMatcher keyTypeXPath = new XPathMatcher("//@key-type"); + private final XPathMatcher valueTypeXPath = new XPathMatcher("//@value-type"); + private final XPathMatcher tags = new XPathMatcher("//value"); - @Override - protected @Nullable SpringXmlReference test(Cursor cursor) { - Object value = cursor.getValue(); - if (value instanceof Xml.Attribute) { - Xml.Attribute attrib = (Xml.Attribute) value; - if (classXPath.matches(cursor) || typeXPath.matches(cursor) || keyTypeXPath.matches(cursor) || valueTypeXPath.matches(cursor)) { - String stringVal = attrib.getValueAsString(); - if (referencePattern.matcher(stringVal).matches()) { - return new SpringXmlReference(cursor, determineKind(stringVal)); + @Override + protected @Nullable SpringXmlReference test(Cursor cursor) { + Object value = cursor.getValue(); + if (value instanceof Xml.Attribute) { + Xml.Attribute attrib = (Xml.Attribute) value; + if (classXPath.matches(cursor) || typeXPath.matches(cursor) || keyTypeXPath.matches(cursor) || valueTypeXPath.matches(cursor)) { + String stringVal = attrib.getValueAsString(); + if (referencePattern.matcher(stringVal).matches()) { + return new SpringXmlReference(cursor, determineKind(stringVal)); + } } - } - } else if (value instanceof Xml.Tag) { - Xml.Tag tag = (Xml.Tag) value; - if (tags.matches(cursor)) { - Optional stringVal = tag.getValue(); - if (stringVal.isPresent() && referencePattern.matcher(stringVal.get()).matches()) { - return new SpringXmlReference(cursor, determineKind(stringVal.get())); + } else if (value instanceof Xml.Tag) { + Xml.Tag tag = (Xml.Tag) value; + if (tags.matches(cursor)) { + Optional stringVal = tag.getValue(); + if (stringVal.isPresent() && referencePattern.matcher(stringVal.get()).matches()) { + return new SpringXmlReference(cursor, determineKind(stringVal.get())); + } } } + return null; } - return null; - } - - Reference.Kind determineKind(String value) { - return Character.isUpperCase(value.charAt(value.lastIndexOf('.') + 1)) ? Reference.Kind.TYPE : Reference.Kind.PACKAGE; - } - } - @SuppressWarnings("unused") - public static class Provider implements Reference.Provider { - - @Override - public Set getReferences(SourceFile sourceFile) { - Set references = new HashSet<>(); - new Matcher().asVisitor(reference -> { - references.add(reference); - return reference.getTree(); - }).visit(sourceFile, 0); - return references; - } + Reference.Kind determineKind(String value) { + return Character.isUpperCase(value.charAt(value.lastIndexOf('.') + 1)) ? Reference.Kind.TYPE : Reference.Kind.PACKAGE; + } + }; @Override public boolean isAcceptable(SourceFile sourceFile) { @@ -104,5 +92,10 @@ public boolean isAcceptable(SourceFile sourceFile) { } return false; } + + @Override + public SimpleTraitMatcher getMatcher() { + return matcher; + } } } diff --git a/rewrite-xml/src/main/java/org/openrewrite/xml/tree/Xml.java b/rewrite-xml/src/main/java/org/openrewrite/xml/tree/Xml.java index 1fabf24388c..138f470ab7a 100755 --- a/rewrite-xml/src/main/java/org/openrewrite/xml/tree/Xml.java +++ b/rewrite-xml/src/main/java/org/openrewrite/xml/tree/Xml.java @@ -31,15 +31,11 @@ import org.openrewrite.xml.internal.XmlPrinter; import org.openrewrite.xml.internal.XmlWhitespaceValidationService; -import java.beans.Transient; import java.lang.ref.SoftReference; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.Path; -import java.util.Collections; -import java.util.List; -import java.util.Optional; -import java.util.UUID; +import java.util.*; import java.util.stream.Collectors; import static java.util.Collections.emptyList; @@ -171,23 +167,11 @@ public T service(Class service) { @NonFinal transient SoftReference references; - @Transient @Override public References getReferences() { - References cache; - if (this.references == null) { - cache = References.build(this); - this.references = new SoftReference<>(cache); - } else { - cache = this.references.get(); - if (cache == null || cache.getSourceFile() != this) { - cache = References.build(this); - this.references = new SoftReference<>(cache); - } - } - return cache; + this.references = build(this.references); + return Objects.requireNonNull(this.references.get()); } - } @Value diff --git a/rewrite-xml/src/test/java/org/openrewrite/xml/trait/SpringXmlReferenceTest.java b/rewrite-xml/src/test/java/org/openrewrite/xml/trait/SpringXmlReferenceTest.java index 000d242b16e..1eec75fbb0a 100644 --- a/rewrite-xml/src/test/java/org/openrewrite/xml/trait/SpringXmlReferenceTest.java +++ b/rewrite-xml/src/test/java/org/openrewrite/xml/trait/SpringXmlReferenceTest.java @@ -27,7 +27,7 @@ class SpringXmlReferenceTest implements RewriteTest { @Override public void defaults(RecipeSpec spec) { - spec.recipe(RewriteTest.toRecipe(() -> new SpringXmlReference.Matcher() + spec.recipe(RewriteTest.toRecipe(() -> new SpringXmlReference.Provider().getMatcher() .asVisitor(springJavaTypeReference -> SearchResult.found(springJavaTypeReference.getTree(), springJavaTypeReference.getValue())))); } diff --git a/rewrite-yaml/src/main/java/org/openrewrite/yaml/trait/YamlApplicationConfigReference.java b/rewrite-yaml/src/main/java/org/openrewrite/yaml/trait/YamlApplicationConfigReference.java new file mode 100644 index 00000000000..693f6bcdc77 --- /dev/null +++ b/rewrite-yaml/src/main/java/org/openrewrite/yaml/trait/YamlApplicationConfigReference.java @@ -0,0 +1,69 @@ +/* + * 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 org.openrewrite.yaml.trait; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Value; +import org.jspecify.annotations.Nullable; +import org.openrewrite.Cursor; +import org.openrewrite.SourceFile; +import org.openrewrite.trait.SimpleTraitMatcher; +import org.openrewrite.yaml.tree.Yaml; + +import java.util.function.Predicate; +import java.util.regex.Pattern; + +@Value +@EqualsAndHashCode(callSuper = false) +public class YamlApplicationConfigReference extends YamlReference { + Cursor cursor; + @Getter + Kind kind; + + public static class Provider extends YamlProvider { + private static final Predicate applicationPropertiesMatcher = Pattern.compile("^application(-\\w+)?\\.(yaml|yml)$").asPredicate(); + private static final SimpleTraitMatcher matcher = new SimpleTraitMatcher() { + private final Predicate javaFullyQualifiedTypePattern = Pattern.compile( + "^\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*" + + "\\.\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*" + + "(?:\\.\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*)*$").asPredicate(); + + @Override + protected @Nullable YamlReference test(Cursor cursor) { + Object value = cursor.getValue(); + if (value instanceof Yaml.Scalar && javaFullyQualifiedTypePattern.test(((Yaml.Scalar) value).getValue())) { + return new YamlApplicationConfigReference(cursor, determineKind(((Yaml.Scalar) value).getValue())); + } + return null; + } + + private Kind determineKind(String value) { + return Character.isUpperCase(value.charAt(value.lastIndexOf('.') + 1)) ? Kind.TYPE : Kind.PACKAGE; + } + }; + + @Override + public boolean isAcceptable(SourceFile sourceFile) { + return super.isAcceptable(sourceFile) && applicationPropertiesMatcher.test(sourceFile.getSourcePath().getFileName().toString()); + } + + @Override + public SimpleTraitMatcher getMatcher() { + return matcher; + } + } +} diff --git a/rewrite-yaml/src/main/java/org/openrewrite/yaml/trait/YamlReference.java b/rewrite-yaml/src/main/java/org/openrewrite/yaml/trait/YamlReference.java index de2dce82039..18ced348a74 100644 --- a/rewrite-yaml/src/main/java/org/openrewrite/yaml/trait/YamlReference.java +++ b/rewrite-yaml/src/main/java/org/openrewrite/yaml/trait/YamlReference.java @@ -15,31 +15,14 @@ */ package org.openrewrite.yaml.trait; -import lombok.Value; -import org.jspecify.annotations.Nullable; import org.openrewrite.Cursor; import org.openrewrite.ExecutionContext; import org.openrewrite.SourceFile; import org.openrewrite.Tree; import org.openrewrite.trait.Reference; -import org.openrewrite.trait.SimpleTraitMatcher; import org.openrewrite.yaml.tree.Yaml; -import java.util.HashSet; -import java.util.Set; -import java.util.function.Predicate; -import java.util.regex.Pattern; - -@Value -public class YamlReference implements Reference { - Cursor cursor; - Kind kind; - - @Override - public Kind getKind() { - return kind; - } - +public abstract class YamlReference implements Reference { @Override public String getValue() { if (getTree() instanceof Yaml.Scalar) { @@ -62,44 +45,10 @@ public Tree rename(Renamer renamer, Cursor cursor, ExecutionContext ctx) { throw new IllegalArgumentException("cursor.getValue() must be an Yaml.Scalar but is: " + tree.getClass()); } - private static class Matcher extends SimpleTraitMatcher { - private static final Predicate javaFullyQualifiedTypePattern = Pattern.compile( - "\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*\\.\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*(?:\\.\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*)*") - .asPredicate(); - - @Override - protected @Nullable YamlReference test(Cursor cursor) { - Object value = cursor.getValue(); - if (value instanceof Yaml.Scalar && - javaFullyQualifiedTypePattern.test(((Yaml.Scalar) value).getValue())) { - return new YamlReference(cursor, determineKind(((Yaml.Scalar) value).getValue())); - } - return null; - } - - private Kind determineKind(String value) { - return !value.endsWith(".") && Character.isUpperCase(value.charAt(value.lastIndexOf('.') + 1)) ? Kind.TYPE : Kind.PACKAGE; - } - } - - @SuppressWarnings("unused") - public static class Provider implements Reference.Provider { - - private static final Predicate applicationPropertiesMatcher = Pattern.compile("^application(-\\w+)?\\.(yaml|yml)$").asPredicate(); - + public static abstract class YamlProvider extends AbstractProvider { @Override public boolean isAcceptable(SourceFile sourceFile) { - return sourceFile instanceof Yaml.Documents && applicationPropertiesMatcher.test(sourceFile.getSourcePath().getFileName().toString()); - } - - @Override - public Set getReferences(SourceFile sourceFile) { - Set references = new HashSet<>(); - new Matcher().asVisitor(reference -> { - references.add(reference); - return reference.getTree(); - }).visit(sourceFile, 0); - return references; + return sourceFile instanceof Yaml.Documents; } } } diff --git a/rewrite-yaml/src/main/java/org/openrewrite/yaml/tree/Yaml.java b/rewrite-yaml/src/main/java/org/openrewrite/yaml/tree/Yaml.java index 4639bfbf40b..13ab313924b 100755 --- a/rewrite-yaml/src/main/java/org/openrewrite/yaml/tree/Yaml.java +++ b/rewrite-yaml/src/main/java/org/openrewrite/yaml/tree/Yaml.java @@ -24,12 +24,12 @@ import org.openrewrite.yaml.YamlVisitor; import org.openrewrite.yaml.internal.YamlPrinter; -import java.beans.Transient; import java.lang.ref.SoftReference; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.util.List; +import java.util.Objects; import java.util.UUID; import static java.util.stream.Collectors.toList; @@ -135,21 +135,10 @@ public

TreeVisitor> printer(Cursor cursor) { @NonFinal transient SoftReference references; - @Transient @Override public References getReferences() { - References cache; - if (this.references == null) { - cache = References.build(this); - this.references = new SoftReference<>(cache); - } else { - cache = this.references.get(); - if (cache == null || cache.getSourceFile() != this) { - cache = References.build(this); - this.references = new SoftReference<>(cache); - } - } - return cache; + this.references = build(this.references); + return Objects.requireNonNull(this.references.get()); } } diff --git a/rewrite-yaml/src/main/resources/META-INF/services/org.openrewrite.trait.Reference$Provider b/rewrite-yaml/src/main/resources/META-INF/services/org.openrewrite.trait.Reference$Provider index dd857df5564..a51673a11b4 100644 --- a/rewrite-yaml/src/main/resources/META-INF/services/org.openrewrite.trait.Reference$Provider +++ b/rewrite-yaml/src/main/resources/META-INF/services/org.openrewrite.trait.Reference$Provider @@ -1 +1 @@ -org.openrewrite.yaml.trait.YamlReference$Provider \ No newline at end of file +org.openrewrite.yaml.trait.YamlApplicationConfigReference$Provider diff --git a/rewrite-yaml/src/test/java/org/openrewrite/yaml/trait/YamlReferenceTest.java b/rewrite-yaml/src/test/java/org/openrewrite/yaml/trait/YamlReferenceTest.java index 956e6eb996c..ddc9abdeff9 100644 --- a/rewrite-yaml/src/test/java/org/openrewrite/yaml/trait/YamlReferenceTest.java +++ b/rewrite-yaml/src/test/java/org/openrewrite/yaml/trait/YamlReferenceTest.java @@ -108,16 +108,11 @@ void endsWithDot() { """, spec -> spec .path("application.yml") - .afterRecipe(doc -> assertThat(doc.getReferences().getReferences()) - .satisfiesExactlyInAnyOrder( - ref -> { - assertThat(ref.getKind()).isEqualTo(Reference.Kind.TYPE); - assertThat(ref.getValue()).isEqualTo("org.openrewrite.java.DoSomething"); - }, - ref -> { - assertThat(ref.getKind()).isEqualTo(Reference.Kind.PACKAGE); - assertThat(ref.getValue()).isEqualTo("org.foo."); - }) + .afterRecipe(doc -> assertThat(doc.getReferences().getReferences()).singleElement().satisfies( + ref -> { + assertThat(ref.getKind()).isEqualTo(Reference.Kind.TYPE); + assertThat(ref.getValue()).isEqualTo("org.openrewrite.java.DoSomething"); + }) ) ) );