Skip to content

Commit 790483d

Browse files
committed
[MNG-7559] Fix version comparison with case insensitive lexical order
1 parent 3b15101 commit 790483d

File tree

4 files changed

+118
-14
lines changed

4 files changed

+118
-14
lines changed

maven-resolver-util/src/main/java/org/eclipse/aether/util/version/GenericVersion.java

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -176,26 +176,33 @@ public String toString() {
176176

177177
static final class Tokenizer {
178178

179-
private static final Integer QUALIFIER_ALPHA = -5;
179+
private static final Integer QUALIFIER_ALPHA = -7;
180180

181-
private static final Integer QUALIFIER_BETA = -4;
181+
private static final Integer QUALIFIER_BETA = -6;
182182

183-
private static final Integer QUALIFIER_MILESTONE = -3;
183+
private static final Integer QUALIFIER_MILESTONE = -5;
184184

185185
private static final Map<String, Integer> QUALIFIERS;
186186

187187
static {
188188
QUALIFIERS = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
189+
// PRE RELEASE
189190
QUALIFIERS.put("alpha", QUALIFIER_ALPHA);
190191
QUALIFIERS.put("beta", QUALIFIER_BETA);
191192
QUALIFIERS.put("milestone", QUALIFIER_MILESTONE);
192-
QUALIFIERS.put("cr", -2);
193-
QUALIFIERS.put("rc", -2);
193+
QUALIFIERS.put("pr", -4);
194+
QUALIFIERS.put("pre", -4);
195+
QUALIFIERS.put("preview", -4);
196+
QUALIFIERS.put("rc", -3);
197+
QUALIFIERS.put("cr", -3);
198+
QUALIFIERS.put("dev", -2);
194199
QUALIFIERS.put("snapshot", -1);
195-
QUALIFIERS.put("ga", 0);
200+
// RELEASE
201+
QUALIFIERS.put("", 0);
196202
QUALIFIERS.put("final", 0);
203+
QUALIFIERS.put("ga", 0);
197204
QUALIFIERS.put("release", 0);
198-
QUALIFIERS.put("", 0);
205+
// POST RELEASE
199206
QUALIFIERS.put("sp", 1);
200207
}
201208

maven-resolver-util/src/main/java/org/eclipse/aether/util/version/GenericVersionScheme.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,9 @@
3636
* <p>
3737
* Numeric segments are compared mathematically, alphabetic segments are compared lexicographically and
3838
* case-insensitively. However, the following qualifier strings are recognized and treated specially: "alpha" = "a" &lt;
39-
* "beta" = "b" &lt; "milestone" = "m" &lt; "cr" = "rc" &lt; "snapshot" &lt; "final" = "ga" &lt; "sp". All of those
40-
* well-known qualifiers are considered smaller/older than other strings. An empty segment/string is equivalent to 0.
39+
* "beta" = "b" &lt; "milestone" = "m" &lt; "pr" = "pre" = "preview" &lt; "rc" = "cr" &lt; "dev" &lt; "snapshot" &lt;
40+
* "final" = "ga" = "release" &lt; "sp". All of those well-known qualifiers are considered smaller/older than other
41+
* strings. An empty segment/string is equivalent to 0.
4142
* </p>
4243
* <p>
4344
* In addition to the above mentioned qualifiers, the tokens "min" and "max" may be used as final version segment to

maven-resolver-util/src/main/java/org/eclipse/aether/util/version/package-info.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,18 +42,20 @@
4242
* <li>"alpha" (== "a" when immediately followed by number)</li>
4343
* <li>"beta" (== "b" when immediately followed by number)</li>
4444
* <li>"milestone" (== "m" when immediately followed by number)</li>
45-
* <li>"rc" == "cr" (use of "cr" is discouraged)</li>
45+
* <li>"pr" = "pre" = "preview" (use is discouraged)</li>
46+
* <li>"rc" == "cr" (use of "cr" is discouraged, use rc instead)</li>
47+
* <li>"dev" (use is discouraged)</li>
4648
* <li>"snapshot"</li>
47-
* <li>"ga" == "final" == "release"</li>
48-
* <li>"sp"</li>
49+
* <li>"final" == "ga" == "release" (use is discouraged, use no qualifier instead)</li>
50+
* <li>"sp" (use of "sp" is discouraged, increment patch version instead)</li>
4951
* </ul>
5052
* </li>
5153
* <li>String segments are sorted lexicographically and case-insensitively per ROOT locale, ascending.</li>
5254
* <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>
5355
* <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>
54-
* <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>
56+
* <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>
5557
* <li>In comparison of same kind segments, the given type of segment determines comparison rules.</li>
56-
* <li>In comparison of different kind of segments, following applies: {@code max > numeric > string > qualifier > min}.</li>
58+
* <li>In comparison of different kind of segments, following applies: {@code min < qualifier < string < numeric < max}.</li>
5759
* <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>
5860
* <li>It is common that a version identifier starts with numeric segment (consider this "best practice").</li>
5961
* </ul>

maven-resolver-util/src/test/java/org/eclipse/aether/util/version/GenericVersionTest.java

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -632,4 +632,98 @@ private Stream<String> uuidVersionStringStream() {
632632
"f95e94f7-2443-4b2f-a10d-059d8d224dd9",
633633
"b558af80-78bc-43c7-b916-d635a23cc4b5");
634634
}
635+
636+
/**
637+
* Test <a href="https://issues.apache.org/jira/browse/MNG-5568">MNG-5568</a> edge case
638+
* which was showing transitive inconsistency: since A &gt; B and B &gt; C then we should have A &gt; C
639+
* otherwise sorting a list of ComparableVersions() will in some cases throw runtime exception;
640+
* see Netbeans issues <a href="https://netbeans.org/bugzilla/show_bug.cgi?id=240845">240845</a> and
641+
* <a href="https://netbeans.org/bugzilla/show_bug.cgi?id=226100">226100</a>
642+
*/
643+
@Test
644+
void testMng5568() {
645+
assertOrder(X_LT_Y, "6.1.0rc3", "6.1H.5-beta");
646+
assertOrder(X_LT_Y, "6.1.0rc3", "6.1.0"); // classical
647+
assertOrder(X_LT_Y, "6.1.0", "6.1H.5-beta"); // transitivity
648+
}
649+
650+
/**
651+
* Test <a href="https://jira.apache.org/jira/browse/MNG-6572">MNG-6572</a> optimization.
652+
*/
653+
@Test
654+
void testMng6572() {
655+
String a = "20190126.230843"; // resembles a SNAPSHOT
656+
String b = "1234567890.12345"; // 10 digit number
657+
String c = "123456789012345.1H.5-beta"; // 15 digit number
658+
String d = "12345678901234567890.1H.5-beta"; // 20 digit number
659+
660+
assertOrder(X_LT_Y, a, b);
661+
assertOrder(X_LT_Y, b, c);
662+
assertOrder(X_LT_Y, a, c);
663+
assertOrder(X_LT_Y, c, d);
664+
assertOrder(X_LT_Y, b, d);
665+
assertOrder(X_LT_Y, a, d);
666+
}
667+
668+
/**
669+
* Test <a href="https://issues.apache.org/jira/browse/MNG-6964">MNG-6964</a> edge cases
670+
* for qualifiers that start with "-0.", which was showing A == C and B == C but A &lt; B.
671+
*/
672+
@Test
673+
void testMng6964() {
674+
String a = "1-0.alpha";
675+
String b = "1-0.beta";
676+
String c = "1";
677+
678+
assertOrder(X_LT_Y, a, c); // Now a < c, but before MNG-6964 they were equal
679+
assertOrder(X_LT_Y, b, c); // Now b < c, but before MNG-6964 they were equal
680+
assertOrder(X_LT_Y, a, b); // Should still be true
681+
}
682+
683+
/**
684+
* Test <a href="https://issues.apache.org/jira/browse/MNG-7559">MNG-7559</a> edge cases
685+
* -pfd < final, ga, release
686+
* 2.0.1.MR < 2.0.1
687+
* 9.4.1.jre16 > 9.4.1.jre16-preview
688+
*/
689+
@Test
690+
void testMng7559() {
691+
// checking general cases
692+
// assertSequence("ab", "alpha", "beta", "cd", "ea", "milestone", "pfd", "preview", "RC");
693+
assertSequence("alpha", "beta", "milestone", "preview", "rc", "dev", "snapshot", "");
694+
// checking identified issues respect the general case
695+
// assertOrder(X_LT_Y, "2.3-pfd", "2.3");
696+
// assertOrder(X_LT_Y, "2.0.1.MR", "2.0.1");
697+
assertOrder(X_LT_Y, "9.4.1.jre16-preview", "9.4.1.jre16");
698+
assertOrder(X_LT_Y, "1-ga-1", "1-sp-1");
699+
}
700+
701+
/**
702+
* Test <a href="https://issues.apache.org/jira/browse/MNG-7644">MNG-7644</a> edge cases
703+
* 1.0.0.RC1 &lt; 1.0.0-RC2 and more generally:
704+
* 1.0.0.X1 &lt; 1.0.0-X2 for any string X
705+
*/
706+
@Test
707+
void testMng7644() {
708+
for (String x : new String[] {"abc", "alpha", "a", "beta", "b", "def", "m", "preview", "RC"}) {
709+
// 1.0.0.X1 < 1.0.0-X2 for any string x
710+
assertOrder(X_LT_Y, "1.0.0." + x + "1", "1.0.0-" + x + "2");
711+
// 2.0.X1 == 2-X1 == 2.0.0.X1 for any string x
712+
assertOrder(X_EQ_Y, "2-" + x + "1", "2.0." + x + "1"); // previously ordered, now equals
713+
assertOrder(X_EQ_Y, "2-" + x + "1", "2.0.0." + x + "1"); // previously ordered, now equals
714+
assertOrder(X_EQ_Y, "2.0." + x + "1", "2.0.0." + x + "1"); // previously ordered, now equals
715+
}
716+
}
717+
718+
@Test
719+
public void testMng7714() {
720+
String f = ("1.0.final-redhat");
721+
String sp1 = ("1.0-sp1-redhat");
722+
String sp2 = ("1.0-sp-1-redhat");
723+
String sp3 = ("1.0-sp.1-redhat");
724+
assertOrder(X_LT_Y, f, sp1);
725+
assertOrder(X_LT_Y, f, sp2);
726+
assertOrder(X_LT_Y, f, sp3);
727+
}
728+
635729
}

0 commit comments

Comments
 (0)