The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Java::Maven::Artifact::Version - a perl module for comparing Artifact versions exactly like Maven does.

VERSION

Version 1.000001

see "MAVEN VERSION COMPATIBILITY".

SYNOPSIS

Note that this documentation is intended as a reference to the module.

    use Java::Maven::Artifact::Version qw/version_compare version_parse/;

    my $y = version_compare('1-alpha', '1-beta'); # $y = -1 
    my $x = version_compare('1.0', '1-0.alpha'); # $x = 0

    my $z = version_parse('1-1.2-alpha'); # $z = '(1,(1,2,alpha))' 
    my @l = version_parse('1-1.2-alpha'); # @l = (1,[1,2,'alpha'])

DESCRIPTION

Apache Maven has a peculiar way to compare Artifact versions. The aim of this module is to exactly reproduce this way in hope that it could be usefull to someone that wants to write utils like SCM hooks. It may quickly ensure an Artifact version respect a grow order without to have to install Java and Maven on the system in charge of this checking.

The official Apache document that describes it is here http://docs.codehaus.org/display/MAVEN/Versioning. But don't blindly believe everything. Take the red pill, and I show you how deep the rabbit-hole goes. Because there is a gap between the truth coded in org.apache.maven.artifact.versioning.ComparableVersion.java that can be found here and that Maven official document.

Lucky for you this module cares about the real comparison differences hard coded in ComparableVersion and reproduces it.

see "FAQ" for details.

SUBROUTINES

version_compare

By default version_compare compares a version string to another one exactly like Maven does.

See http://docs.codehaus.org/display/MAVEN/Versioning for general comparison description, and "DESCRIPTION" for more details about mechanisms not described in that official Maven doc but occur during Maven Artifact versions comparison in Java.

This function will return :

  • 0 if versions compared are equal

  • 1 if version is greater than version that is compared to

  • -1 if version is lower than version that is compared to

    $v = version_compare('1.0', '1.1'); # $v = -1

version_compare can go further. You can set max_depth to stop comparison before the whole version comparison has processed.

Suppose you have to code SCM hook which enforce that pushed artifact source must always begin by the same two version items and new version must be greater than the old one.

    my ($old, $new) = ('1.1.12', '1.1.13');
    my $common = version_compare($old, $new, 2); # returns 0 here
    die "you did not respect the version policy" if $common; 
    die "you must increment artifact version" if version_compare($old, $new) >= 0;

Note that max_depth cares about sub listitems.

    $v = '1-1.0.sp; # normalized to (1,(1,0,'sp'))
    $o = '1-1-SNAPSHOT'; # normalized to (1,(1,'SNAPSHOT'))
    $x = version_compare($v, $o, 3); # will compare '0' to 'SNAPSHOT' and will return 1

Of course understand that this computation is done after normalization.

    $x = version_compare('1-1.0-1-ga-0-1.2', '1-1.0-1-ga-0-1.3', 4); #only last item will be ignored during this comparison
    #                     ^ ^   ^      ^      ^ ^   ^      ^

Note that set negative max_depth will always return 0, because no comparison will be done at all

    $x = version_compare(1, 2, -1); # $x = 0

version_parse

will return normalized version representation (see "Normalization").

In scalar context, it will return string representation :

    $s = version_parse('1.0-final-1'); # $s = '(1,(,1))'

You would have the same string if you had call org.apache.maven.artifact.versioning.ComparableVersion.ListItem.toString() private method of org.apache.maven.artifact.versioning.ComparableVersion.java on the main ListItem.

In list context, it will return the data structure representation :

    @l = version_parse('1.0-final-1'); # [1,['',1]]

FAQ

What are differences between actual Maven comparison algo and that described in the official Maven doc ?

zero appending on blank separator

zero ('0') will be appended on each blank separator char (dot '.' or dash '-') During parsing if separator char is encountered and it was not preceded by stringitem or listitem, zero char ('0') is automatically appended. Then version that begins with separator is automatically prefixed by zero.

'-1' will be internally moved to '0-1'.

'1....1' will be internally moved to '1.0.0.0.1'.

The dash separator "-"

The dash separator "-" will create listitem only if it is preceeded by an integeritem and it is followed by digit.

Then when they say 1-alpha10-SNAPSHOT => [1,["alpha",10,["SNAPSHOT"]]] understand that it's wrong.

1-alpha10-SNAPSHOT is internally represented by [1,"alpha",10,"SNAPSHOT"]. Which has a fully different comparison behavior because no sub listitem is created.

Please note that "zero appending on blank separator" has been done after listitem splitting.

Then understand that '-1--1' will NOT be internally represented by '(0,(1,(0,(1))', but by '(0,1,0,1)'.

