From 3bfe32877da69c7a4d67e0c92a0660d094ccb3c1 Mon Sep 17 00:00:00 2001 From: "James R. Perkins" Date: Fri, 22 Mar 2024 10:19:43 -0700 Subject: [PATCH 1/2] [17] Add an option to ignore the SNAPSHOT extension when comparing versions. Signed-off-by: James R. Perkins --- .../plugin/tools/VersionComparator.java | 148 ++++++++++++++---- .../wildfly/plugin/tools/VersionTestCase.java | 14 +- 2 files changed, 133 insertions(+), 29 deletions(-) diff --git a/src/main/java/org/wildfly/plugin/tools/VersionComparator.java b/src/main/java/org/wildfly/plugin/tools/VersionComparator.java index ab060a2..8176bf8 100644 --- a/src/main/java/org/wildfly/plugin/tools/VersionComparator.java +++ b/src/main/java/org/wildfly/plugin/tools/VersionComparator.java @@ -6,22 +6,88 @@ package org.wildfly.plugin.tools; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; +import java.util.Collection; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Objects; /** - * Compares two versions. + * Compares two versions. The comparison is case-insensitive. + *

+ * Some qualifiers map to other qualifiers. Below is a table of those mappings. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
QualifierMapping
GAFinal
aAlpha
bBeta
mMilestone
crrc
+ *

* * @author James R. Perkins */ public class VersionComparator implements Comparator { private static final VersionComparator INSTANCE = new VersionComparator(); + private static final VersionComparator IGNORE_INSTANCE = new VersionComparator(true); + + private final boolean ignoreSnapshots; + + /** + * Creates a new version comparator. + */ + public VersionComparator() { + this(false); + } + + private VersionComparator(final boolean ignoreSnapshots) { + this.ignoreSnapshots = ignoreSnapshots; + } + + /** + * Returns an instance of a version comparator. + * + * @return a version comparator instance + */ + public static VersionComparator getInstance() { + return getInstance(false); + } + + /** + * Returns an instance of a version comparator which optionally ignore the SNAPSHOT release extension. This can + * be useful for cases where you want to compare a version is at least a base version, not caring if it's a + * SNAPSHOT. + * + * @param ignoreSnapshots {@code true} to ignore the SNAPSHOT release extension, otherwise {@code false} which + * values a SNAPSHOT dependency less than a non-SNAPSHOT of the same version + * + * @return a version comparator instance + */ + public static VersionComparator getInstance(final boolean ignoreSnapshots) { + return ignoreSnapshots ? IGNORE_INSTANCE : INSTANCE; + } /** * Compares the first version against the second version. @@ -35,14 +101,41 @@ public class VersionComparator implements Comparator { * @see Comparator#compare(Object, Object) */ public static int compareVersion(final String v1, final String v2) { - return INSTANCE.compare(v1, v2); + return compareVersion(false, v1, v2); + } + + /** + * Compares the first version against the second version optionally ignoring if either version has a SNAPSHOT + * release extension. This can be useful for cases where you want to compare a version is at least a base version, + * not caring if it's a SNAPSHOT. + * + *

+ * If {@code ignoreSnapshots} is {@code true}, the version {@code 1.0.0.Final} and {@code 1.0.0.Final-SNAPSHOT} are + * said to be equal. If set to {@code false}, {@code 1.0.0.Final} is greater than {@code 1.0.0.Final-SNAPSHOT}. + *

+ * + * @param ignoreSnapshots {@code true} to ignore the SNAPSHOT release extension, otherwise {@code false} which + * values a SNAPSHOT dependency less than a non-SNAPSHOT of the same version + * @param v1 the first version + * @param v2 the second version + * + * @return {@code 0} if the versions are equal, {@code -1} if version first version is less than the second version + * or {@code 1} if the first version is greater than the second version + */ + public static int compareVersion(final boolean ignoreSnapshots, final String v1, final String v2) { + // If the strings are equal ignoring the case, we can assume these are equal + if (Objects.requireNonNull(v1).equalsIgnoreCase(v2)) { + return 0; + } + final Version version1 = Version.parse(v1, ignoreSnapshots); + final Version version2 = Version.parse(Objects.requireNonNull(v2), ignoreSnapshots); + // Ensures the result is always 0, 1 or -1 + return Integer.compare(version1.compareTo(version2), 0); } @Override public int compare(final String o1, final String o2) { - final Version v1 = Version.parse(o1); - final Version v2 = Version.parse(o2); - return v1.compareTo(v2); + return compareVersion(ignoreSnapshots, o1, o2); } private enum ReleaseType { @@ -68,7 +161,7 @@ private enum ReleaseType { map.put(alias, r); } } - ENTRIES = Collections.unmodifiableMap(map); + ENTRIES = Map.copyOf(map); } private final String type; @@ -76,14 +169,11 @@ private enum ReleaseType { ReleaseType(final String type, final String... aliases) { this.type = type; - this.aliases = Collections.unmodifiableList(Arrays.asList(aliases)); + this.aliases = List.of(aliases); } static ReleaseType find(final String s) { - if (ENTRIES.containsKey(s)) { - return ENTRIES.get(s); - } - return UNKNOWN; + return ENTRIES.getOrDefault(s, UNKNOWN); } } @@ -96,7 +186,7 @@ private Version(final String original, final List parts) { this.parts = parts; } - public static Version parse(final String version) { + static Version parse(final String version, final boolean ignoreSnapshot) { final List parts = new ArrayList<>(); final StringBuilder sb = new StringBuilder(); boolean isDigit = false; @@ -107,7 +197,7 @@ public static Version parse(final String version) { if (isDigit) { parts.add(new IntegerPart(Integer.parseInt(sb.toString()))); } else { - parts.add(new StringPart(sb.toString())); + addStringPart(parts, sb, ignoreSnapshot); } sb.setLength(0); isDigit = false; @@ -116,7 +206,7 @@ public static Version parse(final String version) { default: { if (Character.isDigit(c)) { if (!isDigit && sb.length() > 0) { - parts.add(new StringPart(sb.toString())); + addStringPart(parts, sb, ignoreSnapshot); sb.setLength(0); } isDigit = true; @@ -135,10 +225,17 @@ public static Version parse(final String version) { if (isDigit) { parts.add(new IntegerPart(Integer.parseInt(sb.toString()))); } else { - parts.add(new StringPart(sb.toString())); + addStringPart(parts, sb, ignoreSnapshot); } } - return new Version(version, parts); + return new Version(version, List.copyOf(parts)); + } + + private static void addStringPart(final Collection parts, final StringBuilder sb, final boolean ignoreSnapshot) { + final var value = sb.toString(); + if (!(ignoreSnapshot && ReleaseType.SNAPSHOT.type.equalsIgnoreCase(value))) { + parts.add(new StringPart(value)); + } } @Override @@ -151,7 +248,7 @@ public int compareTo(final Version o) { result = left.next().compareTo(right.next()); } else if (left.hasNext()) { result = left.next().compareTo(NULL_PART); - } else if (right.hasNext()) { + } else { // Need the inverse of the comparison result = (-1 * right.next().compareTo(NULL_PART)); } @@ -175,7 +272,7 @@ public boolean equals(final Object obj) { return false; } final Version other = (Version) obj; - return original.equals(other.original); + return Objects.equals(original, other.original); } @Override @@ -183,18 +280,15 @@ public String toString() { return original; } + @FunctionalInterface private interface Part extends Comparable { } - private static final Part NULL_PART = new Part() { - @Override - public int compareTo(final Part o) { - throw new UnsupportedOperationException(); - } + private static final Part NULL_PART = o -> { + throw new UnsupportedOperationException(); }; private static class IntegerPart implements Part { - private static final Integer DEFAULT_INTEGER = 0; private final Integer value; private IntegerPart(final Integer value) { @@ -204,7 +298,7 @@ private IntegerPart(final Integer value) { @Override public int compareTo(final Part o) { if (o == NULL_PART) { - return value.compareTo(DEFAULT_INTEGER); + return value.compareTo(0); } if (o instanceof IntegerPart) { return value.compareTo(((IntegerPart) o).value); diff --git a/src/test/java/org/wildfly/plugin/tools/VersionTestCase.java b/src/test/java/org/wildfly/plugin/tools/VersionTestCase.java index e8f4e9c..2aa7bf9 100644 --- a/src/test/java/org/wildfly/plugin/tools/VersionTestCase.java +++ b/src/test/java/org/wildfly/plugin/tools/VersionTestCase.java @@ -34,6 +34,16 @@ public void testGetLatest() { compareLatest("7.5.Final", "7.1.1.Final", "7.1.3.Final", "7.5.Final", "7.4.Final", "7.5.Final-SNAPSHOT"); } + @Test + public void ignoreSnapshot() { + Assertions.assertEquals(0, VersionComparator.compareVersion(true, "1.0.0.Final-SNAPSHOT", "1.0.0.Final")); + Assertions.assertEquals(0, VersionComparator.compareVersion(true, "10.11.0.Alpha1-SNAPSHOT", "10.11.0.a1-SNAPSHOT")); + Assertions.assertEquals(0, VersionComparator.compareVersion(true, "15.0.0.ga", "15.0.0.Final-SNAPSHOT")); + Assertions.assertEquals(1, VersionComparator.compareVersion(true, "1.0.1.Final-SNAPSHOT", "1.0.0.Final")); + Assertions.assertEquals(1, VersionComparator.compareVersion(true, "12.0.2.Final-SNAPSHOT", "12.0.2.Beta1")); + Assertions.assertEquals(-1, VersionComparator.compareVersion(true, "12.0.1.Final-SNAPSHOT", "12.0.2.Alpha1-SNAPSHOT")); + } + @Test public void testSortOrder() { // Define a list in the expected order @@ -62,12 +72,12 @@ public void testSortOrder() { // All entries should in the same order Assertions.assertTrue(orderedVersions.containsAll(versions)); - versions.sort(new VersionComparator()); + versions.sort(VersionComparator.getInstance()); Assertions.assertEquals(orderedVersions, versions); } private void compareLatest(final String expected, final String... versions) { - final SortedSet set = new TreeSet<>(new VersionComparator()); + final SortedSet set = new TreeSet<>(VersionComparator.getInstance()); set.addAll(Arrays.asList(versions)); Assertions.assertEquals(expected, set.last()); } From d891591fba247fcea65dfb8ae19a0eea25f8364e Mon Sep 17 00:00:00 2001 From: "James R. Perkins" Date: Fri, 22 Mar 2024 10:23:36 -0700 Subject: [PATCH 2/2] Fix the CI name. Signed-off-by: James R. Perkins --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index afb00be..00ff078 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,7 +1,7 @@ # This workflow will build a Java project with Maven # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven -name: WildFly Maven Plugin - CI +name: WildFly Plugin Tools - CI on: push: @@ -38,7 +38,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest, windows-latest ] + os: [ 'ubuntu-latest' , 'windows-latest' ] java: ['11', '17', '21'] steps: