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:
in something-X, where X is \ [1..9\], X will be a buildnumberWiki Markup - 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
- well-known qualifiers (case insensitive)
- 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-alpha1 => [1, 0, \ ["alpha", 1\]\]unmigrated-wiki-markupWiki 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:
1.0 => \ [1, 0\]Wiki Markup Wiki Markup - 1.0.1 => \ [1, 0, 1\]unmigrated-wiki-markup
- 1-SNAPSHOT => \[[1, \ ["SNAPSHOT"\]\]
1-alpha10-SNAPSHOT => \ [1, \ ["alpha", "10", \ ["SNAPSHOT"\]\]\]Wiki Markup
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, |
String | Integer is newer | order by well-known | 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) |
null | If integer==0 then equal, | Compare with "" | Compare with empty list item (recursion) | 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 | ||||
---|---|---|---|---|
|