File Coverage

blib/lib/Java/Maven/Artifact/Version.pm
Criterion Covered Total %
statement 172 191 90.0
branch 63 80 78.7
condition 15 21 71.4
subroutine 51 57 89.4
pod 2 2 100.0
total 303 351 86.3


line stmt bran cond sub pod time code
1             package Java::Maven::Artifact::Version;
2              
3 7     7   183796 use 5.006;
  7         28  
  7         384  
4 7     7   45 use strict;
  7         15  
  7         279  
5 7     7   38 use warnings FATAL => 'all';
  7         22  
  7         371  
6 7     7   38 use Exporter;
  7         14  
  7         335  
7 7     7   50 use Scalar::Util qw/reftype/;
  7         12  
  7         804  
8 7     7   40 use Carp;
  7         13  
  7         1567  
9              
10             our @ISA = qw/Exporter/;
11             our @EXPORT_OK = qw/&version_parse &version_compare/;
12             =head1 NAME
13              
14             Java::Maven::Artifact::Version - a perl module for comparing Artifact versions exactly like Maven does.
15              
16             =head1 VERSION
17              
18             Version 1.00
19              
20             see L.
21              
22             =cut
23              
24             our $VERSION = '1.00';
25              
26             =head1 SYNOPSIS
27              
28             Note that this documentation is intended as a reference to the module.
29              
30             use Java::Maven::Artifact::Version qw/version_compare version_parse/;
31              
32             my $y = version_compare('1-alpha', '1-beta'); # $y = -1
33             my $x = version_compare('1.0', '1-0.alpha'); # $x = 0
34              
35             my $z = version_parse('1-1.2-alpha'); # $z = '(1,(1,2,alpha))'
36             my @l = version_parse('1-1.2-alpha'); # [1,[1,2,'alpha']]
37              
38             =head1 DESCRIPTION
39              
40             L has a peculiar way to compare Artifact versions.
41             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.
42              
43             The official Apache document that describes it is here L.
44             But don't blindly believe everything. Take the red pill, and I show you how deep the rabbit-hole goes.
45             Because there is a gap between the truth coded in C that can be found L and that Maven official document.
46              
47             Lucky for you this module cares about the real comparison differences hard coded in C and reproduces it.
48              
49             see L for details.
50              
51             =cut
52              
53             use constant {
54 7         27439 _ALPHA => 'alpha',
55             _BETA => 'beta',
56             _DEBUG => 0,
57             _INTEGER_ITEM => 'integeritem',
58             _LIST_ITEM => 'listitem',
59             _MILESTONE => 'milestone',
60             _NULL_ITEM => 'nullitem',
61             _RC => 'rc',
62             _SNAPSHOT => 'snapshot',
63             _SP => 'sp',
64             _STRING_ITEM => 'stringitem',
65             _UNDEF => 'undef'
66 7     7   41 };
  7         20  
