Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: added link to MNG-3010

issue tracked as MNG-3010

Improve default support for version schemes

...

  • It only supports the following schemes:
    • 'positiveInteger-buildnumber' where buildnumber doesn't start with '0' and has to be
    • 'positiveinteger-qualifier'
    • positiveInteger(.positiveInteger(.positiveInteger))-(buildNr|qualifier)
  • Inconsistent/unintuitive parsing:
    • Wiki Markupin something-X, where X is \ [1..9\], X will be a buildnumber
    • in something-0X, where X is any string, '0X' will be a qualifier
    • something.0something will yield 0.0.0.0-something.0something
    • something.NaN will also yield 0.0.0.0-something.NaN
  • getBuildNumber returns '0' when no buildnumber is specified, yet you can never specify 0 as a buildnumber
  • qualifiers are sorted lexically
  • if a qualifier is a prefix of another, the shorter one is considered newer (example:'1.0-alpha10' is considered older than '1.0-alpha1')

...

I'm proposing the following implementation: GenericArtifactVersion.java (unit test: GenericArtifactVersionTest.java). It has been integrated in artifact 3.0-SNAPSHOT r656775(15/5/2008) as ComparableVersion.java.

Features:

  • Mixing of '-' (dash) and '.' (dot) separators
  • Transition between characters and digits also constitutes a separator:
      unmigrated-wiki-markup
    • 1.0alpha1 => \ [1, 0, alpha, 1\]; This fixes '1.0alpha10 < 1.0alpha2'
  • 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)
      • snapshot (NOTE; snapshot needs discussion)
      • alpha or a
      • beta or b
      • milestone or m
      • rc or cr
      • snapshot (NOTE; snapshot needs discussion)
      • (the empty string) or ga or final
      • sp
  • version components prefixed with '-' will result in a sub-list of version components.
    A dash usually precedes a qualifier, and is always less important than something preceded with a dot.
    We need to somehow record the separators themselves, which is done by sublists.
    Parse examples:
    • Wiki Markup1.0-alpha1 => \1.0-alpha1 => [1, 0, \ ["alpha", 1\]\]unmigrated-wiki-markup
    • 1.0-rc-2 => \ [1, 0, \ ["rc", \ [2\]\]\]

Parsing versions

