J Fabian Meier
J Fabian Meier

Reputation: 35823

Maven version ordering for alpha and a

The answer https://stackoverflow.com/a/31482463/927493 explains the order of Maven versions. Following https://blog.soebes.de/blog/2017/02/04/apache-maven-how-version-comparison-works/, you can compare versions on the command line. I got the following results:

C:\coprogramme\apache-maven-3.5.0\lib>java -jar maven-artifact-3.5.0.jar 2.0.0 2.0.0-a
Display parameters as parsed by Maven (in canonical form) and comparison result:
1. 2.0.0 == 2
   2.0.0 < 2.0.0-a
2. 2.0.0-a == 2-a

C:\coprogramme\apache-maven-3.5.0\lib>java -jar maven-artifact-3.5.0.jar 2.0.0 2.0.0-alpha
Display parameters as parsed by Maven (in canonical form) and comparison result:
1. 2.0.0 == 2
   2.0.0 > 2.0.0-alpha
2. 2.0.0-alpha == 2-alpha

Following the answer and also the Javadoc in https://github.com/apache/maven/blob/master/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/ComparableVersion.java, the version parts alpha and a should be equal, which is apparently not the case.

Can anyone explain this to me?

Upvotes: 0

Views: 1399

Answers (1)

Jelaby
Jelaby

Reputation: 1187

Despite what the documentation says, it appears that a is a synonym for alpha only when it is immediately followed by a digit. So

  • 2.0.0-a1 is equivalent to 2.0.0-alpha1 and 2.0.0-alpha-1 (but not 2.0.0-alpha.1)
  • the a in 2.0.0-a, 2.0.0-a.1 or 2.0.0-a-1 is an "unknown qualifier", not equivalent to 2.0.0-alpha-1, and sorted after all the known qualifiers

The behaviour appears because ComparableVersion has two ways of defining aliases, in the inner class that deals with string components:

private static final List<String> QUALIFIERS =
        Arrays.asList( "alpha", "beta", "milestone", "rc", "snapshot", "", "sp"  );

private static final Properties ALIASES = new Properties();
static
{
    ALIASES.put( "ga", "" );
    ALIASES.put( "final", "" );
    ALIASES.put( "release", "" );
    ALIASES.put( "cr", "rc" );
}

// ...

StringItem( String value, boolean followedByDigit )
{
    if ( followedByDigit && value.length() == 1 )
    {
        // a1 = alpha-1, b1 = beta-1, m1 = milestone-1
        switch ( value.charAt( 0 ) )
        {
            case 'a':
                value = "alpha";
                break;
            case 'b':
                value = "beta";
                break;
            case 'm':
                value = "milestone";
                break;
            default:
        }
    }
    this.value = ALIASES.getProperty( value , value );
}
  1. A special-case scenario for single-letter qualifiers a, b, and m, that is only triggered if followedByDigit==true, and that only happens when the qualifier was terminated by the next character being a digit.
  2. A list of aliases: ga, final and release are exactly equivalent to the empty string (with the latter being the canonical form), and cr's canonical form is rc. This part behaves as the OP expects.

I would guess that this is based on observed version usage, where sometimes letters are used literally as versions (e.g. OpenSSL used to use a trailing letter or two to number patch releases, such as 0.9.7m or 0.9.8zh), while alpha, beta and milestone releases always have a release number in practice, and projects using a short version aren't going to put any extra characters in: so a by itself meaning "alpha" never actually occurs.

It's a shame it's not documented properly!

Upvotes: 4

Related Questions