67              
68             =head1 SUBROUTINES
69              
70             =cut
71              
72             # replace all following separators ('..', '--', '-.' or '.-') by .0.
73             # or replace leading separator by '0.'
74             # example : '-1..1' -> '0.1.0.1'
75             sub _append_zero {
76 203 100   203   752 join '.', map { $_ eq '' ? '0' : $_ } split /\-|\./, shift;
  283         1028  
77             }
78              
79             sub _check_comparison_settings {
80 0     0   0 my ($settings) = @_;
81 0 0       0 croak("'version' mandatory parameter is missing") if not exists $settings->{version};
82 0 0 0     0 carp("'max_depth' should be >= 0") if (exists $settings->{max_depth} && $settings->{max_depth} <= 0);
83             }
84              
85             sub _compare_integeritem_to {
86 75     75   99 my ($integeritem, $item, $depth) = @_;
87             my $dispatch = {
88             &_NULL_ITEM => sub {
89 5     5   4 print("comparing $integeritem to nullitem\n") if (_DEBUG);
90 5         6 $$depth++;
91 5 100       85 $integeritem =~ m/^0+$/ ? 0 : 1;
92             },
93             &_LIST_ITEM => sub {
94 1     1   3 print("comparing $integeritem to listitem\n") if (_DEBUG);
95 1         11 1;
96             },
97             &_INTEGER_ITEM => sub {
98 64     64   67 print("comparing $integeritem to $item\n") if (_DEBUG);
99 64         76 $$depth++;
100 64         761 $integeritem <=> $item;
101             },
102             &_STRING_ITEM => sub {
103 5     5   9 print("comparing $integeritem to stringitem\n") if (_DEBUG);
104 5         66 1;
105             }
106 75         731 };
107 75         233 $dispatch->{_identify_item_type($item)}->();
108             }
109              
110             sub _compare_items {
111 126     126   208 my ($item1, $item2, $max_depth, $depth) = @_;
112             my $dispatch = {
113             &_NULL_ITEM => sub {
114 0     0   0 print("_compare_items(nullitem, ?)\n") if (_DEBUG);
115 0 0       0 unless (defined($item2)) {
116 0         0 $$depth++;
117 0         0 return 0 ;
118             }
119 0         0 _compare_items($item2, undef, $depth) * -1;
120             },
121             &_LIST_ITEM => sub {
122 32     8   29 print("_compare_items(listitem, ?)\n") if (_DEBUG);
123 32         76 _compare_listitem_to($item1, $item2, $max_depth, $depth);
124             },
125             &_INTEGER_ITEM => sub {
126 74     44   72 print("_compare_items(integeritem, ?)\n") if (_DEBUG);
127 74         134 _compare_integeritem_to($item1, $item2, $depth);
128             },
129             &_STRING_ITEM => sub {
130 20     17   23 print("_compare_items(stringitem, ?)\n") if (_DEBUG);
131 20         82 _compare_stringitem_to($item1, $item2, $depth);
132             }
133 126         1580 };
134 126         300 $dispatch->{_identify_item_type($item1)}->();
135             }
136              
137             sub _compare_listitem_to {
138 33     33   47 my ($listitem, $item, $max_depth, $depth) = @_;
139             my $dispatch = {
140 3     1   11 &_NULL_ITEM => sub { _compare_listitem_to_nullitem($listitem, $max_depth, $depth) },
141 26     6   117 &_LIST_ITEM => sub { _compare_listitems($listitem, $item, $max_depth, $depth) },
142 1     0   10 &_INTEGER_ITEM => sub { -1 },
143 3     2   40 &_STRING_ITEM => sub { 1 }
144 33         293 };
145 33         70 $dispatch->{_identify_item_type($item)}->();
146             }
147              
148             sub _compare_listitem_to_nullitem {
149 3     3   6 my ($listitem, $max_depth, $depth) = @_;
150 3 50       9 if (not @$listitem) {
151 0         0 warn("comparing listitem with empty listitem should never occur. Check your code boy...");
152 0         0 0; #empty listitem (theoricaly impossible) equals null item
153             } else {
154             #only compare first element with null item (yes they did that...)
155 3         12 _compare_items(@$listitem[0], undef, $max_depth, $depth);
156             }
157             }
158              
159             sub _compare_listitems {
160 70     70   101 my ($list1, $list2, $max_depth, $depth) = @_;
161 70         140 my @l = @$list1;
162 70         104 my @r = @$list2;
163 70   100     198 while (@l || @r) {
164 128 100 100     395 last if ($max_depth && $$depth >= $max_depth);
165 123 100       272 my $li = @l ? shift(@l) : undef;
166 123 100       266 my $ri = @r ? shift(@r) : undef;
167 123 100       352 my $c = defined($li) ? _compare_items($li, $ri, $max_depth, $depth) : _compare_items($ri, $li, $max_depth, $depth) * -1;
168 123         148 print("depth is $$depth\n") if (_DEBUG);
169 123 100       764 $c and return $c;
170             }
171 15         100 0;
172             }
173              
174             sub _compare_stringitem_to {
175 20     20   35 my ($stringitem, $item , $max_depth, $depth) = @_;
176             my $dispatch = {
177 5     5   14 &_NULL_ITEM => sub { _compare_stringitem_to_stringitem($stringitem, $item, $depth) },
178 1     1   4 &_LIST_ITEM => sub { _compare_listitem_to($item, $stringitem, $max_depth, $depth) * -1 },
179 1     1   2 &_INTEGER_ITEM => sub { _compare_integeritem_to($item, $stringitem, $depth) * -1 },
180 13     13   28 &_STRING_ITEM => sub { _compare_stringitem_to_stringitem($stringitem, $item, $depth) }
181 20         224 };
182 20         59 $dispatch->{_identify_item_type($item)}->();
183             }
184              
185             sub _compare_stringitem_to_stringitem {
186 18     18   26 my ($stringitem1, $stringitem2, $depth) = @_;
187 18         26 $$depth++;
188 18         41 _substitute_to_qualifier($stringitem1) cmp _substitute_to_qualifier($stringitem2);
189             }
190              
191             sub _compare_to_mvn_version {
192 0     0   0 my ($this, $another_version, $max_depth) = @_;
193 0 0       0 die("parameter is not a Java::Maven::Artifact::Version") unless ($another_version->isa('Java::Maven::Artifact::Version'));
194 0         0 my $depth = 0;
195 0         0 _compare_listitems($this->{items}, $another_version->{items}, $max_depth, \$depth);
196             }
197              
198             sub _get_version {
199 0     0   0 my ($version) = @_;
200 0 0       0 ref($version) eq 'Java::Maven::Artifact::Version' ? $version : Java::Maven::Artifact::Version->new(version => $version);
201             }
202              
203             sub _getref {
204 482     482   677 my ($var) = @_;
205 482 100 66     3060 (ref($var) || not defined($var)) ? $var : \$var; # var may already be a ref
206             }
207              
208             sub _identify_item_type {
209 254     254   419 my ($item) = @_;
210             my $types = {
211 13     13   79 _UNDEF() => sub { _NULL_ITEM },
212 181     181   308 'SCALAR' => sub { _identify_scalar_item_type($item) },
213 60     60   398 'ARRAY' => sub { _LIST_ITEM },
214 0     0   0 _DEFAULT_ => sub { die "unable to identify item type of item $item ." }
215 254         2169 };
216 254         536 my $t = _reftype($item);
217 254         381 print("_identify_item_type($t)\n") if (_DEBUG);
218 254 50       682 exists $types->{$t} ? $types->{$t}->() : $types->{_DEFAULT_}->();
219             }
220              
221             sub _identify_qualifier {
222 36     36   43 my ($stringitem) = @_;
223 36 100       148 return _NULL_ITEM unless defined($stringitem);
224 31 100       142 return _ALPHA if $stringitem =~ m/^(alpha|a\d+)$/;
225 28 100       114 return _BETA if $stringitem =~ m/^(beta|b\d+)$/;
226 25 100       106 return _MILESTONE if $stringitem =~ m/^(milestone|m\d+)$/;
227 21 100       74 return _RC if $stringitem =~ m/^rc$/;
228 17 50       170 return _SNAPSHOT if $stringitem =~ m/^snapshot$/;
229 17 100       61 return _NULL_ITEM if $stringitem =~ m/^$/;
230 15 100       51 return _SP if $stringitem =~ m/^sp$/;
231 11         131 '_DEFAULT_';
232             }
233              
234             sub _identify_scalar_item_type {
235 181     181   222 my ($scalar) = @_;
236 181 100       1509 $scalar =~ m/^\d+$/ ? _INTEGER_ITEM : _STRING_ITEM;
237             }
238              
239             sub _is_nullitem {
240 254     254   282 my ($item) = @_;
241 254 100       742 (not defined($item)) ? 1 : _UNDEF eq reftype(_getref($item));
242             }
243              
244             sub _normalize {
245 382     382   489 my ($listitems) = @_;
246 382         465 my $norm_sublist;
247 382 100       1069 if (ref(@$listitems[-1]) eq 'ARRAY') {
248 71         91 my $sublist = pop(@$listitems);
249 71         169 $norm_sublist = _normalize($sublist);
250             }
251 382   100     2685 pop(@$listitems) while (@$listitems && @$listitems[-1] =~ m/^(0+|ga|final)?$/ );
252 382 100 100     987 push(@$listitems, $norm_sublist) if (defined($norm_sublist) && @$norm_sublist);
253 382         1209 $listitems;
254             }
255              
256             sub _reftype {
257 254     254   390 my ($item) = @_;
258 254 100       381 _is_nullitem($item) ? _UNDEF : reftype(_getref($item));
259             }
260              
261             sub _replace_alias {
262 291     291   380 my ($string) = @_;
263 291 50       1106 if ($string eq '') {
    100          
    50          
264 0         0 return 0;
265             } elsif ($string =~ m/^(ga|final)$/) {
266 15         49 return '';
267             } elsif ($string eq 'cr') {
268 0         0 return 'rc';
269             }
270 276         925 $string;
271             }
272              
273             sub _replace_special_aliases {
274 203     203   247 my ($string) = @_;
275 203         296 $string =~ s/((?:^)|(?:\.|\-))a(\d)/$1alpha.$2/g; # a1 = alpha.1
276 203         243 $string =~ s/((?:^)|(?:\.|\-))b(\d)/$1beta.$2/g; # b11 = beta.11
277 203         254 $string =~ s/((?:^)|(?:\.|\-))m(\d)/$1milestone.$2/g; # m7 = milestone.7
278 203         397 $string;
279             }
280              
281             # split 'xxx12' to ['xxx',12] and vice versa
282             sub _split_hybrid_items {
283 276     276   346 my ($string) = @_;
284 276         460 $string =~ s/(\D)(\d)/$1.$2/g;
285 276         323 $string =~ s/(\d)(\D)/$1.$2/g;
286 276         982 split /\./, $string;
287             }
288              
289             # _split_to_items must only be called when version has been splitted into listitems
290             # Then it works only on a single listitem
291             sub _split_to_items {
292 179     179   272 my ($string) = @_;
293 179         273 my @items = ();
294 179         282 my @tonormalize = _split_to_to_normalize($string);
295             #at this time we must replace aliases with their values
296             my $closure = sub {
297 203     203   286 my ($i) = shift;
298 203         359 $i = _append_zero($i);
299 203         616 $i = _replace_special_aliases($i); #must be replaced BEFORE items splitting
300 203         697 my @xs = split(/\-|\./, $i);
301 203         319 @xs = map({ _replace_alias($_) } @xs); #must be replaced after items splitting
  291         468  
302 203 100       312 @xs = map({ $_ !~ /^\s*$/ ? _split_hybrid_items($_) : $_ } @xs);
  291         1008  
303 203         276 push(@items, @{_normalize(\@xs)} );
  203         391  
304 179         910 };
305 179         297 map { $closure->($_) } @tonormalize;
  203         476  
306 179         1388 @items;
307             }
308              
309             sub _split_to_lists {
310 179     179   294 my ($string, @items) = @_;
311             #listitems are created every encountered dash when there are a digits in front and after it
312 179 100       738 if (my ($a, $b) = ($string =~ m/(.*?\d)\-(\d.*)/)) {
313 71         126 push(@items, _split_to_items($a), _split_to_lists($b, ()));
314             } else {
315 108         193 push(@items, _split_to_items($string));
316             }
317 179         434 \@items;
318             }
319              
320             #_normalize must be called each time a digit is followed by a dash
321             sub _split_to_to_normalize {
322 179     179   214 my ($string) = @_;
323 179         388 $string =~ s#(\d)\-#$1#g; # use '' as seperator because it cannot be a part of an artifact version...
324 179         515 split('', $string);
325             }
326              
327             sub _substitute_to_qualifier {
328 36     36   63 my ($stringitem) = @_;
329 36 100       366 my $qualifier_cmp_values = {
330             &_ALPHA => '0',
331             &_BETA => '1',
332             &_MILESTONE => '2',
333             &_RC => '3',
334             &_SNAPSHOT => '4',
335             &_NULL_ITEM => '5',
336             &_SP => '6',
337             _DEFAULT_ => $stringitem ? "7-$stringitem" : '7-' #yes they really did that in ComparableVersion...
338             };
339 36         68 $qualifier_cmp_values->{_identify_qualifier($stringitem)};
340             }
341              
342              
343             sub _to_normalized_string {
344 28     28   39 my ($items) = @_;
345 28         39 my $s = '(';
346             my $append = sub {
347 76     76   97 my ($i) = shift;
348 76 100       183 ref($i) eq 'ARRAY' ? $s .= _to_normalized_string($i) : ($s .= "$i");
349 76         176 $s .= ',';
350 28         111 };
351 28         44 map { $append->($_) } @$items ;
  76         139  
352 28 100       73 chop($s) if (length($s) > 1);
353 28         206 $s .= ')';
354             }
355              
356             =head2 version_compare
357              
358             By default C compares a version string to another one exactly like Maven does.
359              
360             See L for general comparison description, and L for more details about mechanisms not described in that official Maven doc but occur during Maven Artifact versions comparison in Java.
361              
362             This function will return :
363              
364             =over 4
365              
366             =item * C<0> if versions compared are equal
367              
368             =item * C<1> if version is greater than version that is compared to
369              
370             =item * C<-1> if version is lower than version that is compared to
371              
372             =back
373              
374             $v = version_compare('1.0', '1.1'); # $v = -1
375              
376             C can go further. You can set C to stop comparison before the whole version comparison has processed.
377              
378             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.
379              
380             my ($old, $new) = ('1.1.12', '1.1.13');
381             my $common = version_compare($old, $new, 2); # returns 0 here
382             die "you did not respect the version policy" if $common;
383             die "you must increment artifact version" if version_compare($old, $new) >= 0;
384              
385             Note that C cares about sub C.
386            
387             $v = '1-1.0.sp; # normalized to (1,(1,0,'sp'))
388             $o = '1-1-SNAPSHOT'; # normalized to (1,(1,'SNAPSHOT'))
389             $x = version_compare($v, $o, 3); # will compare '0' to 'SNAPSHOT' and will return 1
390              
391             Of course understand that this computation is done B normalization.
392            
393             $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
394             # ^ ^ ^ ^ ^ ^ ^ ^
395              
396             Note that set negative C will always return 0, because no comparison will be done at all
397              
398             $x = version_compare(1, 2, -1); # $x = 0
399              
400             =cut
401              
402             sub version_compare {
403 44     44 1 1148 my ($v1, $v2, $max_depth) = @_;
404 44 50 33     168 return unless defined($v1) || defined($v2);
405 44 100       146 $max_depth = defined $max_depth ? $max_depth : 0;
406 44         62 my $depth = 0;
407 44         91 my @listitem1 = version_parse($v1);
408 44         157 my @listitem2 = version_parse($v2);
409 44         135 _compare_listitems(\@listitem1, \@listitem2, $max_depth, \$depth);
410             }
411              
412             =head2 version_parse
413              
414             will return normalized version representation (see L).
415              
416             In B, it will return string representation :
417              
418             $s = version_parse('1.0-final-1'); # $s = '(1,(,1))'
419              
420             You would have the same string if you had call C private method of C on the main C.
421              
422             In B, it will return the data structure representation :
423              
424             @l = version_parse('1.0-final-1'); # [1,['',1]]
425              
426             =cut
427              
428             sub version_parse {
429 108     108 1 574 my ($v) = @_;
430 108 50       275 return unless defined wantarray;
431 108         301 my $listitem = _normalize(_split_to_lists(lc($v), ()));
432 108 100       430 wantarray ? @$listitem : _to_normalized_string($listitem);
433             }
434              
435             =head1 FAQ
436              
437             =head2 What are differences between actual Maven comparison algo and that described in the official Maven doc ?
438              
439             =head3 zero appending on blank separator
440              
441             zero ('C<0>') will be appended on each blank separator char (dot '.' or dash '-')
442             During parsing if separator char is encountered and it was not preceded by C or C, zero char ('C<0>') is automatically appended.
443             Then version that begins with separator is automatically prefixed by zero.
444              
445             'C<-1>' will be internally moved to 'C<0-1>'.
446              
447             'C<1....1>' will be internally moved to 'C<1.0.0.0.1>'.
448              
449             =head3 The dash separator "B<->"
450              
451             The dash separator "B<->" will create C only if it is preceeded by an C and it is followed by digit.
452              
453             Then when they say C<1-alpha10-SNAPSHOT =E [1,["alpha",10,["SNAPSHOT"]]]> understand that it's wrong.
454              
455             C<1-alpha10-SNAPSHOT> is internally represented by C<[1,"alpha",10,"SNAPSHOT"]>. Which has a fully different comparison behavior because no sub C is created.
456              
457             Please note that L has been done B C splitting.
458              
459             Then understand that 'C<-1--1>' will B be internally represented by 'C<(0,(1,(0,(1))>', but by 'C<(0,1,0,1)>'.
460              
461              
462             =head3 Normalization
463              
464             Normalization is one of the most important part of version comparison but it is not described at all in the official Maven document.
465             So what is I ?
466             It's kind of reducing version components function.
467             Its aim is to shoot useless version components in artifact version. To simplify it, understand that C<1.0> must be internally represented by C<1> during comparison.
468             But I appends in specific times during artifact version parsing.
469              
470             It appends:
471              
472             =over 4
473              
474             =item 1. each time a dash 'C<->' separator is preceded by digit but B any alias substitution (except when any of these digits is a L, because C splitting is done before 'zero appending').
475              
476              
477             =item 2. at the end of each parsed C, then B all alias substitution
478              
479             =back
480              
481             And I process current parsed C from current position when normalization is called, back to the beginning of this current C.
482              
483             Each encountered C will be shot until a non C is encountered or until the begining of this C is reached if all its items are C.
484             In this last case precisely, the empty C will be shot except if it is the main one.
485              
486             Then understand that :
487              
488             =over 4
489              
490             =item * C<1.0.alpha.0> becomes C<(1,0,alpha)> #because when main C parsing has ended, I has been called. Last item was 0, 0 is the C of C, then it has been shooted. Next last item was C that is not C then normalization process stopped.
491              
492             =item * C<1.0-final-1> becomes C<(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 C is 0. Then it has been shot. C has been substituted by C<''> but when next normalization has been called, at the end of the parsing, the last item was not C, then normalization did not meet C<''>.
493              
494             =item * C<0.0.ga> becomes C<()> # because 'ga' has been substituted by C<''> and when C has been normalized at the end, all items where Cs
495              
496             =item * C (,0,1) # because normalization has not been called after first dash because it was not been preceded by digit.
497              
498             =back
499              
500             If you told me I, I would answer I am not responsible of drug consumption...
501              
502             In C, the representation of normalized version is only displayable with the call of C private method on the main C.
503              
504             Comma "C<,>" is used as items separator, and enclosing braces are used to represent C.
505              
506             For example:
507             in Java world C on C<"1-0.1"> gives C<"(1,(0,1))">.
508              
509             L function reproduces this algo for the whole set C.
510              
511             $v = version_parse('1-0.1'); # $v = '(1,(O,1))'
512              
513             =head3 listitem and nullitem comparison
514              
515             It is not very clear in the official Maven doc.
516              
517             Comparing C with C will just compare first C of the C with C.
518              
519             =head1 MAVEN VERSION COMPATIBILITY
520              
521             This version is fully compatible with the C algo of C embedded with Maven 3.2.2
522              
523             All L tests are also available with Java Junit tests to ensure comparison results are similars.
524              
525             See L if you want to check them.
526              
527             I will do my best to check the Maven compatibility on each Maven new release.
528              
529             =head1 AUTHOR
530              
531             Thomas Cazali, C<< >>
532              
533             =head1 SOURCE
534              
535             The source code repository for C can be found at L
536              
537             =head1 BUGS
538              
539             Please report any bugs or feature requests to L.
540              
541             =head1 SUPPORT
542              
543             You can find documentation for this module with the perldoc command.
544              
545             perldoc Java::Maven::Artifact::Version
546              
547              
548             You can also look for information at:
549              
550             L
551              
552             =over 4
553              
554             =item * github repository issues tracker (report bugs here)
555              
556             L
557              
558             =item * AnnoCPAN: Annotated CPAN documentation
559              
560             L
561              
562             =item * CPAN Ratings
563              
564             L
565              
566             =item * Search CPAN
567              
568             L
569              
570             =back
571              
572             =head1 LICENSE AND COPYRIGHT
573              
574             Copyright 2014 Thomas Cazali.
575              
576             This program is free software; you can redistribute it and/or modify it
577             under the terms of the the Artistic License (2.0). You may obtain a
578             copy of the full license at:
579              
580             L
581              
582             Any use, modification, and distribution of the Standard or Modified
583             Versions is governed by this Artistic License. By using, modifying or
584             distributing the Package, you accept this license. Do not use, modify,
585             or distribute the Package, if you do not accept this license.
586              
587             If your Modified Version has been derived from a Modified Version made
588             by someone other than you, you are nevertheless required to ensure that
589             your Modified Version complies with the requirements of this license.
590              
591             This license does not grant you the right to use any trademark, service
592             mark, tradename, or logo of the Copyright Holder.
593              
594             This license includes the non-exclusive, worldwide, free-of-charge
595             patent license to make, have made, use, offer to sell, sell, import and
596             otherwise transfer the Package with respect to any patent claims
597             licensable by the Copyright Holder that are necessarily infringed by the
598             Package. If you institute patent litigation (including a cross-claim or
599             counterclaim) against any party alleging that the Package constitutes
600             direct or contributory patent infringement, then this Artistic License
601             to you shall terminate on the date that such litigation is filed.
602              
603             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
604             AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
605             THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
606             PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
607             YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
608             CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
609             CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
610             EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
611              
612              
613             =cut
614              
615             1; # End of Java::Maven::Artifact::Version