You are viewing an old version of this page. View the current version.

Compare with Current View Page History

Version 1 Next »

Improve default support for version schemes

The current implementation for version schemes is rather limited. It only supports 5 properties:

  1. Major versoin
  2. Minor version
  3. Incremental version (bugfix)
  4. Build number
  5. A Qualifier.

Flaws:

  • 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:
    • in 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 longest one wins (example:'1.0-alpha10' is considered older than '1.0-alpha2')

Proposal

I'm proposing the following implementation: GenericArtifactVersion.java (unit test: GenericArtifactVersionTest.java)

Features:

  • Mixing of '-' and '.' separators
  • Transition between characters and digits also constitutes a separator:
    • 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
      • beta
      • rc
      • (the empty string)
      • ga
      • 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:
    • 1.0-alpha1 => [1, 0, ["alpha", 1]]

    • 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:

  • 1.0 => [1, 0]

  • 1.0.1 => [1, 0, 1]

  • 1-SNAPSHOT => [1, ["SNAPSHOT"]]

  • 1-alpha10-SNAPSHOT => [1, ["alpha", "10", ["SNAPSHOT"]]]

Ordering algorithm

Internally 3 version component types are used:

  • integer (IntegerItem)
  • string (StringItem) (knows if it's a qualifier or not)
  • sublist (ListItem)

Elements from both versions are compared one at a time; first the first element of both, then the second, etc.

(Note: 'item' and 'component' are used interchangeably)

ordering rules when comparing version components:

 

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

Special note on string comparing:
A predefined list of well-known qualifiers is present. For comparison, the string is converted to another string, as follows:

  • First, the well-known qualifier list is consulted for presence of the string
  • If the string is present, the index in the list is returned, as a string
  • If the string is not present, then qualifiers.size() + "-" + string is returned.

Then the strings are lexically compared.

Examples:

  • "alpha" yields "1"
  • "" yields "4"
  • "abc" yields "7-abc"
  • "xyz" yields "7-xyz"

String Compare examples:

  • 1.0 ==? 1.0-alpha: "" (or null) ==? "alpha" -> "4" ==? "1" -> 1.0 is newer
  • 1 ==? 1.0: equal
  • 1-beta ==? 1-xyz: "2" ==? "7-xyz" -> 1-xyz is newer

Some comparisons that yield different results from the current implementation:

  • 1-beta ==? 1-abc: "2" ==? "7-abc" -> 1-abc is newer
  • 1.0 ==? 1.0-abc: "4" ==? "7-abc" -> 1.0-abc is newer
  • 1.0-alpha-10 ==? 1.0-alpha-2: 10 > 2, so '1.0-alpha-10' is newer
  • 1.0-alpha-1.0 ==? 1.0-alpha-1: equal
  • 1.0-alpha-1.2 ==? 1.0-alpha-2: 1.0-alpha-2 is newer

Make version handling pluggable

tbd

Define a grammar for version specifications

tbd

  • No labels