Skip to content
Draft
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 @@ -176,27 +176,30 @@ public String toString() {

static final class Tokenizer {

private static final Integer QUALIFIER_ALPHA = -5;

private static final Integer QUALIFIER_BETA = -4;

private static final Integer QUALIFIER_MILESTONE = -3;

private static final Map<String, Integer> QUALIFIERS;

private static final Map<String, String> ALIASES;

static {
QUALIFIERS = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
QUALIFIERS.put("alpha", QUALIFIER_ALPHA);
QUALIFIERS.put("beta", QUALIFIER_BETA);
QUALIFIERS.put("milestone", QUALIFIER_MILESTONE);
QUALIFIERS.put("cr", -2);
QUALIFIERS.put("rc", -2);
// PRE RELEASE
QUALIFIERS.put("dev", -1);
QUALIFIERS.put("snapshot", -1);
QUALIFIERS.put("ga", 0);
// RELEASE
QUALIFIERS.put("", 0);
QUALIFIERS.put("final", 0);
QUALIFIERS.put("ga", 0);
QUALIFIERS.put("release", 0);
QUALIFIERS.put("", 0);
// POST RELEASE
QUALIFIERS.put("sp", 1);

// ALIASES
ALIASES = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
ALIASES.put("a", "alpha");
ALIASES.put("b", "beta");
ALIASES.put("m", "milestone");
ALIASES.put("mr", "milestone");
ALIASES.put("cr", "rc");
}

private final String version;
Expand Down Expand Up @@ -230,7 +233,7 @@ public boolean next() {
for (; index < versionLength; index++) {
char c = version.charAt(index);

if (c == '.' || c == '-' || c == '_') {
if (c == '.' || c == '-' || c == '_' || c == '+') {
end = index;
index++;
break;
Expand Down Expand Up @@ -292,43 +295,38 @@ public Item toItem() {
return Item.MAX;
}
}
if (terminatedByNumber && token.length() == 1) {
switch (token.charAt(0)) {
case 'a':
case 'A':
return new Item(Item.KIND_QUALIFIER, QUALIFIER_ALPHA);
case 'b':
case 'B':
return new Item(Item.KIND_QUALIFIER, QUALIFIER_BETA);
case 'm':
case 'M':
return new Item(Item.KIND_QUALIFIER, QUALIFIER_MILESTONE);
default:
}
if (!terminatedByNumber && token.length() == 1) {
return new Item(Item.KIND_POST_RELEASE, token);
}
String alias = ALIASES.get(token);
if (alias != null) {
token = alias;
}
Integer qualifier = QUALIFIERS.get(token);
if (qualifier != null) {
return new Item(Item.KIND_QUALIFIER, qualifier);
} else {
return new Item(Item.KIND_STRING, token.toLowerCase(Locale.ENGLISH));
return new Item(Item.KIND_PRE_RELEASE, token.toLowerCase(Locale.ENGLISH));
}
}
}
}

static final class Item {

static final int KIND_MAX = 8;
static final int KIND_MAX = 4;

static final int KIND_BIGINT = 3;

static final int KIND_BIGINT = 5;
static final int KIND_INT = 2;

static final int KIND_INT = 4;
static final int KIND_POST_RELEASE = 1;

static final int KIND_STRING = 3;
static final int KIND_QUALIFIER = 0;

static final int KIND_QUALIFIER = 2;
static final int KIND_PRE_RELEASE = -1;

static final int KIND_MIN = 0;
static final int KIND_MIN = -2;

static final Item MAX = new Item(KIND_MAX, "max");

Expand All @@ -344,20 +342,21 @@ static final class Item {
}

public boolean isNumber() {
return (kind & KIND_QUALIFIER) == 0; // i.e. kind != string/qualifier
return kind != KIND_QUALIFIER && kind != KIND_PRE_RELEASE;
}

public int compareTo(Item that) {
int rel;
if (that == null) {
// null in this context denotes the pad item (0 or "ga")
switch (kind) {
case KIND_PRE_RELEASE:
case KIND_MIN:
rel = -1;
break;
case KIND_POST_RELEASE:
case KIND_MAX:
case KIND_BIGINT:
case KIND_STRING:
rel = 1;
break;
case KIND_INT:
Expand All @@ -371,6 +370,7 @@ public int compareTo(Item that) {
rel = kind - that.kind;
if (rel == 0) {
switch (kind) {
case KIND_POST_RELEASE:
case KIND_MAX:
case KIND_MIN:
break;
Expand All @@ -381,7 +381,7 @@ public int compareTo(Item that) {
case KIND_QUALIFIER:
rel = ((Integer) value).compareTo((Integer) that.value);
break;
case KIND_STRING:
case KIND_PRE_RELEASE:
rel = ((String) value).compareToIgnoreCase((String) that.value);
break;
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ void testTrimPaddingNotNeededString() {
new GenericVersion.Item(GenericVersion.Item.KIND_INT, 1),
new GenericVersion.Item(GenericVersion.Item.KIND_INT, 0),
new GenericVersion.Item(GenericVersion.Item.KIND_INT, 0),
new GenericVersion.Item(GenericVersion.Item.KIND_STRING, "string")));
new GenericVersion.Item(GenericVersion.Item.KIND_PRE_RELEASE, "string")));
assertEquals(4, items.size());
GenericVersion.trimPadding(items);
assertEquals(2, items.size());
Expand All @@ -87,7 +87,7 @@ void testTrimPaddingNeededQualifierMixed() {
new GenericVersion.Item(GenericVersion.Item.KIND_INT, 1),
new GenericVersion.Item(GenericVersion.Item.KIND_INT, 0),
new GenericVersion.Item(GenericVersion.Item.KIND_INT, 0),
new GenericVersion.Item(GenericVersion.Item.KIND_STRING, "string"),
new GenericVersion.Item(GenericVersion.Item.KIND_PRE_RELEASE, "string"),
new GenericVersion.Item(GenericVersion.Item.KIND_INT, 0),
new GenericVersion.Item(GenericVersion.Item.KIND_QUALIFIER, 0)));
assertEquals(6, items.size());
Expand All @@ -103,7 +103,7 @@ void testTrimPaddingNeededQualifierMixedInBetweenGa() {
new GenericVersion.Item(GenericVersion.Item.KIND_INT, 0),
new GenericVersion.Item(GenericVersion.Item.KIND_QUALIFIER, 0),
new GenericVersion.Item(GenericVersion.Item.KIND_INT, 0),
new GenericVersion.Item(GenericVersion.Item.KIND_STRING, "string"),
new GenericVersion.Item(GenericVersion.Item.KIND_PRE_RELEASE, "string"),
new GenericVersion.Item(GenericVersion.Item.KIND_INT, 0),
new GenericVersion.Item(GenericVersion.Item.KIND_QUALIFIER, 0)));
assertEquals(7, items.size());
Expand Down Expand Up @@ -323,13 +323,13 @@ void testWellKnownQualifierOrdering() {

@Test
void testWellKnownQualifierVersusUnknownQualifierOrdering() {
assertOrder(X_GT_Y, "1-abc", "1-alpha");
assertOrder(X_GT_Y, "1-abc", "1-beta");
assertOrder(X_GT_Y, "1-abc", "1-milestone");
assertOrder(X_GT_Y, "1-abc", "1-rc");
assertOrder(X_GT_Y, "1-abc", "1-snapshot");
assertOrder(X_GT_Y, "1-abc", "1");
assertOrder(X_GT_Y, "1-abc", "1-sp");
assertOrder(X_LT_Y, "1-abc", "1-alpha");
assertOrder(X_LT_Y, "1-abc", "1-beta");
assertOrder(X_LT_Y, "1-abc", "1-milestone");
assertOrder(X_LT_Y, "1-abc", "1-rc");
assertOrder(X_LT_Y, "1-abc", "1-snapshot");
assertOrder(X_LT_Y, "1-abc", "1");
assertOrder(X_LT_Y, "1-abc", "1-sp");
}

@Test
Expand Down Expand Up @@ -632,4 +632,113 @@ private Stream<String> uuidVersionStringStream() {
"f95e94f7-2443-4b2f-a10d-059d8d224dd9",
"b558af80-78bc-43c7-b916-d635a23cc4b5");
}

private void checkVersionsOrder(String[] versions) {
assertSequence(versions);
}

private void checkVersionsEqual(String v1, String v2) {
assertOrder(X_EQ_Y, v1, v2);
}

private void checkVersionsHaveSameOrder(String v1, String v2) {
assertOrder(X_EQ_Y, v1, v2);
}

private void checkVersionsOrder(String v1, String v2) {
assertOrder(X_LT_Y, v1, v2);
}

/**
* Test <a href="https://issues.apache.org/jira/browse/MNG-5568">MNG-5568</a> edge case
* which was showing transitive inconsistency: since A &gt; B and B &gt; C then we should have A &gt; C
* otherwise sorting a list of ComparableVersions() will in some cases throw runtime exception;
* see Netbeans issues <a href="https://netbeans.org/bugzilla/show_bug.cgi?id=240845">240845</a> and
* <a href="https://netbeans.org/bugzilla/show_bug.cgi?id=226100">226100</a>
*/
@Test
void testMng5568() {
checkVersionsOrder("6.1.0rc3", "6.1H.5-beta");
checkVersionsOrder("6.1.0rc3", "6.1.0"); // classical
checkVersionsOrder("6.1.0", "6.1H.5-beta"); // transitivity
}

/**
* Test <a href="https://jira.apache.org/jira/browse/MNG-6572">MNG-6572</a> optimization.
*/
@Test
void testMng6572() {
String a = "20190126.230843"; // resembles a SNAPSHOT
String b = "1234567890.12345"; // 10 digit number
String c = "123456789012345.1H.5-beta"; // 15 digit number
String d = "12345678901234567890.1H.5-beta"; // 20 digit number

checkVersionsOrder(a, b);
checkVersionsOrder(b, c);
checkVersionsOrder(a, c);
checkVersionsOrder(c, d);
checkVersionsOrder(b, d);
checkVersionsOrder(a, d);
}

/**
* Test <a href="https://issues.apache.org/jira/browse/MNG-6964">MNG-6964</a> edge cases
* for qualifiers that start with "-0.", which was showing A == C and B == C but A &lt; B.
*/
@Test
void testMng6964() {
String a = "1-0.alpha";
String b = "1-0.beta";
String c = "1";

checkVersionsOrder(a, c); // Now a < c, but before MNG-6964 they were equal
checkVersionsOrder(b, c); // Now b < c, but before MNG-6964 they were equal
checkVersionsOrder(a, b); // Should still be true
}

/**
* Test <a href="https://issues.apache.org/jira/browse/MNG-7559">MNG-7559</a> edge cases
* -pfd < final, ga, release
* 2.0.1.MR < 2.0.1
* 9.4.1.jre16 > 9.4.1.jre16-preview
*/
@Test
void testMng7559() {
// checking general cases
checkVersionsOrder(new String[] {"ab", "alpha", "beta", "cd", "ea", "milestone", "pfd", "preview", "RC"});
// checking identified issues respect the general case
checkVersionsOrder("2.3-pfd", "2.3");
checkVersionsOrder("2.0.1.MR", "2.0.1");
checkVersionsOrder("9.4.1.jre16-preview", "9.4.1.jre16");
checkVersionsOrder("1-ga-1", "1-sp-1");
}

/**
* Test <a href="https://issues.apache.org/jira/browse/MNG-7644">MNG-7644</a> edge cases
* 1.0.0.RC1 &lt; 1.0.0-RC2 and more generally:
* 1.0.0.X1 &lt; 1.0.0-X2 for any string X
*/
@Test
void testMng7644() {
for (String x : new String[] {"abc", "alpha", "a", "beta", "b", "def", "m", "preview", "RC"}) {
// 1.0.0.X1 < 1.0.0-X2 for any string x
checkVersionsOrder("1.0.0." + x + "1", "1.0.0-" + x + "2");
// 2.0.X1 == 2-X1 == 2.0.0.X1 for any string x
checkVersionsEqual("2-" + x + "1", "2.0." + x + "1"); // previously ordered, now equals
checkVersionsEqual("2-" + x + "1", "2.0.0." + x + "1"); // previously ordered, now equals
checkVersionsEqual("2.0." + x + "1", "2.0.0." + x + "1"); // previously ordered, now equals
}
}

@Test
public void testMng7714() {
String f = ("1.0.final-redhat");
String sp1 = ("1.0-sp1-redhat");
String sp2 = ("1.0-sp-1-redhat");
String sp3 = ("1.0-sp.1-redhat");
assertOrder(X_LT_Y, f, sp1);
assertOrder(X_LT_Y, f, sp2);
assertOrder(X_LT_Y, f, sp3);
}

}