diff --git a/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/ComparableVersion.java b/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/ComparableVersion.java
index 17aa8cc7a703..bf29c2102ab3 100644
--- a/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/ComparableVersion.java
+++ b/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/ComparableVersion.java
@@ -41,25 +41,28 @@
* 1.0alpha1 => [1, 0, alpha, 1]
*
unlimited number of version components,
* version components in the text can be digits or strings,
- * strings are checked for well-known qualifiers and the qualifier ordering is used for version ordering.
- * Well-known qualifiers (case insensitive) are:
- * alpha or a
- * beta or b
- * milestone or m
- * rc or cr
- * snapshot
- * (the empty string) or ga or final
- * sp
- *
- * Unknown qualifiers are considered after known qualifiers, with lexical order (always case insensitive),
- *
+ *
+ * Following semver rules is encouraged, and some qualifiers are discouraged (no matter the case):
+ *
+ * - The usage of 'CR' qualifier is discouraged. Use 'RC' instead.
+ * - The usage of 'Final', 'GA', and 'Release' qualifiers is discouraged. Use no qualifier instead.
+ * - The usage of 'SP' qualifier is discouraged. Increment the patch version instead.
+ *
+ * String qualifiers are ordered lexically (case insensitive), with the following exceptions:
+ *
+ * alpha = a < beta = b < milestone = m < rc = cr
+ * < snapshot < '' = final = ga = release < sp
+ * - Other qualifiers are ordered lexically (case insensitive),
+ * and considered less than Snapshot and Release.
+ *
+ *
* a hyphen usually precedes a qualifier, and is always less important than digits/number, for example
* {@code 1.0.RC2 < 1.0-RC3 < 1.0.1}; but prefer {@code 1.0.0-RC1} over {@code 1.0.0.RC1}, and more
* generally: {@code 1.0.X2 < 1.0-X3 < 1.0.1} for any string {@code X}; but prefer {@code 1.0.0-X1}
* over {@code 1.0.0.X1}.
*
*
- * @see "Versioning" on Maven Wiki
+ * @see Version Order Specification
* @author Kenney Westerhof
* @author Hervé Boutemy
*/
@@ -135,7 +138,7 @@ public int compareTo( Item item )
{
case INT_ITEM:
int itemValue = ( (IntItem) item ).value;
- return ( value < itemValue ) ? -1 : ( ( value == itemValue ) ? 0 : 1 );
+ return Integer.compare( value, itemValue );
case LONG_ITEM:
case BIGINTEGER_ITEM:
return -1;
@@ -221,7 +224,7 @@ public int compareTo( Item item )
return 1;
case LONG_ITEM:
long itemValue = ( (LongItem) item ).value;
- return ( value < itemValue ) ? -1 : ( ( value == itemValue ) ? 0 : 1 );
+ return Long.compare( value, itemValue );
case BIGINTEGER_ITEM:
return -1;
@@ -356,23 +359,23 @@ public String toString()
private static class StringItem
implements Item
{
- private static final List QUALIFIERS =
- Arrays.asList( "alpha", "beta", "milestone", "rc", "snapshot", "", "sp" );
+ private static final List QUALIFIERS = Arrays.asList( "snapshot", "", "sp" );
private static final Properties ALIASES = new Properties();
+
static
{
- ALIASES.put( "ga", "" );
+ ALIASES.put( "cr", "rc" );
ALIASES.put( "final", "" );
+ ALIASES.put( "ga", "" );
ALIASES.put( "release", "" );
- ALIASES.put( "cr", "rc" );
}
/**
- * A comparable value for the empty-string qualifier. This one is used to determine if a given qualifier makes
+ * An index value for the empty-string qualifier. This one is used to determine if a given qualifier makes
* the version older than one without a qualifier, or more recent.
*/
- private static final String RELEASE_VERSION_INDEX = String.valueOf( QUALIFIERS.indexOf( "" ) );
+ private static final int RELEASE_VERSION_INDEX = QUALIFIERS.indexOf( "" );
private final String value;
@@ -407,7 +410,7 @@ public int getType()
@Override
public boolean isNull()
{
- return ( comparableQualifier( value ).compareTo( RELEASE_VERSION_INDEX ) == 0 );
+ return QUALIFIERS.indexOf( value ) == RELEASE_VERSION_INDEX;
}
/**
@@ -422,12 +425,37 @@ public boolean isNull()
*
* @param qualifier
* @return an equivalent value that can be used with lexical comparison
+ * @deprecated Use {@link #compareQualifiers(String, String)} instead
*/
+ @Deprecated
public static String comparableQualifier( String qualifier )
{
- int i = QUALIFIERS.indexOf( qualifier );
+ int index = QUALIFIERS.indexOf( qualifier ) + 1;
+
+ return index == 0 ? ( "0-" + qualifier ) : String.valueOf( index );
+ }
+
+ /**
+ * Compare the qualifiers of two artifact versions.
+ *
+ * @param qualifier1 qualifier of first artifact
+ * @param qualifier2 qualifier of second artifact
+ * @return a negative integer, zero, or a positive integer as the first argument is less than, equal to, or
+ * greater than the second
+ */
+ public static int compareQualifiers( String qualifier1, String qualifier2 )
+ {
+ int i1 = QUALIFIERS.indexOf( qualifier1 );
+ int i2 = QUALIFIERS.indexOf( qualifier2 );
+
+ // if both pre-release, then use natural lexical ordering
+ if ( i1 == -1 && i2 == -1 )
+ {
+ return qualifier1.compareTo( qualifier2 );
+ }
- return i == -1 ? ( QUALIFIERS.size() + "-" + qualifier ) : String.valueOf( i );
+ // 'other qualifier' < 'snapshot' < '' < 'sp'
+ return Integer.compare( i1, i2 );
}
@Override
@@ -436,7 +464,7 @@ public int compareTo( Item item )
if ( item == null )
{
// 1-rc < 1, 1-ga > 1
- return comparableQualifier( value ).compareTo( RELEASE_VERSION_INDEX );
+ return Integer.compare( QUALIFIERS.indexOf( value ), RELEASE_VERSION_INDEX );
}
switch ( item.getType() )
{
@@ -446,7 +474,7 @@ public int compareTo( Item item )
return -1; // 1.any < 1.1 ?
case STRING_ITEM:
- return comparableQualifier( value ).compareTo( comparableQualifier( ( (StringItem) item ).value ) );
+ return compareQualifiers( value, ( ( StringItem ) item ).value );
case LIST_ITEM:
return -1; // 1.any < 1-1
diff --git a/maven-artifact/src/test/java/org/apache/maven/artifact/versioning/ComparableVersionTest.java b/maven-artifact/src/test/java/org/apache/maven/artifact/versioning/ComparableVersionTest.java
index 832ab179b83f..67e6d43b6596 100644
--- a/maven-artifact/src/test/java/org/apache/maven/artifact/versioning/ComparableVersionTest.java
+++ b/maven-artifact/src/test/java/org/apache/maven/artifact/versioning/ComparableVersionTest.java
@@ -46,13 +46,13 @@ private Comparable newComparable( String version )
}
private static final String[] VERSIONS_QUALIFIER =
- { "1-alpha2snapshot", "1-alpha2", "1-alpha-123", "1-beta-2", "1-beta123", "1-m2", "1-m11", "1-rc", "1-cr2",
- "1-rc123", "1-SNAPSHOT", "1", "1-sp", "1-sp2", "1-sp123", "1-abc", "1-def", "1-pom-1", "1-1-snapshot",
+ { "1-abc", "1-alpha2snapshot", "1-alpha2", "1-alpha-123", "1-beta-2", "1-beta123", "1-def", "1-m2", "1-m11",
+ "1-pom-1", "1-rc", "1-cr2", "1-rc123", "1-SNAPSHOT", "1", "1-sp", "1-sp2", "1-sp123", "1-1-snapshot",
"1-1", "1-2", "1-123" };
private static final String[] VERSIONS_NUMBER =
- { "2.0", "2.0.a", "2-1", "2.0.2", "2.0.123", "2.1.0", "2.1-a", "2.1b", "2.1-c", "2.1-1", "2.1.0.1", "2.2",
- "2.123", "11.a2", "11.a11", "11.b2", "11.b11", "11.m2", "11.m11", "11", "11.a", "11b", "11c", "11m" };
+ { "2.0.a", "2.0", "2-1", "2.0.2", "2.0.123", "2.1-a", "2.1b", "2.1-c", "2.1.0", "2.1-1", "2.1.0.1", "2.2",
+ "2.123", "11.a", "11.a2", "11.a11", "11b", "11.b2", "11.b11", "11c", "11m", "11.m2", "11.m11", "11" };
private void checkVersionsOrder( String[] versions )
{
@@ -189,7 +189,7 @@ public void testVersionComparing()
checkVersionsOrder( "2.0-1", "2.0.1" );
checkVersionsOrder( "2.0.1-klm", "2.0.1-lmn" );
- checkVersionsOrder( "2.0.1", "2.0.1-xyz" );
+ checkVersionsOrder( "2.0.1-xyz", "2.0.1" ); // now 2.0.1-xyz < 2.0.1 as of MNG-7559
checkVersionsOrder( "2.0.1", "2.0.1-123" );
checkVersionsOrder( "2.0.1-xyz", "2.0.1-123" );
@@ -204,13 +204,9 @@ public void testVersionComparing()
*/
public void testMng5568()
{
- String a = "6.1.0";
- String b = "6.1.0rc3";
- String c = "6.1H.5-beta"; // this is the unusual version string, with 'H' in the middle
-
- checkVersionsOrder( b, a ); // classical
- checkVersionsOrder( b, c ); // now b < c, but before MNG-5568, we had b > c
- checkVersionsOrder( a, c );
+ checkVersionsOrder( "6.1H.5-beta", "6.1.0rc3" ); // now H < RC as of MNG-7559
+ checkVersionsOrder( "6.1.0rc3", "6.1.0" ); // classical
+ checkVersionsOrder( "6.1H.5-beta", "6.1.0" ); // transitivity
}
/**
@@ -338,6 +334,24 @@ public void testReuse()
assertEquals( "reused instance should be equivalent to new instance", c1, c2 );
}
+ /**
+ * Test MNG-7559 edge cases
+ * -pfd < final, ga, release
+ * 2.0.1.MR < 2.0.1
+ * 9.4.1.jre16 > 9.4.1.jre16-preview
+ */
+ public void testMng7559()
+ {
+ // checking general cases
+ checkVersionsOrder(
+ new String[]{ "ab", "alpha", "beta", "cd", "ea", "milestone", "mr", "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-sp-1", "1-ga-1" ); // proving website documentation right.
+ }
+
/**
* Test MNG-7644 edge cases
* 1.0.0.RC1 < 1.0.0-RC2 and more generally:
@@ -345,7 +359,8 @@ public void testReuse()
*/
public void testMng7644()
{
- for ( String x : new String[]{ "abc", "alpha", "a", "beta", "b", "def", "milestone", "m", "RC" } ) {
+ for ( String x : new String[]{ "abc", "alpha", "a", "beta", "b", "def", "milestone", "m", "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.X == 2-X == 2.0.0.X for any string x
diff --git a/maven-artifact/src/test/java/org/apache/maven/artifact/versioning/DefaultArtifactVersionTest.java b/maven-artifact/src/test/java/org/apache/maven/artifact/versioning/DefaultArtifactVersionTest.java
index fc5a2742edf3..01e473693f9b 100644
--- a/maven-artifact/src/test/java/org/apache/maven/artifact/versioning/DefaultArtifactVersionTest.java
+++ b/maven-artifact/src/test/java/org/apache/maven/artifact/versioning/DefaultArtifactVersionTest.java
@@ -121,7 +121,7 @@ public void testVersionComparing()
assertVersionOlder( "2.0-1", "2.0.1" );
assertVersionOlder( "2.0.1-klm", "2.0.1-lmn" );
- assertVersionOlder( "2.0.1", "2.0.1-xyz" );
+ assertVersionOlder( "2.0.1-xyz", "2.0.1" ); // now 2.0.1-xyz < 2.0.1 as of MNG-7559
assertVersionOlder( "2.0.1-xyz-1", "2.0.1-1-xyz" );
assertVersionOlder( "2.0.1", "2.0.1-123" );