Skip to content

Commit

Permalink
Merge pull request #18 from jamezp/issue17
Browse files Browse the repository at this point in the history
[17] Add an option to ignore the SNAPSHOT extension when comparing ve…
  • Loading branch information
jamezp authored Mar 22, 2024
2 parents eefab7f + d891591 commit 9edb0b3
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 31 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -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:
Expand Down Expand Up @@ -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:
Expand Down
148 changes: 121 additions & 27 deletions src/main/java/org/wildfly/plugin/tools/VersionComparator.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.
* <p>
* Some qualifiers map to other qualifiers. Below is a table of those mappings.
* <table border="1">
* <tr>
* <th>Qualifier</th>
* <th>Mapping</th>
* </tr>
* <tr>
* <td>GA</td>
* <td>Final</td>
* </tr>
* <tr>
* <td>a</td>
* <td>Alpha</td>
* </tr>
* <tr>
* <td>b</td>
* <td>Beta</td>
* </tr>
* <tr>
* <td>m</td>
* <td>Milestone</td>
* </tr>
* <tr>
* <td>cr</td>
* <td>rc</td>
* </tr>
* </table>
* </p>
*
* @author <a href="mailto:jperkins@redhat.com">James R. Perkins</a>
*/
public class VersionComparator implements Comparator<String> {
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.
Expand All @@ -35,14 +101,41 @@ public class VersionComparator implements Comparator<String> {
* @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.
*
* <p>
* 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}.
* </p>
*
* @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 {
Expand All @@ -68,22 +161,19 @@ private enum ReleaseType {
map.put(alias, r);
}
}
ENTRIES = Collections.unmodifiableMap(map);
ENTRIES = Map.copyOf(map);
}

private final String type;
private final List<String> aliases;

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);
}
}

Expand All @@ -96,7 +186,7 @@ private Version(final String original, final List<Part> parts) {
this.parts = parts;
}

public static Version parse(final String version) {
static Version parse(final String version, final boolean ignoreSnapshot) {
final List<Part> parts = new ArrayList<>();
final StringBuilder sb = new StringBuilder();
boolean isDigit = false;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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<Part> 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
Expand All @@ -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));
}
Expand All @@ -175,26 +272,23 @@ 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
public String toString() {
return original;
}

@FunctionalInterface
private interface Part extends Comparable<Part> {
}

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) {
Expand All @@ -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);
Expand Down
14 changes: 12 additions & 2 deletions src/test/java/org/wildfly/plugin/tools/VersionTestCase.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<String> set = new TreeSet<>(new VersionComparator());
final SortedSet<String> set = new TreeSet<>(VersionComparator.getInstance());
set.addAll(Arrays.asList(versions));
Assertions.assertEquals(expected, set.last());
}
Expand Down

0 comments on commit 9edb0b3

Please sign in to comment.