diff --git a/rewrite-core/src/main/java/org/openrewrite/text/DockerImageReference.java b/rewrite-core/src/main/java/org/openrewrite/text/DockerImageReference.java new file mode 100644 index 00000000000..858585388d1 --- /dev/null +++ b/rewrite-core/src/main/java/org/openrewrite/text/DockerImageReference.java @@ -0,0 +1,50 @@ +package org.openrewrite.text; + +import lombok.Value; +import org.openrewrite.Cursor; +import org.openrewrite.SourceFile; +import org.openrewrite.internal.StringUtils; +import org.openrewrite.trait.Reference; +import org.openrewrite.trait.SimpleTraitMatcher; + +@Value +public class DockerImageReference implements Reference { + Cursor cursor; + String value; + + @Override + public Kind getKind() { + return Kind.IMAGE; + } + + public static class Provider implements Reference.Provider { + @Override + public boolean isAcceptable(SourceFile sourceFile) { + if (sourceFile instanceof PlainText) { + PlainText text = (PlainText) sourceFile; + String fileName = text.getSourcePath().toFile().getName(); + return (fileName.equals("Dockerfile") || fileName.equals("Containerfile")) && text.getText().contains("FROM"); + } + return false; + } + + @Override + public SimpleTraitMatcher getMatcher() { + return new SimpleTraitMatcher() { + @Override + protected DockerImageReference test(Cursor cursor) { + String text = ((PlainText) cursor.getValue()).getText(); + int index = StringUtils.indexOfNextNonWhitespace(text.indexOf("FROM") + 4, text); + StringBuilder image = new StringBuilder(); + for (char c : text.substring(index).toCharArray()) { + if (Character.isWhitespace(c)) { + break; + } + image.append(c); + } + return new DockerImageReference(cursor, image.toString()); + } + }; + } + } +} 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..23676049fe8 100644 --- a/rewrite-core/src/main/java/org/openrewrite/text/PlainText.java +++ b/rewrite-core/src/main/java/org/openrewrite/text/PlainText.java @@ -16,10 +16,12 @@ 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; @@ -36,7 +38,7 @@ @Value @Builder @AllArgsConstructor -public class PlainText implements SourceFile, Tree { +public class PlainText implements SourceFileWithReferences, Tree { @Builder.Default @With @@ -82,7 +84,7 @@ public SourceFile withCharset(Charset charset) { 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; } @@ -123,7 +125,28 @@ 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, references); + } + + @Nullable + @NonFinal + @ToString.Exclude + transient SoftReference references; + + @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; } @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 ab3ef6c2d75..5cfaf108459 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") @@ -51,11 +52,19 @@ default Tree rename(Renamer renamer, Cursor cursor, ExecutionContext ctx) { throw new UnsupportedOperationException(); } - interface Provider { + interface Provider { + boolean isAcceptable(SourceFile sourceFile); - Set getReferences(SourceFile sourceFile); + SimpleTraitMatcher getMatcher(); - boolean isAcceptable(SourceFile sourceFile); + default Set getReferences(SourceFile sourceFile) { + Set references = new HashSet<>(); + getMatcher().asVisitor(reference -> { + references.add(reference); + return reference.getTree(); + }).visit(sourceFile, 0); + return references; + } } interface Matcher { diff --git a/rewrite-core/src/main/resources/META-INF/services/org.openrewrite.trait.Reference$Provider b/rewrite-core/src/main/resources/META-INF/services/org.openrewrite.trait.Reference$Provider new file mode 100644 index 00000000000..1e723ab2679 --- /dev/null +++ b/rewrite-core/src/main/resources/META-INF/services/org.openrewrite.trait.Reference$Provider @@ -0,0 +1 @@ +org.openrewrite.text.DockerImageReference$Provider diff --git a/rewrite-java/src/test/java/org/openrewrite/java/FindImageTest.java b/rewrite-java/src/test/java/org/openrewrite/java/FindImageTest.java index de2934de2b9..3565ddc5322 100644 --- a/rewrite-java/src/test/java/org/openrewrite/java/FindImageTest.java +++ b/rewrite-java/src/test/java/org/openrewrite/java/FindImageTest.java @@ -17,11 +17,9 @@ import org.junit.jupiter.api.Test; import org.openrewrite.DocumentExample; -import org.openrewrite.java.table.ImageSourceFiles; import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; -import static org.assertj.core.api.Assertions.assertThat; import static org.openrewrite.test.SourceSpecs.text; import static org.openrewrite.yaml.Assertions.yaml; @@ -33,7 +31,6 @@ public void defaults(RecipeSpec spec) { spec.recipe(new FindImage()); } - @DocumentExample @Test void gitlabCIFile() { @@ -114,7 +111,7 @@ void dockerFile() { ENTRYPOINT ["java","-jar","/app.jar"] """, """ - ~~(FROM openjdk:8-jdk-alpine)~~>FROM openjdk:8-jdk-alpine + ~~(openjdk:8-jdk-alpine)~~>FROM openjdk:8-jdk-alpine ARG JAR_FILE=target/*.jar COPY ${JAR_FILE} app.jar ENTRYPOINT ["java","-jar","/app.jar"] 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..2e4f91dcdf9 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 @@ -18,15 +18,11 @@ 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.properties.tree.Properties; 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; @@ -53,39 +49,8 @@ public boolean supportsRename() { return true; } - @Override - public Tree rename(Renamer renamer, Cursor cursor, ExecutionContext ctx) { - Tree tree = cursor.getValue(); - if (tree instanceof Properties.Entry) { - Properties.Entry entry = (Properties.Entry) tree; - String newValueText = renamer.rename(this); - return entry.withValue(entry.getValue().withText(newValueText)); - } - 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(); - - @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; - } - - 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 { - + public static class Provider implements Reference.Provider { private static final Predicate applicationPropertiesMatcher = Pattern.compile("^application(-\\w+)?\\.properties$").asPredicate(); @Override @@ -94,13 +59,25 @@ 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 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())); + } + return null; + } + + private Kind determineKind(String value) { + return Character.isUpperCase(value.charAt(value.lastIndexOf('.') + 1)) ? Kind.TYPE : Kind.PACKAGE; + } + }; } } } diff --git a/rewrite-xml/src/main/java/org/openrewrite/xml/trait/SpringReference.java b/rewrite-xml/src/main/java/org/openrewrite/xml/trait/SpringReference.java index a2a78481461..53ddc3fcc90 100644 --- a/rewrite-xml/src/main/java/org/openrewrite/xml/trait/SpringReference.java +++ b/rewrite-xml/src/main/java/org/openrewrite/xml/trait/SpringReference.java @@ -26,13 +26,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 -class SpringReference implements Reference { +public class SpringReference implements Reference { Cursor cursor; Kind kind; @@ -115,18 +113,7 @@ Kind determineKind(String value) { } } - @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; - } + public static class Provider implements Reference.Provider { @Override public boolean isAcceptable(SourceFile sourceFile) { @@ -143,5 +130,10 @@ public boolean isAcceptable(SourceFile sourceFile) { } return false; } + + @Override + public SimpleTraitMatcher getMatcher() { + return new Matcher(); + } } } 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 index 1b9643212a8..aab22598d4f 100644 --- a/rewrite-yaml/src/main/java/org/openrewrite/yaml/trait/YamlApplicationConfigReference.java +++ b/rewrite-yaml/src/main/java/org/openrewrite/yaml/trait/YamlApplicationConfigReference.java @@ -43,7 +43,7 @@ public boolean isAcceptable(SourceFile sourceFile) { } @Override - SimpleTraitMatcher getMatcher() { + public SimpleTraitMatcher getMatcher() { return new SimpleTraitMatcher() { private final Predicate javaFullyQualifiedTypePattern = Pattern.compile( "\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*\\.\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*(?:\\.\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*)*") diff --git a/rewrite-yaml/src/main/java/org/openrewrite/yaml/trait/YamlImageReference.java b/rewrite-yaml/src/main/java/org/openrewrite/yaml/trait/YamlImageReference.java index f7615eb1e89..14150cc937c 100644 --- a/rewrite-yaml/src/main/java/org/openrewrite/yaml/trait/YamlImageReference.java +++ b/rewrite-yaml/src/main/java/org/openrewrite/yaml/trait/YamlImageReference.java @@ -38,7 +38,7 @@ public Kind getKind() { public static class Provider extends YamlProvider { @Override - SimpleTraitMatcher getMatcher() { + public SimpleTraitMatcher getMatcher() { return new SimpleTraitMatcher() { private final Predicate image = Pattern.compile("image").asPredicate(); private final AtomicBoolean found = new AtomicBoolean(false); 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 18b08ca9645..50337257c65 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 @@ -17,12 +17,8 @@ import org.openrewrite.SourceFile; import org.openrewrite.trait.Reference; -import org.openrewrite.trait.SimpleTraitMatcher; import org.openrewrite.yaml.tree.Yaml; -import java.util.HashSet; -import java.util.Set; - public abstract class YamlReference implements Reference { @Override public String getValue() { @@ -37,22 +33,10 @@ public boolean supportsRename() { return true; } - static abstract class YamlProvider implements Reference.Provider { - abstract SimpleTraitMatcher getMatcher(); - + static abstract class YamlProvider implements Reference.Provider { @Override public boolean isAcceptable(SourceFile sourceFile) { return sourceFile instanceof Yaml.Documents; } - - @Override - public Set getReferences(SourceFile sourceFile) { - Set references = new HashSet<>(); - getMatcher().asVisitor(reference -> { - references.add(reference); - return reference.getTree(); - }).visit(sourceFile, 0); - return references; - } } }