Skip to content
Open
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,26 +176,33 @@ public String toString() {

static final class Tokenizer {

private static final Integer QUALIFIER_ALPHA = -5;
private static final Integer QUALIFIER_ALPHA = -7;

private static final Integer QUALIFIER_BETA = -4;
private static final Integer QUALIFIER_BETA = -6;

private static final Integer QUALIFIER_MILESTONE = -3;
private static final Integer QUALIFIER_MILESTONE = -5;

private static final Map<String, Integer> QUALIFIERS;

static {
QUALIFIERS = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
// PRE RELEASE
QUALIFIERS.put("alpha", QUALIFIER_ALPHA);
QUALIFIERS.put("beta", QUALIFIER_BETA);
QUALIFIERS.put("milestone", QUALIFIER_MILESTONE);
QUALIFIERS.put("cr", -2);
QUALIFIERS.put("rc", -2);
QUALIFIERS.put("pr", -4);
QUALIFIERS.put("pre", -4);
QUALIFIERS.put("preview", -4);
QUALIFIERS.put("rc", -3);
QUALIFIERS.put("cr", -3);
QUALIFIERS.put("dev", -2);
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);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,9 @@
* <p>
* Numeric segments are compared mathematically, alphabetic segments are compared lexicographically and
* case-insensitively. However, the following qualifier strings are recognized and treated specially: "alpha" = "a" &lt;
* "beta" = "b" &lt; "milestone" = "m" &lt; "cr" = "rc" &lt; "snapshot" &lt; "final" = "ga" &lt; "sp". All of those
* well-known qualifiers are considered smaller/older than other strings. An empty segment/string is equivalent to 0.
* "beta" = "b" &lt; "milestone" = "m" &lt; "pr" = "pre" = "preview" &lt; "rc" = "cr" &lt; "dev" &lt; "snapshot" &lt;
* "final" = "ga" = "release" &lt; "sp". All of those well-known qualifiers are considered smaller/older than other
* strings. An empty segment/string is equivalent to 0.
* </p>
* <p>
* In addition to the above mentioned qualifiers, the tokens "min" and "max" may be used as final version segment to
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,20 @@
* <li>"alpha" (== "a" when immediately followed by number)</li>
* <li>"beta" (== "b" when immediately followed by number)</li>
* <li>"milestone" (== "m" when immediately followed by number)</li>
* <li>"rc" == "cr" (use of "cr" is discouraged)</li>
* <li>"pr" = "pre" = "preview" (use is discouraged)</li>
* <li>"rc" == "cr" (use of "cr" is discouraged, use rc instead)</li>
* <li>"dev" (use is discouraged)</li>
* <li>"snapshot"</li>
* <li>"ga" == "final" == "release"</li>
* <li>"sp"</li>
* <li>"final" == "ga" == "release" (use is discouraged, use no qualifier instead)</li>
* <li>"sp" (use of "sp" is discouraged, increment patch version instead)</li>
* </ul>
* </li>
* <li>String segments are sorted lexicographically and case-insensitively per ROOT locale, ascending.</li>
* <li>There are two special segments, {@code "min"} and {@code "max"} that represent absolute minimum and absolute maximum in comparisons. They can be used only as the trailing segment.</li>
* <li>As last step, trailing "zero segments" are trimmed. Similarly, "zero segments" positioned before numeric and non-numeric transitions (either explicitly or implicitly delimited) are trimmed.</li>
* <li>When trimming, "zero segments" are qualifiers {@code "ga"}, {@code "final"}, {@code "release"} only if being last (right-most) segment, empty string and "0" always.</li>
* <li>When trimming, "zero segments" are qualifiers {@code "final"}, {@code "ga"}, {@code "release"} only if being last (right-most) segment, empty string and "0" always.</li>
* <li>In comparison of same kind segments, the given type of segment determines comparison rules.</li>
* <li>In comparison of different kind of segments, following applies: {@code max > numeric > string > qualifier > min}.</li>
* <li>In comparison of different kind of segments, following applies: {@code min < qualifier < string < numeric < max}.</li>
* <li>Any version can be considered to have an infinite number of invisible trailing "zero segments", for the purposes of comparison (in other words, "1" == "1.0.0.0.0.0.0.0.0....")</li>
* <li>It is common that a version identifier starts with numeric segment (consider this "best practice").</li>
* </ul>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -632,4 +632,98 @@ private Stream<String> uuidVersionStringStream() {
"f95e94f7-2443-4b2f-a10d-059d8d224dd9",
"b558af80-78bc-43c7-b916-d635a23cc4b5");
}

/**
* 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() {
assertOrder(X_LT_Y, "6.1.0rc3", "6.1H.5-beta");
assertOrder(X_LT_Y, "6.1.0rc3", "6.1.0"); // classical
assertOrder(X_LT_Y, "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

assertOrder(X_LT_Y, a, b);
assertOrder(X_LT_Y, b, c);
assertOrder(X_LT_Y, a, c);
assertOrder(X_LT_Y, c, d);
assertOrder(X_LT_Y, b, d);
assertOrder(X_LT_Y, 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";

assertOrder(X_LT_Y, a, c); // Now a < c, but before MNG-6964 they were equal
assertOrder(X_LT_Y, b, c); // Now b < c, but before MNG-6964 they were equal
assertOrder(X_LT_Y, 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
// assertSequence("ab", "alpha", "beta", "cd", "ea", "milestone", "pfd", "preview", "RC");
assertSequence("alpha", "beta", "milestone", "preview", "rc", "dev", "snapshot", "");
// checking identified issues respect the general case
// assertOrder(X_LT_Y, "2.3-pfd", "2.3");
// assertOrder(X_LT_Y, "2.0.1.MR", "2.0.1");
assertOrder(X_LT_Y, "9.4.1.jre16-preview", "9.4.1.jre16");
assertOrder(X_LT_Y, "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
assertOrder(X_LT_Y, "1.0.0." + x + "1", "1.0.0-" + x + "2");
// 2.0.X1 == 2-X1 == 2.0.0.X1 for any string x
assertOrder(X_EQ_Y, "2-" + x + "1", "2.0." + x + "1"); // previously ordered, now equals
assertOrder(X_EQ_Y, "2-" + x + "1", "2.0.0." + x + "1"); // previously ordered, now equals
assertOrder(X_EQ_Y, "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);
}

}