The version string is examined one character at a time.
There's a buffer containing the current text - all characters are appended, except for '.' and '-'.
Below, when it's stated 'append buffer to list', the buffer is  first converted to an Integer item if that's possible, otherwise left alone as a String. It will only be appended if it's length is not 0.

  • If a '.' is encountered, the current buffer is appended to the current list, either as a IntegerItem (if it's a number) or a StringItem.
  • If a '-' is encountered, do the same as when a '.' is encountered, then create a new sublist, append it to the current list and replace the current list with the new sub-list.
  • If the last character was a digit:
    • and the current one is too, append it to the buffer.
    • otherwise append the current buffer to the list, reset the buffer with the current char as content
  • if the last character was NOT a digit:
    • if the last character was also NOT a digit, append it to the buffer
    • if it is a digit, append buffer to list, set buffers content to the digit
  • finally, append the buffer to the list

Some examples:

  • Wiki Markup1.0 => \ [1, 0\] Wiki Markup
  • 1.0.1 => \ [1, 0, 1\]unmigrated-wiki-markup
  • 1-SNAPSHOT => \[[1, \ ["SNAPSHOT"\]\]
  • Wiki Markup1-alpha10-SNAPSHOT => \ [1, \ ["alpha", "10", \ ["SNAPSHOT"\]\]\]

Ordering algorithm

Internally 3 version component types are used:

...

 

Integer

String

List

null

Integer

Highest is newer

Integer is newer

Integer is newer

If integer==0 then equal,
otherwise integer is newer

String

Integer is newer

order by well-known
qualifiers and lexically
(see below)

List is newer

 Compare with ""

List

Integer is newer

List is newer 

Version itself is a list; compare item by item

Compare with empty list item (recursion)
this will finally result in String==?null or
Integer==?null

null

If integer==0 then equal,
otherwise integer is newer

Compare with "" 

Compare with empty list item (recursion)
this will finally result in String==?null or
Integer==?null

doesn't happen

...

Make version handling pluggable (not implemented)

...

Pluggable version handling is not implemented in Maven core dependency resolution since considered hardly manageable,
but it is implemented in versions-maven-plugin for its versions management tasks.
See the version numbers rules documentation.

When somebody devices a version scheme that cannot be handled by the above, it should be possible to plug in a new scheme. Two possible scenarios for unsupported schemes:

...

To make version schemes pluggable, the following is required:

  • A POM change to support something like this to identify a version-scheme implementation artifact:

    No Format
    <versionScheme>
       <groupId>..</groupId>
       <artifactId>..</artifactId>
       <version>..</version> <\!-\-  we may need to disallow version ranges here \-->
    </versionScheme>
    
  • Maven-metadata at the artifact level needs to include the tag above. We'll limit version schemes on a per artifact basis. This is required in order to resolve versions using ranges.
  • An interface definition for VersionScheme
  • A way to detect what is the version class inside the version-scheme artifact; I hope we can use plexus, as long as multiple version-scheme implementations (same hint, same package/classname) can be accessed simultaneously without conflict.
  • Refactoring the version code out of maven-artifact so plugin code etc. can use it too
  • The super pom will contain a default versionScheme tag listing the maven internal implementation

...

As an example here's an XSD you could use to describe versions:

No Format
<xs:schema>
  <xs:element name="versionSchemeDefinition">
    <xs:complexType>
      <xs:sequence>
        <!-- the order of qualifierDefinitions is from oldest to newest -->
        <xs:element ref="qualifierDefinition" minOccurs="0" maxOccurs="unbounded"/>
        <xs:choice maxOccurs="unbounded" minOccurs="0">
          <xs:element ref="stringComponent"/>
          <xs:element ref="numberComponent"/>
          <xs:element ref="subComponent"/>
        </xs:choice>
      </xs:sequence>
    </xs:complexType>
  </xs:element>

  <xs:element name="qualifierDefinition">
    <xs:complexType>
      <xs:attribute name="name" type="xs:string"/>
      <xs:attribute name="caseSensitive" type="xs:boolean" default="false"/>
    </xs:complexType>
  </xs:element>

  <xs:element name="stringComponent">
    <xs:complexType>
      <xs:attribute name="name" type="xs:string"/>
      <xs:attribute name="prefix" type="xs:string" default="."/>
    </xs:complexType>
  </xs:element>

  <xs:element name="numberComponent" type="xs:int">
    <xs:complexType>
      <xs:attribute name="name" type="xs:string"/>
      <xs:attribute name="prefix" type="xs:string" default="."/>
      <xs:attribute name="optional" type="xs:boolean" default="false"/>
    </xs:complexType>
  </xs:element>

  <xs:element name="subComponent">
    <xs:complexType>
      <xs:choice maxOccurs="unbounded" minOccurs="0">
        <xs:element ref="stringComponent"/>
        <xs:element ref="numberComponent"/>
        <xs:element ref="subComponent"/>
      </xs:choice>
      <xs:attribute name="name" type="xs:string"/>
      <xs:attribute name="prefix" type="xs:string" default="-"/>
    </xs:complexType>
  </xs:element>
</xs:schema>

...

References and Related Material

Anchor
osgi
osgi
[0] OSGi Service Platform Release 4 Version 4.1 Core Specification, Wiki Markup<ac:structured-macro ac:name="anchor" ac:schema-version="1" ac:macro-id="b7971247-39a9-4416-b1c9-90a8c570aa0f"><ac:parameter ac:name="">osgi</ac:parameter></ac:structured-macro> \[0\] [OSGi Service Platform Release 4 Version 4.1 Core Specification|http://www.osgi.org/Release4/Download], §3.2.4 "Version" and §3.2.5 "Version Ranges" on page 38, §3.5.3 "Bundle-Version" on page 46, §6.1.26.5 "Version.compareTo()" on page 200