8000 Improve `LatestRelease#compare()` by knutwannheden · Pull Request #5666 · openrewrite/rewrite · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Improve LatestRelease#compare() #5666

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jun 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public class ExactVersion extends LatestRelease {
String version;

public ExactVersion(String pattern) {
super(pattern);
super(null);
if (pattern.startsWith("=")) {
this.version = pattern.substring(1);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.util.regex.Matcher;

public class LatestRelease implements VersionComparator {

@Nullable
private final String metadataPattern;

Expand Down Expand Up @@ -116,18 +117,19 @@ public int compare(@Nullable String currentVersion, String v1, String v2) {
nv1 = nv1Builder.toString();
}

Matcher v1Gav = VersionComparator.RELEASE_PATTERN.matcher(nv1);
Matcher v2Gav = VersionComparator.RELEASE_PATTERN.matcher(nv2);

v1Gav.find();
v2Gav.find();

// Remove the metadata pattern from the normalized versions, this only impacts the comparison when all version
// parts are the same:
//
// HyphenRange [25-28] should include "28-jre" and "28-android" as possible candidates.
String normalized1 = metadataPattern == null ? nv1 : nv1.replaceAll(metadataPattern, "");
String normalized2 = metadataPattern == null ? nv2 : nv2.replaceAll(metadataPattern, "");

Matcher v1Gav = VersionComparator.RELEASE_PATTERN.matcher(normalized1);
Matcher v2Gav = VersionComparator.RELEASE_PATTERN.matcher(normalized2);

v1Gav.find();
v2Gav.find();

try {
for (int i = 1; i <= Math.max(vp1, vp2); i++) {
String v1Part = v1Gav.group(i);
Expand All @@ -152,19 +154,61 @@ public int compare(@Nullable String currentVersion, String v1, String v2) {
// When all numeric parts are equal, we need to handle pre-release versions properly
// A pre-release version should be considered less than a release version
// e.g., "3.5.0-RC1" < "3.5.0"
boolean v1IsPreRelease = v1Gav.group(6) != null && PRE_RELEASE_ENDING.matcher(v1Gav.group(6)).find();
boolean v2IsPreRelease = v2Gav.group(6) != null && PRE_RELEASE_ENDING.matcher(v2Gav.group(6)).find();

if (v1IsPreRelease && !v2IsPreRelease) {
return -1; // v1 is pre-release, v2 is not, so v1 < v2
} else if (!v1IsPreRelease && v2IsPreRelease) {
return 1; // v1 is not pre-release, v2 is, so v1 > v2
int v1Prio = qualifierPriority(v1Gav.group(6));
int v2Prio = qualifierPriority(v2Gav.group(6));

if (v1Prio != v2Prio) {
return Integer.compare(v1Prio, v2Prio);
}

// Both are either pre-release or release versions, do string comparison
return normalized1.compareTo(normalized2);
}

private static int qualifierPriority(@Nullable String suffix) {
String qualifier = extractQualifier(suffix);
switch (qualifier) {
case "alpha":
case "a":
return 1;
case "beta":
case "b":
return 2;
case "milestone":
case "m":
return 3;
case "rc":
case "cr":
return 4;
case "snapshot":
return 5;
case "":
case "ga":
case "final":
case "release":
return 6;
case "sp":
return 7;
default:
return 8;
}
}

private static String extractQualifier(@Nullable String suffix) {
if (suffix == null) {
return "";
}
StringBuilder builder = new StringBuilder();
for (int i = 1; i < suffix.length(); i++) {
if (Character.isLetter(suffix.charAt(i))) {
builder.append(Character.toLowerCase(suffix.charAt(i)));
} else {
break;
}
}
return builder.toString();
}

public static Validated<LatestRelease> buildLatestRelease(String toVersion, @Nullable String metadataPattern) {
return "latest.release".equalsIgnoreCase(toVersion) || "latest.major".equalsIgnoreCase(toVersion) ?
Validated.valid("latestRelease", new LatestRelease(metadataPattern)) :
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,25 +48,25 @@ void noSnapshots() {
@Test
void upgrade() {
var upgrade = latestPatch.upgrade("2.10.10.3.24", List.of("2.10.0"));
assertThat(upgrade.isPresent()).isFalse();
assertThat(upgrade).isEmpty();

upgrade = latestPatch.upgrade("2.10.10.3.24", List.of("2.11.0"));
assertThat(upgrade.isPresent()).isFalse();
assertThat(upgrade).isEmpty();
upgrade = latestPatch.upgrade("2.10.10.3.24", List.of("2.10.9"));
assertThat(upgrade.isPresent()).isFalse();
assertThat(upgrade).isEmpty();

upgrade = latestPatch.upgrade("2.10.10.3.24", List.of("2.10.11"));
assertThat(upgrade.isPresent()).isTrue();
assertThat(upgrade).isPresent();
assertThat(upgrade.get()).isEqualTo("2.10.11");

upgrade = latestPatch.upgrade("2.10.10.3.24", List.of("2.10.10.3.23"));
assertThat(upgrade.isPresent()).isFalse();
assertThat(upgrade).isEmpty();

upgrade = latestPatch.upgrade("2.10.10.3.24", List.of("2.10.10.2.25"));
assertThat(upgrade.isPresent()).isFalse();
assertThat(upgrade).isEmpty();

upgrade = latestPatch.upgrade("2.10.10.3.24", List.of("2.10.10.3.25"));
assertThat(upgrade.isPresent()).isTrue();
assertThat(upgrade).isPresent();
assertThat(upgrade.get()).isEqualTo("2.10.10.3.25");
}

Expand All @@ -86,25 +86,25 @@ void metadataValid() {
@Test
void metadataUpgrade() {
var upgrade = latestMetadataPatch.upgrade("2.10.10.3.24-fred", List.of("2.10.0-fred"));
assertThat(upgrade.isPresent()).isFalse();
assertThat(upgrade).isEmpty();

upgrade = latestMetadataPatch.upgrade("2.10.10.3.24-fred", List.of("2.11.0-fred"));
assertThat(upgrade.isPresent()).isFalse();
assertThat(upgrade).isEmpty();
upgrade = latestMetadataPatch.upgrade("2.10.10.3.24-fred", List.of("2.10.9-fred"));
assertThat(upgrade.isPresent()).isFalse();
assertThat(upgrade).isEmpty();

upgrade = latestMetadataPatch.upgrade("2.10.10.3.24-fred", List.of("2.10.11-fred"));
assertThat(upgrade.isPresent()).isTrue();
assertThat(upgrade).isPresent();
assertThat(upgrade.get()).isEqualTo("2.10.11-fred");

upgrade = latestMetadataPatch.upgrade("2.10.10.3.24-fred", List.of("2.10.10.3.23-fred"));
assertThat(upgrade.isPresent()).isFalse();
assertThat(upgrade).isEmpty();

upgrade = latestMetadataPatch.upgrade("2.10.10.3.24-fred", List.of("2.10.10.2.25-fred"));
assertThat(upgrade.isPresent()).isFalse();
assertThat(upgrade).isEmpty();

upgrade = latestMetadataPatch.upgrade("2.10.10.3.24-fred", List.of("2.10.10.3.25-fred"));
assertThat(upgrade.isPresent()).isTrue();
assertThat(upgrade).isPresent();
assertThat(upgrade.get()).isEqualTo("2.10.10.3.25-fred");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,22 @@ void releaseOrLatestKeyword_beatsEverything() {
assertThat(latestRelease.compare(null, "reLeaSE", "1.2.3")).isPositive();
}

@Test
void compareQualifiers() {
assertThat(latestRelease.compare(null, "1.2.3.final", "1.2.3")).isEqualTo(0);
assertThat(latestRelease.compare(null, "1.2.3.ga", "1.2.3")).isEqualTo(0);
assertThat(latestRelease.compare(null, "1.2.3.release", "1.2.3")).isEqualTo(0);

assertThat(latestRelease.compare(null, "1.2.3.alpha-1", "1.2.3")).isNegative();
assertThat(latestRelease.compare(null, "1.2.3.alpha-1", "1.2.3.alpha-2")).isNegative();
assertThat(latestRelease.compare(null, "1.2.3.m", "1.2.3")).isNegative();

assertThat(latestRelease.compare(null, "1.2.3.sp", "1.2.3")).isPositive();
assertThat(latestRelease.compare(null, "1.2.3.sp", "1.2.3.final")).isPositive();

assertThat(latestRelease.compare(null, "1.2.3.mr", "1.2.3")).isPositive();
}

@Test
void latestKeywordIsNewerThanReleaseKeyword() {
assertThat(latestRelease.compare(null, "RELEASE", "LATEST")).isNegative();
Expand Down Expand Up @@ -180,8 +196,8 @@ void preReleaseVersionsShouldBeLessThanReleaseVersions() {
assertThat(latestRelease.compare(null, "3.5.0", "3.5.0-RC1")).isGreaterThan(0);
assertThat(latestRelease.compare(null, "3.5.0-RC1", "3.5.0-RC2")).isLessThan(0);
assertThat(latestRelease.compare(null, "3.5.0-alpha", "3.5.0-beta")).isLessThan(0);
// String comparison: "beta" > "RC1" lexicographically
assertThat(latestRelease.compare(null, "3.5.0-beta", "3.5.0-RC1")).isGreaterThan(0);
assertThat(latestRelease.compare(null, "3.5.0-RC1", "3.5.0-beta")).isLessThan(0);
// "RC" is a known qualifier and "beta" is not
assertThat(latestRelease.compare(null, "3.5.0-beta", "3.5.0-RC1")).isLessThan(0);
assertThat(latestRelease.compare(null, "3.5.0-RC1", "3.5.0-beta")).isGreaterThan(0);
}
}
0