Normalization

Normalization is one of the most important part of version comparison but it is not described at all in the official Maven document. So what is normalization ? It's kind of reducing version components function. Its aim is to shoot useless version components in artifact version. To simplify it, understand that 1.0 must be internally represented by 1 during comparison. But normalization appends in specific times during artifact version parsing.

It appends:

1. each time a dash '-' separator is preceded by digit but before any alias substitution (except when any of these digits is a zero appended, because listitem splitting is done before 'zero appending').
2. at the end of each parsed listitem, then after all alias substitution

And normalization process current parsed listitem from current position when normalization is called, back to the beginning of this current listitem.

Each encountered nullitem will be shot until a non nullitem is encountered or until the begining of this listitem is reached if all its items are nullitems. In this last case precisely, the empty listitem will be shot except if it is the main one.

Then understand that :

  • 1.0.alpha.0 becomes (1,0,alpha) #because when main listitem parsing has ended, normalization has been called. Last item was 0, 0 is the nullitem of integeritem, then it has been shooted. Next last item was alpha that is not nullitem then normalization process stopped.

  • 1.0-final-1 becomes (1,,1) #because a dash has been encoutered during parsing. Then normalization has been called because it was preceded by a digit and last item in the current listitem is 0. Then it has been shot. final has been substituted by '' but when next normalization has been called, at the end of the parsing, the last item was not nullitem, then normalization did not meet ''.

  • 0.0.ga becomes () # because 'ga' has been substituted by '' and when listitem has been normalized at the end, all items where nullitems

  • final-0.1 becomes (,0,1) # because normalization has not been called after first dash because it was not been preceded by digit.

If you told me WTF ?, I would answer I am not responsible of drug consumption...

In org.apache.maven.artifact.versioning.ComparableVersion.java, the representation of normalized version is only displayable with the call of org.apache.maven.artifact.versioning.ComparableVersion.ListItem.toString() private method on the main ListItem.

Comma "," is used as items separator, and enclosing braces are used to represent ListItem.

For example: in Java world org.apache.maven.artifact.versioning.ComparableVersion.ListItem.toString() on "1-0.1" gives "(1,(0,1))".

"version_parse" function reproduces this algo for the whole set Java::Maven::Artifact::Version.

    $v = version_parse('1-0.1'); # $v = '(1,(O,1))'

listitem and nullitem comparison

It is not very clear in the official Maven doc.

Comparing listitem with nullitem will just compare first item of the listitem with nullitem.

MAVEN VERSION COMPATIBILITY

This version is fully compatible with the org.apache.maven.artifact.versioning.ComparableVersion.java algo of org.apache.maven:maven-artifact embedded with :

  • Maven 3.2.3

  • Maven 3.2.2

All Test::More tests are also available with Java Junit tests to ensure comparison results are similars.

See "SOURCE" if you want to check them.

I will do my best to check the Maven compatibility on each Maven new release.

AUTHOR

Thomas Cazali, <pandragon at cpan.org>

SOURCE

The source code repository for Java::Maven::Artifact::Version can be found at https://github.com/apendragon/Java-Maven-Artifact-Version/

BUGS

Please report any bugs or feature requests to https://github.com/apendragon/Java-Maven-Artifact-Version/issues.

SUPPORT

You can find documentation for this module with the perldoc command.

    perldoc Java::Maven::Artifact::Version

You can also look for information at:

https://github.com/apendragon/Java-Maven-Artifact-Version/wiki

LICENSE AND COPYRIGHT

Copyright 2014 Thomas Cazali.

This program is free software; you can redistribute it and/or modify it under the terms of the the Artistic License (2.0). You may obtain a copy of the full license at:

http://www.perlfoundation.org/artistic_license_2_0

Any use, modification, and distribution of the Standard or Modified Versions is governed by this Artistic License. By using, modifying or distributing the Package, you accept this license. Do not use, modify, or distribute the Package, if you do not accept this license.

If your Modified Version has been derived from a Modified Version made by someone other than you, you are nevertheless required to ensure that your Modified Version complies with the requirements of this license.

This license does not grant you the right to use any trademark, service mark, tradename, or logo of the Copyright Holder.

This license includes the non-exclusive, worldwide, free-of-charge patent license to make, have made, use, offer to sell, sell, import and otherwise transfer the Package with respect to any patent claims licensable by the Copyright Holder that are necessarily infringed by the Package. If you institute patent litigation (including a cross-claim or counterclaim) against any party alleging that the Package constitutes direct or contributory patent infringement, then this Artistic License to you shall terminate on the date that such litigation is filed.

Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.