File Coverage

blib/lib/Java/Maven/Artifact/Version.pm
Criterion Covered Total %
statement 172 182 94.5
branch 62 72 86.1
condition 15 18 83.3
subroutine 51 54 94.4
pod 2 2 100.0
total 302 328 92.0


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