) resource;
- if (resourceType.equals(ResourceType.fromSpec((String) resourceMap.get("type")))) {
+ if (resourceType == ResourceType.fromSpec((String) resourceMap.get("type"))) {
resources.add(resourceMap);
}
}
diff --git a/rewrite-core/src/main/java/org/openrewrite/format/LineBreaks.java b/rewrite-core/src/main/java/org/openrewrite/format/LineBreaks.java
new file mode 100644
index 00000000000..da2bdfc05ea
--- /dev/null
+++ b/rewrite-core/src/main/java/org/openrewrite/format/LineBreaks.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2025 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.format;
+
+public class LineBreaks {
+ public static String normalizeNewLines(String text, boolean useCrlf) {
+ if (!text.contains("\n")) {
+ return text;
+ }
+ StringBuilder normalized = new StringBuilder();
+ for (int i = 0; i < text.length(); i++) {
+ char c = text.charAt(i);
+ if (useCrlf && c == '\n' && (i == 0 || text.charAt(i - 1) != '\r')) {
+ normalized.append('\r').append('\n');
+ } else if (useCrlf || c != '\r') {
+ normalized.append(c);
+ }
+ }
+ return normalized.toString();
+ }
+}
diff --git a/rewrite-core/src/main/java/org/openrewrite/internal/ListUtils.java b/rewrite-core/src/main/java/org/openrewrite/internal/ListUtils.java
index a18cc8bf585..5de88dc8c50 100644
--- a/rewrite-core/src/main/java/org/openrewrite/internal/ListUtils.java
+++ b/rewrite-core/src/main/java/org/openrewrite/internal/ListUtils.java
@@ -261,6 +261,7 @@ public static List insert(@Nullable List ls, @Nullable T t, int index)
/**
* For backwards compatibility; prefer {@link #map(List, Function)}.
*/
+ @Contract("null, _ -> null; !null, _ -> !null")
public static @Nullable List map(@Nullable List ls, UnaryOperator<@Nullable T> map) {
return map(ls, (Function) map);
}
diff --git a/rewrite-core/src/main/java/org/openrewrite/internal/NameCaseConvention.java b/rewrite-core/src/main/java/org/openrewrite/internal/NameCaseConvention.java
index 74639b39b89..038a065c074 100755
--- a/rewrite-core/src/main/java/org/openrewrite/internal/NameCaseConvention.java
+++ b/rewrite-core/src/main/java/org/openrewrite/internal/NameCaseConvention.java
@@ -96,6 +96,9 @@ public boolean matches(String str) {
}
public static boolean matches(NameCaseConvention convention, String str) {
+ if (str.isEmpty()) {
+ return false;
+ }
switch (convention) {
case LOWER_CAMEL:
if (!Character.isLowerCase(str.charAt(0)) && str.charAt(0) != '$') {
diff --git a/rewrite-core/src/main/java/org/openrewrite/internal/ReflectionUtils.java b/rewrite-core/src/main/java/org/openrewrite/internal/ReflectionUtils.java
index 8cda946a88d..b207a7232e9 100644
--- a/rewrite-core/src/main/java/org/openrewrite/internal/ReflectionUtils.java
+++ b/rewrite-core/src/main/java/org/openrewrite/internal/ReflectionUtils.java
@@ -36,7 +36,18 @@ public class ReflectionUtils {
* Cache for {@link Class#getDeclaredMethods()} plus equivalent default methods
* from Java 8 based interfaces, allowing for fast iteration.
*/
- private static final Map, Method[]> declaredMethodsCache = new ConcurrentHashMap<>(256);
+ private static final Map, Method[]> DECLARED_METHODS_CACHE = new ConcurrentHashMap<>(256);
+
+ public static boolean isClassAvailable(String fullyQualifiedClassName) {
+ try {
+ ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
+ ClassLoader classLoader = contextClassLoader == null ? ReflectionUtils.class.getClassLoader() : contextClassLoader;
+ Class.forName(fullyQualifiedClassName, false, classLoader);
+ return true;
+ } catch (ClassNotFoundException e) {
+ return false;
+ }
+ }
public static @Nullable Method findMethod(Class> clazz, String name, Class>... paramTypes) {
Class> searchType = clazz;
@@ -54,7 +65,7 @@ public class ReflectionUtils {
}
private static Method[] getDeclaredMethods(Class> clazz) {
- Method[] result = declaredMethodsCache.get(clazz);
+ Method[] result = DECLARED_METHODS_CACHE.get(clazz);
if (result == null) {
try {
Method[] declaredMethods = clazz.getDeclaredMethods();
@@ -70,7 +81,7 @@ private static Method[] getDeclaredMethods(Class> clazz) {
} else {
result = declaredMethods;
}
- declaredMethodsCache.put(clazz, (result.length == 0 ? EMPTY_METHOD_ARRAY : result));
+ DECLARED_METHODS_CACHE.put(clazz, (result.length == 0 ? EMPTY_METHOD_ARRAY : result));
} catch (Throwable ex) {
throw new IllegalStateException("Failed to introspect Class [" + clazz.getName() +
"] from ClassLoader [" + clazz.getClassLoader() + "]", ex);
diff --git a/rewrite-core/src/main/java/org/openrewrite/internal/StringUtils.java b/rewrite-core/src/main/java/org/openrewrite/internal/StringUtils.java
index 7cafdeb341a..e06c1d58281 100644
--- a/rewrite-core/src/main/java/org/openrewrite/internal/StringUtils.java
+++ b/rewrite-core/src/main/java/org/openrewrite/internal/StringUtils.java
@@ -692,7 +692,13 @@ public static int indexOfNextNonWhitespace(int cursor, String source) {
continue;
} else if (length > cursor + 1) {
char next = source.charAt(cursor + 1);
- if (current == '/' && next == '/') {
+ if (inMultiLineComment) {
+ if (current == '*' && next == '/') {
+ inMultiLineComment = false;
+ cursor++;
+ continue;
+ }
+ } else if (current == '/' && next == '/') {
inSingleLineComment = true;
cursor++;
continue;
@@ -700,10 +706,6 @@ public static int indexOfNextNonWhitespace(int cursor, String source) {
inMultiLineComment = true;
cursor++;
continue;
- } else if (current == '*' && next == '/') {
- inMultiLineComment = false;
- cursor++;
- continue;
}
}
if (!inMultiLineComment && !Character.isWhitespace(current)) {
@@ -720,4 +722,14 @@ public static String formatUriForPropertiesFile(String uri) {
public static boolean hasLineBreak(@Nullable String s) {
return s != null && LINE_BREAK.matcher(s).find();
}
+
+ public static boolean containsWhitespace(String s) {
+ for (int i = 0; i < s.length(); ++i) {
+ if (Character.isWhitespace(s.charAt(i))) {
+ return true;
+ }
+ }
+
+ return false;
+ }
}
diff --git a/rewrite-core/src/main/java/org/openrewrite/remote/RemoteArchive.java b/rewrite-core/src/main/java/org/openrewrite/remote/RemoteArchive.java
index 23de1da8810..6e553fdac4b 100644
--- a/rewrite-core/src/main/java/org/openrewrite/remote/RemoteArchive.java
+++ b/rewrite-core/src/main/java/org/openrewrite/remote/RemoteArchive.java
@@ -34,6 +34,7 @@
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.nio.file.Paths;
import java.util.List;
import java.util.UUID;
import java.util.regex.Pattern;
@@ -92,6 +93,9 @@ public InputStream getInputStream(ExecutionContext ctx) {
try {
Path localArchive = cache.compute(uri, () -> {
//noinspection resource
+ if ("file".equals(uri.getScheme())) {
+ return Files.newInputStream(Paths.get(uri));
+ }
HttpSender.Response response = httpSender.send(httpSender.get(uri.toString()).build());
if (response.isSuccessful()) {
return response.getBody();
diff --git a/rewrite-core/src/main/java/org/openrewrite/semver/LatestMinor.java b/rewrite-core/src/main/java/org/openrewrite/semver/LatestMinor.java
new file mode 100644
index 00000000000..17737b52488
--- /dev/null
+++ b/rewrite-core/src/main/java/org/openrewrite/semver/LatestMinor.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2021 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.semver;
+
+import lombok.Value;
+import org.jspecify.annotations.Nullable;
+import org.openrewrite.Validated;
+
+@Value
+public class LatestMinor implements VersionComparator {
+ @Nullable
+ String metadataPattern;
+
+ @Override
+ public boolean isValid(@Nullable String currentVersion, String version) {
+ Validated extends VersionComparator> validated = currentVersion == null ?
+ LatestRelease.buildLatestRelease("latest.release", metadataPattern) :
+ TildeRange.build("~" + Semver.majorVersion(currentVersion), metadataPattern);
+
+ if (validated.isValid()) {
+ VersionComparator comparator = validated.getValue();
+ if (comparator != null) {
+ return comparator.isValid(currentVersion, version);
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public int compare(@Nullable String currentVersion, String v1, String v2) {
+ if(currentVersion == null) {
+ return new LatestRelease(null)
+ .compare(null, v1, v2);
+ }
+
+ //noinspection ConstantConditions
+ return TildeRange.build("~" + Semver.majorVersion(currentVersion) + "." + Semver.minorVersion(currentVersion), metadataPattern)
+ .getValue()
+ .compare(currentVersion, v1, v2);
+ }
+
+ public static Validated build(String toVersion, @Nullable String metadataPattern) {
+ return "latest.minor".equalsIgnoreCase(toVersion) ?
+ Validated.valid("latestMinor", new LatestMinor(metadataPattern)) :
+ Validated.invalid("latestMinor", toVersion, "not latest release");
+ }
+}
diff --git a/rewrite-core/src/main/java/org/openrewrite/semver/LatestRelease.java b/rewrite-core/src/main/java/org/openrewrite/semver/LatestRelease.java
index 18fc5d4b12e..0b1fa2b0095 100644
--- a/rewrite-core/src/main/java/org/openrewrite/semver/LatestRelease.java
+++ b/rewrite-core/src/main/java/org/openrewrite/semver/LatestRelease.java
@@ -140,9 +140,8 @@ public int compare(@Nullable String currentVersion, String v1, String v2) {
// parts are the same:
//
// HyphenRange [25-28] should include "28-jre" and "28-android" as possible candidates.
- String normalized1 = metadataPattern == null ? nv1 : nv1.replace(metadataPattern, "");
- String normalized2 = metadataPattern == null ? nv2 : nv1.replace(metadataPattern, "");
-
+ String normalized1 = metadataPattern == null ? nv1 : nv1.replaceAll(metadataPattern, "");
+ String normalized2 = metadataPattern == null ? nv2 : nv2.replaceAll(metadataPattern, "");
try {
for (int i = 1; i <= Math.max(vp1, vp2); i++) {
String v1Part = v1Gav.group(i);
@@ -168,7 +167,7 @@ public int compare(@Nullable String currentVersion, String v1, String v2) {
}
public static Validated buildLatestRelease(String toVersion, @Nullable String metadataPattern) {
- return "latest.release".equalsIgnoreCase(toVersion) ?
+ return "latest.release".equalsIgnoreCase(toVersion) || "latest.major".equalsIgnoreCase(toVersion) ?
Validated.valid("latestRelease", new LatestRelease(metadataPattern)) :
Validated.invalid("latestRelease", toVersion, "not latest release");
}
diff --git a/rewrite-core/src/main/java/org/openrewrite/semver/Semver.java b/rewrite-core/src/main/java/org/openrewrite/semver/Semver.java
index d4d6388702a..e47ec996629 100644
--- a/rewrite-core/src/main/java/org/openrewrite/semver/Semver.java
+++ b/rewrite-core/src/main/java/org/openrewrite/semver/Semver.java
@@ -59,6 +59,7 @@ public static Validated validate(String toVersion, @Nullable
).and(Validated.none()
.or(LatestRelease.buildLatestRelease(toVersion, metadataPattern))
.or(LatestIntegration.build(toVersion, metadataPattern))
+ .or(LatestMinor.build(toVersion, metadataPattern))
.or(LatestPatch.build(toVersion, metadataPattern))
.or(HyphenRange.build(toVersion, metadataPattern))
.or(XRange.build(toVersion, metadataPattern))
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 90450be6d64..24ef0290980 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;
@@ -27,7 +28,6 @@
import org.openrewrite.table.TextMatches;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -85,12 +85,18 @@ public String getDescription() {
description = "A glob expression that can be used to constrain which directories or source files should be searched. " +
"Multiple patterns may be specified, separated by a semicolon `;`. " +
"If multiple patterns are supplied any of the patterns matching will be interpreted as a match. " +
- "When not set, all source files are searched. ",
+ "When not set, all source files are searched.",
required = false,
example = "**/*.java")
@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, ExecutionContext> getVisitor() {
@@ -122,46 +128,64 @@ public Tree visit(@Nullable Tree tree, ExecutionContext ctx) {
if (!matcher.find()) {
return sourceFile;
}
- matcher.reset();
+
+ String sourceFilePath = sourceFile.getSourcePath().toString();
+
List snippets = new ArrayList<>();
int previousEnd = 0;
- while (matcher.find()) {
+
+ int lastNewLineIndex = -1;
+ int nextNewLineIndex = -1;
+ boolean isFirstMatch = true;
+
+ 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();
- int startLine = Math.max(0, rawText.substring(0, matchStart).lastIndexOf('\n') + 1);
+ // For the first match, search backwards
+ if (isFirstMatch) {
+ lastNewLineIndex = rawText.lastIndexOf('\n', matchStart);
+ nextNewLineIndex = rawText.indexOf('\n', lastNewLineIndex + 1);
+ isFirstMatch = false;
+ } else if (nextNewLineIndex != -1 && nextNewLineIndex < matchStart) {
+ // Advance lastNewLineIndex while before match start
+ while (nextNewLineIndex != -1 && nextNewLineIndex < matchStart) {
+ lastNewLineIndex = nextNewLineIndex;
+ nextNewLineIndex = rawText.indexOf('\n', lastNewLineIndex + 1);
+ }
+ }
+
+ int startLine = lastNewLineIndex + 1;
int endLine = rawText.indexOf('\n', matcher.end());
if (endLine == -1) {
endLine = rawText.length();
}
+ //noinspection StringBufferReplaceableByString
textMatches.insertRow(ctx, new TextMatches.Row(
- sourceFile.getSourcePath().toString(),
- rawText.substring(startLine, matcher.start()) + "~~>" +
- rawText.substring(matcher.start(), endLine)
+ sourceFilePath,
+ new StringBuilder(endLine - startLine + 3)
+ .append(rawText, startLine, matchStart)
+ .append("~~>")
+ .append(rawText, matchStart, endLine)
+ .toString()
));
- }
+ } while (matcher.find());
snippets.add(snippet(rawText.substring(previousEnd)));
return plainText.withText("").withSnippets(snippets);
}
};
- //noinspection DuplicatedCode
if (filePattern != null) {
- //noinspection unchecked
- TreeVisitor, ExecutionContext> check = Preconditions.or(Arrays.stream(filePattern.split(";"))
- .map(FindSourceFiles::new)
- .map(Recipe::getVisitor)
- .toArray(TreeVisitor[]::new));
-
- visitor = Preconditions.check(check, visitor);
+ visitor = Preconditions.check(new FindSourceFiles(filePattern), visitor);
}
return visitor;
}
-
private static PlainText.Snippet snippet(String text) {
return new PlainText.Snippet(Tree.randomId(), Markers.EMPTY, text);
}
+
}
diff --git a/rewrite-core/src/main/java/org/openrewrite/text/FindAndReplace.java b/rewrite-core/src/main/java/org/openrewrite/text/FindAndReplace.java
index 0842da78a57..5a284180a62 100644
--- a/rewrite-core/src/main/java/org/openrewrite/text/FindAndReplace.java
+++ b/rewrite-core/src/main/java/org/openrewrite/text/FindAndReplace.java
@@ -27,7 +27,6 @@
import org.openrewrite.quark.Quark;
import org.openrewrite.remote.Remote;
-import java.util.Arrays;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -95,7 +94,7 @@ public String getDescription() {
description = "A glob expression that can be used to constrain which directories or source files should be searched. " +
"Multiple patterns may be specified, separated by a semicolon `;`. " +
"If multiple patterns are supplied any of the patterns matching will be interpreted as a match. " +
- "When not set, all source files are searched. ",
+ "When not set, all source files are searched.",
required = false,
example = "**/*.java")
@Nullable
@@ -153,15 +152,8 @@ public Tree visit(@Nullable Tree tree, ExecutionContext ctx) {
.withMarkers(sourceFile.getMarkers().add(new AlreadyReplaced(randomId(), find, replace)));
}
};
- //noinspection DuplicatedCode
if (filePattern != null) {
- //noinspection unchecked
- TreeVisitor, ExecutionContext> check = Preconditions.or(Arrays.stream(filePattern.split(";"))
- .map(FindSourceFiles::new)
- .map(Recipe::getVisitor)
- .toArray(TreeVisitor[]::new));
-
- visitor = Preconditions.check(check, visitor);
+ visitor = Preconditions.check(new FindSourceFiles(filePattern), visitor);
}
if (Boolean.TRUE.equals(plaintextOnly)) {
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..623122d9921 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;
@@ -35,8 +38,8 @@
*/
@Value
@Builder
-@AllArgsConstructor
-public class PlainText implements SourceFile, Tree {
+@AllArgsConstructor(access = AccessLevel.PRIVATE)
+public class PlainText implements SourceFileWithReferences, Tree {
@Builder.Default
@With
@@ -79,16 +82,41 @@ public SourceFile withCharset(Charset charset) {
@Builder.Default
String text = "";
+ @Nullable
+ List snippets;
+
+ @Nullable
+ @NonFinal
+ @ToString.Exclude
+ transient SoftReference references;
+
+ public PlainText(UUID id, Path sourcePath, Markers markers, @Nullable String charsetName, boolean charsetBomMarked,
+ @Nullable FileAttributes fileAttributes, @Nullable Checksum checksum, String text, @Nullable List snippets) {
+ this.id = id;
+ this.sourcePath = sourcePath;
+ this.markers = markers;
+ this.charsetName = charsetName;
+ this.charsetBomMarked = charsetBomMarked;
+ this.fileAttributes = fileAttributes;
+ this.checksum = checksum;
+ this.text = text;
+ this.snippets = snippets;
+ }
+
+ @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, P> v, P p) {
return v.isAdaptableTo(PlainTextVisitor.class);
@@ -123,7 +151,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..a1a105e9331 100644
--- a/rewrite-core/src/main/java/org/openrewrite/text/PlainTextParser.java
+++ b/rewrite-core/src/main/java/org/openrewrite/text/PlainTextParser.java
@@ -20,7 +20,6 @@
import org.openrewrite.Parser;
import org.openrewrite.SourceFile;
import org.openrewrite.internal.EncodingDetectingInputStream;
-import org.openrewrite.marker.Markers;
import org.openrewrite.tree.ParseError;
import org.openrewrite.tree.ParsingEventListener;
import org.openrewrite.tree.ParsingExecutionContextView;
@@ -34,8 +33,6 @@
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
-import static org.openrewrite.Tree.randomId;
-
public class PlainTextParser implements Parser {
/**
@@ -73,17 +70,13 @@ public Stream parseInputs(Iterable sources, @Nullable Path re
try {
EncodingDetectingInputStream is = input.getSource(ctx);
String sourceStr = is.readFully();
- PlainText plainText = new PlainText(
- randomId(),
- path,
- Markers.EMPTY,
- is.getCharset().name(),
- is.isCharsetBomMarked(),
- input.getFileAttributes(),
- null,
- sourceStr,
- null
- );
+ PlainText plainText = PlainText.builder()
+ .sourcePath(path)
+ .charsetName(is.getCharset().name())
+ .charsetBomMarked(is.isCharsetBomMarked())
+ .fileAttributes(input.getFileAttributes())
+ .text(sourceStr)
+ .build();
parsingListener.parsed(input, plainText);
return plainText;
} catch (Throwable t) {
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/ExecutionContextTest.java b/rewrite-core/src/test/java/org/openrewrite/ExecutionContextTest.java
index ab9ad0ef731..fa7aaa25d7c 100644
--- a/rewrite-core/src/test/java/org/openrewrite/ExecutionContextTest.java
+++ b/rewrite-core/src/test/java/org/openrewrite/ExecutionContextTest.java
@@ -17,6 +17,7 @@
import org.junit.jupiter.api.Test;
import org.openrewrite.test.RewriteTest;
+import org.openrewrite.test.TypeValidation;
import org.openrewrite.text.PlainText;
import org.openrewrite.text.PlainTextVisitor;
@@ -41,7 +42,8 @@ public PlainText visitText(PlainText text, ExecutionContext ctx) {
cycles.incrementAndGet();
return text;
}
- }).withCausesAnotherCycle(true)),
+ }).withCausesAnotherCycle(true))
+ .typeValidationOptions(TypeValidation.all().immutableExecutionContext(false)),
text("hello world")
);
assertThat(cycles.get()).isEqualTo(2);
diff --git a/rewrite-core/src/test/java/org/openrewrite/GitRemoteTest.java b/rewrite-core/src/test/java/org/openrewrite/GitRemoteTest.java
index 410eb17df9b..37581c2f934 100644
--- a/rewrite-core/src/test/java/org/openrewrite/GitRemoteTest.java
+++ b/rewrite-core/src/test/java/org/openrewrite/GitRemoteTest.java
@@ -232,11 +232,21 @@ void shouldNotStripJgit() {
assertThat(remote.getPath()).isEqualTo("openrewrite/jgit");
}
+ @Test
+ void shouldNotReplaceExistingWellKnownServer(){
+ GitRemote.Parser parser = new GitRemote.Parser()
+ .registerRemote(GitRemote.Service.GitHub, URI.create("https://github.com"), List.of(URI.create("ssh://notgithub.com")));
+
+ assertThat(parser.findRemoteServer("github.com").getUris())
+ .containsExactlyInAnyOrder(URI.create("https://github.com"), URI.create("ssh://git@github.com"));
+ }
+
@Test
void findRemote() {
GitRemote.Parser parser = new GitRemote.Parser()
.registerRemote(GitRemote.Service.Bitbucket, URI.create("scm.company.com/stash"), Collections.emptyList());
assertThat(parser.findRemoteServer("github.com").getService()).isEqualTo(GitRemote.Service.GitHub);
+ assertThat(parser.findRemoteServer("https://github.com").getService()).isEqualTo(GitRemote.Service.GitHub);
assertThat(parser.findRemoteServer("gitlab.com").getService()).isEqualTo(GitRemote.Service.GitLab);
assertThat(parser.findRemoteServer("bitbucket.org").getService()).isEqualTo(GitRemote.Service.BitbucketCloud);
assertThat(parser.findRemoteServer("dev.azure.com").getService()).isEqualTo(GitRemote.Service.AzureDevOps);
@@ -265,18 +275,18 @@ void parseOriginCaseInsensitive(String cloneUrl, String expectedOrigin, String e
@ParameterizedTest
@CsvSource(textBlock = """
- GitHub, GitHub
- GITLAB, GitLab
- bitbucket, Bitbucket
- BitbucketCloud, BitbucketCloud
- Bitbucket Cloud, BitbucketCloud
- BITBUCKET_CLOUD, BitbucketCloud
- AzureDevOps, AzureDevOps
- AZURE_DEVOPS, AzureDevOps
- Azure DevOps, AzureDevOps
- idontknow, Unknown
- """)
- void findServiceForName(String name, GitRemote.Service service){
+ GitHub, GitHub
+ GITLAB, GitLab
+ bitbucket, Bitbucket
+ BitbucketCloud, BitbucketCloud
+ Bitbucket Cloud, BitbucketCloud
+ BITBUCKET_CLOUD, BitbucketCloud
+ AzureDevOps, AzureDevOps
+ AZURE_DEVOPS, AzureDevOps
+ Azure DevOps, AzureDevOps
+ idontknow, Unknown
+ """)
+ void findServiceForName(String name, GitRemote.Service service) {
assertThat(GitRemote.Service.forName(name)).isEqualTo(service);
}
diff --git a/rewrite-core/src/test/java/org/openrewrite/internal/ReflectionUtilsTest.java b/rewrite-core/src/test/java/org/openrewrite/internal/ReflectionUtilsTest.java
new file mode 100644
index 00000000000..83eb1763c75
--- /dev/null
+++ b/rewrite-core/src/test/java/org/openrewrite/internal/ReflectionUtilsTest.java
@@ -0,0 +1,41 @@
+/*
+ * 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.internal;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class ReflectionUtilsTest {
+
+ @Test
+ void classAvailable() {
+ boolean result = ReflectionUtils.isClassAvailable("org.openrewrite.internal.ReflectionUtilsTest");
+ assertTrue(result);
+ }
+
+ @Test
+ void classNotAvailable() {
+ boolean result = ReflectionUtils.isClassAvailable("org.openrewrite.internal.ReflectionUtilsTest2");
+ assertFalse(result);
+ }
+
+ @Test
+ void classNotAvailableWhenFQNOmitted() {
+ boolean result = ReflectionUtils.isClassAvailable("ReflectionUtilsTest");
+ assertFalse(result);
+ }
+}
diff --git a/rewrite-core/src/test/java/org/openrewrite/remote/RemoteArchiveTest.java b/rewrite-core/src/test/java/org/openrewrite/remote/RemoteArchiveTest.java
index f96eea97f00..e09fc8454f5 100644
--- a/rewrite-core/src/test/java/org/openrewrite/remote/RemoteArchiveTest.java
+++ b/rewrite-core/src/test/java/org/openrewrite/remote/RemoteArchiveTest.java
@@ -21,10 +21,12 @@
import org.openrewrite.ExecutionContext;
import org.openrewrite.HttpSenderExecutionContextView;
import org.openrewrite.InMemoryExecutionContext;
+import org.openrewrite.PrintOutputCapture;
import org.openrewrite.test.MockHttpSender;
import java.io.IOException;
import java.io.InputStream;
+import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Paths;
import java.util.concurrent.*;
@@ -40,10 +42,6 @@ class RemoteArchiveTest {
void gradleWrapper(String version) throws Exception {
URL distributionUrl = requireNonNull(RemoteArchiveTest.class.getClassLoader().getResource("gradle-" + version + "-bin.zip"));
ExecutionContext ctx = new InMemoryExecutionContext();
- RemoteExecutionContextView.view(ctx).setArtifactCache(new LocalRemoteArtifactCache(
- Paths.get(System.getProperty("user.home") + "/.rewrite/remote/gradleWrapper")));
- HttpSenderExecutionContextView.view(ctx)
- .setLargeFileHttpSender(new MockHttpSender(distributionUrl::openStream));
RemoteArchive remoteArchive = Remote
.builder(
@@ -58,10 +56,9 @@ void gradleWrapper(String version) throws Exception {
@Test
void gradleWrapperDownloadFails() throws Exception {
- URL distributionUrl = requireNonNull(RemoteArchiveTest.class.getClassLoader().getResource("gradle-7.4.2-bin.zip"));
+ URL distributionUrl = new URL("http://example");
ExecutionContext ctx = new InMemoryExecutionContext();
- RemoteExecutionContextView.view(ctx).setArtifactCache(new LocalRemoteArtifactCache(
- Paths.get(System.getProperty("user.home") + "/.rewrite/remote/gradleWrapperDownloadFails")));
+
HttpSenderExecutionContextView.view(ctx)
.setLargeFileHttpSender(new MockHttpSender(408));
@@ -116,6 +113,21 @@ void gradleWrapperConcurrent(String version) throws Exception {
executorService.shutdown();
}
+ @Test
+ void printingRemoteArchive() throws URISyntaxException {
+ URL zipUrl = requireNonNull(RemoteArchiveTest.class.getClassLoader().getResource("zipfile.zip"));
+
+ RemoteArchive remoteArchive = Remote
+ .builder(
+ Paths.get("content.txt"),
+ zipUrl.toURI()
+ )
+ .build("content.txt");
+
+ String printed = remoteArchive.printAll(new PrintOutputCapture<>(0, PrintOutputCapture.MarkerPrinter.DEFAULT));
+ assertThat(printed).isEqualTo("this is a zipped file");
+ }
+
private Long getInputStreamSize(InputStream is) {
BlackHoleOutputStream out = new BlackHoleOutputStream();
try {
diff --git a/rewrite-core/src/test/java/org/openrewrite/remote/RemoteFileTest.java b/rewrite-core/src/test/java/org/openrewrite/remote/RemoteFileTest.java
index 6e335a1eee3..de1b23476c8 100644
--- a/rewrite-core/src/test/java/org/openrewrite/remote/RemoteFileTest.java
+++ b/rewrite-core/src/test/java/org/openrewrite/remote/RemoteFileTest.java
@@ -55,10 +55,8 @@ void gradleWrapperProperties() throws Exception {
@Test
void gradleWrapperDownloadFails() throws Exception {
- URL distributionUrl = requireNonNull(RemoteFileTest.class.getClassLoader().getResource("gradle-wrapper.properties"));
+ URL distributionUrl = new URL("http://example.com");
ExecutionContext ctx = new InMemoryExecutionContext();
- RemoteExecutionContextView.view(ctx).setArtifactCache(new LocalRemoteArtifactCache(
- Paths.get(System.getProperty("user.home") + "/.rewrite/remote/gradleWrapperDownloadFails")));
HttpSenderExecutionContextView.view(ctx)
.setLargeFileHttpSender(new MockHttpSender(408));
diff --git a/rewrite-core/src/test/java/org/openrewrite/semver/LatestMinorTest.java b/rewrite-core/src/test/java/org/openrewrite/semver/LatestMinorTest.java
new file mode 100644
index 00000000000..ef551e1d882
--- /dev/null
+++ b/rewrite-core/src/test/java/org/openrewrite/semver/LatestMinorTest.java
@@ -0,0 +1,38 @@
+/*
+ * 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.semver;
+
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class LatestMinorTest {
+ private final LatestMinor latestMinor = new LatestMinor(null);
+
+ @Test
+ void isValidWhenCurrentIsNull() {
+ assertThat(latestMinor.isValid(null, "1.0.0")).isTrue();
+ }
+ @Test
+ void isValid() {
+ assertThat(latestMinor.isValid("1.0.0", "1.0.0")).isTrue();
+ assertThat(latestMinor.isValid("1.0.0", "1.0.0.1")).isTrue();
+ assertThat(latestMinor.isValid("1.0.0", "1.0.1")).isTrue();
+ assertThat(latestMinor.isValid("1.0", "1.0.1")).isTrue();
+ assertThat(latestMinor.isValid("1.0.0", "1.1.0")).isTrue();
+ assertThat(latestMinor.isValid("1.0.0", "2.0.0")).isFalse();
+ }
+}
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 0bfae8cadbb..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(
"""
@@ -111,4 +111,136 @@ void caseInsensitive() {
)
);
}
+
+ @Test
+ void regexBasicMultiLine() {
+ rewriteRun(
+ spec -> spec.recipe(new Find("[T\\s]", true, true, true, null, null, null)),
+ text(
+ """
+ This is\ttext.
+ This is\ttext.
+ """,
+ """
+ ~~>This~~> is~~>\ttext.~~>
+ ~~>This~~> is~~>\ttext.
+ """
+ )
+ );
+ }
+
+ @Test
+ void regexWithoutMultilineAndDotall() {
+ rewriteRun(
+ spec -> spec.recipe(new Find("^This.*below\\.$", true, true, false, false, null, null)),
+ text(
+ """
+ This is text.
+ This is a line below.
+ This is a line above.
+ This is text.
+ This is a line below.
+ """
+ )
+ );
+ }
+
+ @Test
+ void regexMatchingWhitespaceWithoutMultilineWithDotall() {
+ rewriteRun(
+ 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
+ """
+ Zero
+ One Two
+ Three
+ """
+ )
+ );
+ }
+
+ @Test
+ void regexWithoutMultilineAndWithDotAll() {
+ rewriteRun(
+ spec -> spec.recipe(new Find("^This.*below\\.$", true, true, false, true, null, null)),
+ text(
+ """
+ This is text.
+ This is a line below.
+ This is a line above.
+ This is text.
+ This is a line below.
+ """,
+ """
+ ~~>This is text.
+ This is a line below.
+ This is a line above.
+ This is text.
+ This is a line below.
+ """
+ )
+ );
+ }
+
+ @Test
+ void regexWithMultilineAndWithoutDotall() {
+ rewriteRun(
+ spec -> spec.recipe(new Find("^This.*below\\.$", true, true, true, false, null, null)),
+ text(
+ """
+ This is text.
+ This is a line below.
+ This is a line above.
+ This is text.
+ This is a line below.
+ """,
+ """
+ This is text.
+ ~~>This is a line below.
+ This is a line above.
+ This is text.
+ ~~>This is a line below.
+ """
+ )
+ );
+ }
+
+ @Test
+ void regexWithBothMultilineAndDotAll() {
+ rewriteRun(
+ spec -> spec.recipe(new Find("^This.*below\\.$", true, true, true, true, null, null)),
+ text(
+ """
+ The first line.
+ This is a line below.
+ This is a line above.
+ This is text.
+ This is a line below.
+ """,
+ """
+ The first line.
+ ~~>This is a line below.
+ This is a line above.
+ This is text.
+ This is a line below.
+ """
+ )
+ );
+ }
+
+ @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-core/src/test/resources/zipfile.zip b/rewrite-core/src/test/resources/zipfile.zip
new file mode 100644
index 00000000000..028830c420e
Binary files /dev/null and b/rewrite-core/src/test/resources/zipfile.zip differ
diff --git a/rewrite-gradle/build.gradle.kts b/rewrite-gradle/build.gradle.kts
index 9f18146b3b0..9df2c9c9477 100644
--- a/rewrite-gradle/build.gradle.kts
+++ b/rewrite-gradle/build.gradle.kts
@@ -57,6 +57,7 @@ dependencies {
testImplementation(project(":rewrite-test")) {
// because gradle-api fatjars this implementation already
exclude("ch.qos.logback", "logback-classic")
+ exclude("org.slf4j", "slf4j-nop")
}
testImplementation("org.openrewrite.gradle.tooling:model:$latest")
diff --git a/rewrite-gradle/src/main/groovy/RewriteGradleProject.groovy b/rewrite-gradle/src/main/groovy/RewriteGradleProject.groovy
index e3283fd5733..ee780d3d430 100644
--- a/rewrite-gradle/src/main/groovy/RewriteGradleProject.groovy
+++ b/rewrite-gradle/src/main/groovy/RewriteGradleProject.groovy
@@ -78,6 +78,7 @@ interface DependencyHandlerSpec extends DependencyHandler {
Dependency earlib(Object... dependencyNotation)
Dependency earlib(Object dependencyNotation, @DelegatesTo(strategy=Closure.DELEGATE_ONLY, value= ExternalDependency) Closure closure)
+ @Override
void constraints(Action super DependencyConstraintHandler> configureAction)
}
diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/AddDependency.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/AddDependency.java
index 990a0606d65..59084bd7cec 100644
--- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/AddDependency.java
+++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/AddDependency.java
@@ -34,6 +34,7 @@
import java.util.*;
+import static java.util.Collections.*;
import static java.util.Objects.requireNonNull;
@Value
@@ -171,14 +172,14 @@ private boolean usesType(SourceFile sourceFile, ExecutionContext ctx) {
return tree;
}
SourceFile sourceFile = (SourceFile) tree;
- sourceFile.getMarkers().findFirst(JavaProject.class).ifPresent(javaProject ->
- sourceFile.getMarkers().findFirst(JavaSourceSet.class).ifPresent(sourceSet -> {
- if (usesType(sourceFile, ctx)) {
- acc.usingType = true;
- }
- Set configurations = acc.configurationsByProject.computeIfAbsent(javaProject, ignored -> new HashSet<>());
- configurations.add("main".equals(sourceSet.getName()) ? "implementation" : sourceSet.getName() + "Implementation");
- }));
+ sourceFile.getMarkers().findFirst(JavaProject.class).ifPresent(javaProject -> {
+ if (usesType(sourceFile, ctx)) {
+ acc.usingType = true;
+ }
+ Set configurations = acc.configurationsByProject.computeIfAbsent(javaProject, ignored -> new HashSet<>());
+ sourceFile.getMarkers().findFirst(JavaSourceSet.class).ifPresent(sourceSet ->
+ configurations.add("main".equals(sourceSet.getName()) ? "implementation" : sourceSet.getName() + "Implementation"));
+ });
return tree;
}
};
@@ -216,7 +217,12 @@ public TreeVisitor, ExecutionContext> getVisitor(Scanned acc) {
GradleProject gp = maybeGp.get();
- Set resolvedConfigurations = StringUtils.isBlank(configuration) ? acc.configurationsByProject.get(jp) : new HashSet<>(Collections.singletonList(configuration));
+ Set resolvedConfigurations = StringUtils.isBlank(configuration) ?
+ acc.configurationsByProject.getOrDefault(jp, new HashSet<>()) :
+ new HashSet<>(singletonList(configuration));
+ if (resolvedConfigurations.isEmpty()) {
+ resolvedConfigurations.add("implementation");
+ }
Set tmpConfigurations = new HashSet<>(resolvedConfigurations);
for (String tmpConfiguration : tmpConfigurations) {
GradleDependencyConfiguration gdc = gp.getConfiguration(tmpConfiguration);
diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/AddDependencyVisitor.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/AddDependencyVisitor.java
index 61659730458..64e58977947 100644
--- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/AddDependencyVisitor.java
+++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/AddDependencyVisitor.java
@@ -290,22 +290,10 @@ public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, Execu
}
} else {
Space originalPrefix = addDependencyInvocation.getPrefix();
- if (currentStatement instanceof J.VariableDeclarations) {
- J.VariableDeclarations variableDeclarations = (J.VariableDeclarations) currentStatement;
- if (variableDeclarations.getTypeExpression() != null) {
- addDependencyInvocation = addDependencyInvocation.withPrefix(variableDeclarations.getTypeExpression().getPrefix());
- }
- } else {
- addDependencyInvocation = addDependencyInvocation.withPrefix(currentStatement.getPrefix());
- }
+ addDependencyInvocation = addDependencyInvocation.withPrefix(currentStatement.getPrefix());
if (addDependencyInvocation.getSimpleName().equals(beforeDependency.getSimpleName())) {
- if (currentStatement instanceof J.VariableDeclarations) {
- J.VariableDeclarations variableDeclarations = (J.VariableDeclarations) currentStatement;
- if (variableDeclarations.getTypeExpression() != null && !variableDeclarations.getTypeExpression().getPrefix().equals(originalPrefix)) {
- statements.set(i, variableDeclarations.withTypeExpression(variableDeclarations.getTypeExpression().withPrefix(originalPrefix)));
- }
- } else if (!currentStatement.getPrefix().equals(originalPrefix)) {
+ if (!currentStatement.getPrefix().equals(originalPrefix)) {
statements.set(i, currentStatement.withPrefix(originalPrefix));
}
}
diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependency.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependency.java
index 0ff50885dee..9312fa96c3b 100644
--- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependency.java
+++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependency.java
@@ -180,14 +180,16 @@ public G.CompilationUnit visitCompilationUnit(G.CompilationUnit cu, ExecutionCon
public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) {
J.MethodInvocation m = super.visitMethodInvocation(method, ctx);
- GradleDependency.Matcher gradleDependencyMatcher = new GradleDependency.Matcher();
+ GradleDependency.Matcher gradleDependencyMatcher = new GradleDependency.Matcher()
+ .groupId(oldGroupId)
+ .artifactId(oldArtifactId);
if (!gradleDependencyMatcher.get(getCursor()).isPresent()) {
return m;
}
List depArgs = m.getArguments();
- if (depArgs.get(0) instanceof J.Literal || depArgs.get(0) instanceof G.GString || depArgs.get(0) instanceof G.MapEntry) {
+ if (depArgs.get(0) instanceof J.Literal || depArgs.get(0) instanceof G.GString || depArgs.get(0) instanceof G.MapEntry || depArgs.get(0) instanceof G.MapLiteral) {
m = updateDependency(m, ctx);
} else if (depArgs.get(0) instanceof J.MethodInvocation &&
(((J.MethodInvocation) depArgs.get(0)).getSimpleName().equals("platform") ||
@@ -204,7 +206,7 @@ private J.MethodInvocation updateDependency(J.MethodInvocation m, ExecutionConte
String gav = (String) ((J.Literal) depArgs.get(0)).getValue();
if (gav != null) {
Dependency original = DependencyStringNotationConverter.parse(gav);
- if (original != null && depMatcher.matches(original.getGroupId(), original.getArtifactId())) {
+ if (original != null) {
Dependency updated = original;
if (!StringUtils.isBlank(newGroupId) && !updated.getGroupId().equals(newGroupId)) {
updated = updated.withGroupId(newGroupId);
@@ -238,7 +240,7 @@ private J.MethodInvocation updateDependency(J.MethodInvocation m, ExecutionConte
J.Literal literal = (J.Literal) strings.get(0);
Dependency original = DependencyStringNotationConverter.parse((String) requireNonNull(literal.getValue()));
- if (original != null && depMatcher.matches(original.getGroupId(), original.getArtifactId())) {
+ if (original != null) {
Dependency updated = original;
if (!StringUtils.isBlank(newGroupId) && !updated.getGroupId().equals(newGroupId)) {
updated = updated.withGroupId(newGroupId);
@@ -352,6 +354,92 @@ private J.MethodInvocation updateDependency(J.MethodInvocation m, ExecutionConte
return arg;
}));
}
+ } else if (m.getArguments().get(0) instanceof G.MapLiteral) {
+ G.MapLiteral map = (G.MapLiteral) depArgs.get(0);
+ G.MapEntry groupEntry = null;
+ G.MapEntry artifactEntry = null;
+ G.MapEntry versionEntry = null;
+ String groupId = null;
+ String artifactId = null;
+ String version = null;
+
+ for (G.MapEntry arg : map.getElements()) {
+ if (!(arg.getKey() instanceof J.Literal) || !(arg.getValue() instanceof J.Literal)) {
+ continue;
+ }
+ J.Literal key = (J.Literal) arg.getKey();
+ J.Literal value = (J.Literal) arg.getValue();
+ if (!(key.getValue() instanceof String) || !(value.getValue() instanceof String)) {
+ continue;
+ }
+ String keyValue = (String) key.getValue();
+ String valueValue = (String) value.getValue();
+ switch (keyValue) {
+ case "group":
+ groupEntry = arg;
+ groupId = valueValue;
+ break;
+ case "name":
+ artifactEntry = arg;
+ artifactId = valueValue;
+ break;
+ case "version":
+ versionEntry = arg;
+ version = valueValue;
+ break;
+ }
+ }
+ if (groupId == null || artifactId == null) {
+ return m;
+ }
+ if (!depMatcher.matches(groupId, artifactId)) {
+ return m;
+ }
+ String updatedGroupId = groupId;
+ if (!StringUtils.isBlank(newGroupId) && !updatedGroupId.equals(newGroupId)) {
+ updatedGroupId = newGroupId;
+ }
+ String updatedArtifactId = artifactId;
+ if (!StringUtils.isBlank(newArtifactId) && !updatedArtifactId.equals(newArtifactId)) {
+ updatedArtifactId = newArtifactId;
+ }
+ String updatedVersion = version;
+ if (!StringUtils.isBlank(newVersion) && (!StringUtils.isBlank(version) || Boolean.TRUE.equals(overrideManagedVersion))) {
+ String resolvedVersion;
+ try {
+ resolvedVersion = new DependencyVersionSelector(null, gradleProject, null)
+ .select(new GroupArtifact(updatedGroupId, updatedArtifactId), m.getSimpleName(), newVersion, versionPattern, ctx);
+ } catch (MavenDownloadingException e) {
+ return e.warn(m);
+ }
+ if (resolvedVersion != null && !resolvedVersion.equals(updatedVersion)) {
+ updatedVersion = resolvedVersion;
+ }
+ }
+
+ if (!updatedGroupId.equals(groupId) || !updatedArtifactId.equals(artifactId) || updatedVersion != null && !updatedVersion.equals(version)) {
+ G.MapEntry finalGroup = groupEntry;
+ String finalGroupIdValue = updatedGroupId;
+ G.MapEntry finalArtifact = artifactEntry;
+ String finalArtifactIdValue = updatedArtifactId;
+ G.MapEntry finalVersion = versionEntry;
+ String finalVersionValue = updatedVersion;
+ m = m.withArguments(ListUtils.mapFirst(m.getArguments(), arg -> {
+ G.MapLiteral mapLiteral = (G.MapLiteral) arg;
+ return mapLiteral.withElements(ListUtils.map(mapLiteral.getElements(), e -> {
+ if (e == finalGroup) {
+ return finalGroup.withValue(ChangeStringLiteral.withStringValue((J.Literal) finalGroup.getValue(), finalGroupIdValue));
+ }
+ if (e == finalArtifact) {
+ return finalArtifact.withValue(ChangeStringLiteral.withStringValue((J.Literal) finalArtifact.getValue(), finalArtifactIdValue));
+ }
+ if (e == finalVersion) {
+ return finalVersion.withValue(ChangeStringLiteral.withStringValue((J.Literal) finalVersion.getValue(), finalVersionValue));
+ }
+ return e;
+ }));
+ }));
+ }
}
return m;
diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependencyArtifactId.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependencyArtifactId.java
index ba98f950ca9..02e028fd305 100755
--- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependencyArtifactId.java
+++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependencyArtifactId.java
@@ -25,21 +25,18 @@
import org.openrewrite.gradle.util.ChangeStringLiteral;
import org.openrewrite.gradle.util.Dependency;
import org.openrewrite.gradle.util.DependencyStringNotationConverter;
-import org.openrewrite.groovy.GroovyVisitor;
+import org.openrewrite.groovy.GroovyIsoVisitor;
import org.openrewrite.groovy.tree.G;
import org.openrewrite.internal.ListUtils;
import org.openrewrite.internal.StringUtils;
-import org.openrewrite.java.MethodMatcher;
import org.openrewrite.java.tree.Expression;
import org.openrewrite.java.tree.J;
-import org.openrewrite.maven.tree.GroupArtifact;
-import org.openrewrite.maven.tree.ResolvedDependency;
import org.openrewrite.semver.DependencyMatcher;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.stream.Collectors;
+import java.util.Optional;
import static java.util.Objects.requireNonNull;
@@ -90,38 +87,42 @@ public Validated