1 package org.apache.maven.artifact.versioning;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.math.BigInteger;
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.Iterator;
26 import java.util.List;
27 import java.util.Locale;
28 import java.util.Properties;
29 import java.util.Stack;
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60 public class ComparableVersion
61 implements Comparable<ComparableVersion>
62 {
63 private String value;
64
65 private String canonical;
66
67 private ListItem items;
68
69 private interface Item
70 {
71 int INTEGER_ITEM = 0;
72 int STRING_ITEM = 1;
73 int LIST_ITEM = 2;
74
75 int compareTo( Item item );
76
77 int getType();
78
79 boolean isNull();
80 }
81
82
83
84
85 private static class IntegerItem
86 implements Item
87 {
88 private static final BigInteger BIG_INTEGER_ZERO = new BigInteger( "0" );
89
90 private final BigInteger value;
91
92 public static final IntegerItem ZERO = new IntegerItem();
93
94 private IntegerItem()
95 {
96 this.value = BIG_INTEGER_ZERO;
97 }
98
99 public IntegerItem( String str )
100 {
101 this.value = new BigInteger( str );
102 }
103
104 public int getType()
105 {
106 return INTEGER_ITEM;
107 }
108
109 public boolean isNull()
110 {
111 return BIG_INTEGER_ZERO.equals( value );
112 }
113
114 public int compareTo( Item item )
115 {
116 if ( item == null )
117 {
118 return BIG_INTEGER_ZERO.equals( value ) ? 0 : 1;
119 }
120
121 switch ( item.getType() )
122 {
123 case INTEGER_ITEM:
124 return value.compareTo( ( (IntegerItem) item ).value );
125
126 case STRING_ITEM:
127 return 1;
128
129 case LIST_ITEM:
130 return 1;
131
132 default:
133 throw new RuntimeException( "invalid item: " + item.getClass() );
134 }
135 }
136
137 public String toString()
138 {
139 return value.toString();
140 }
141 }
142
143
144
145
146 private static class StringItem
147 implements Item
148 {
149 private static final String[] QUALIFIERS = { "alpha", "beta", "milestone", "rc", "snapshot", "", "sp" };
150
151 @SuppressWarnings( "checkstyle:constantname" )
152 private static final List<String> _QUALIFIERS = Arrays.asList( QUALIFIERS );
153
154 private static final Properties ALIASES = new Properties();
155 static
156 {
157 ALIASES.put( "ga", "" );
158 ALIASES.put( "final", "" );
159 ALIASES.put( "cr", "rc" );
160 }
161
162
163
164
165
166 private static final String RELEASE_VERSION_INDEX = String.valueOf( _QUALIFIERS.indexOf( "" ) );
167
168 private String value;
169
170 public StringItem( String value, boolean followedByDigit )
171 {
172 if ( followedByDigit && value.length() == 1 )
173 {
174
175 switch ( value.charAt( 0 ) )
176 {
177 case 'a':
178 value = "alpha";
179 break;
180 case 'b':
181 value = "beta";
182 break;
183 case 'm':
184 value = "milestone";
185 break;
186 default:
187 }
188 }
189 this.value = ALIASES.getProperty( value , value );
190 }
191
192 public int getType()
193 {
194 return STRING_ITEM;
195 }
196
197 public boolean isNull()
198 {
199 return ( comparableQualifier( value ).compareTo( RELEASE_VERSION_INDEX ) == 0 );
200 }
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215 public static String comparableQualifier( String qualifier )
216 {
217 int i = _QUALIFIERS.indexOf( qualifier );
218
219 return i == -1 ? ( _QUALIFIERS.size() + "-" + qualifier ) : String.valueOf( i );
220 }
221
222 public int compareTo( Item item )
223 {
224 if ( item == null )
225 {
226
227 return comparableQualifier( value ).compareTo( RELEASE_VERSION_INDEX );
228 }
229 switch ( item.getType() )
230 {
231 case INTEGER_ITEM:
232 return -1;
233
234 case STRING_ITEM:
235 return comparableQualifier( value ).compareTo( comparableQualifier( ( (StringItem) item ).value ) );
236
237 case LIST_ITEM:
238 return -1;
239
240 default:
241 throw new RuntimeException( "invalid item: " + item.getClass() );
242 }
243 }
244
245 public String toString()
246 {
247 return value;
248 }
249 }
250
251
252
253
254
255 private static class ListItem
256 extends ArrayList<Item>
257 implements Item
258 {
259 public int getType()
260 {
261 return LIST_ITEM;
262 }
263
264 public boolean isNull()
265 {
266 return ( size() == 0 );
267 }
268
269 void normalize()
270 {
271 for ( int i = size() - 1; i >= 0; i-- )
272 {
273 Item lastItem = get( i );
274
275 if ( lastItem.isNull() )
276 {
277
278 remove( i );
279 }
280 else if ( !( lastItem instanceof ListItem ) )
281 {
282 break;
283 }
284 }
285 }
286
287 public int compareTo( Item item )
288 {
289 if ( item == null )
290 {
291 if ( size() == 0 )
292 {
293 return 0;
294 }
295 Item first = get( 0 );
296 return first.compareTo( null );
297 }
298 switch ( item.getType() )
299 {
300 case INTEGER_ITEM:
301 return -1;
302
303 case STRING_ITEM:
304 return 1;
305
306 case LIST_ITEM:
307 Iterator<Item> left = iterator();
308 Iterator<Item> right = ( (ListItem) item ).iterator();
309
310 while ( left.hasNext() || right.hasNext() )
311 {
312 Item l = left.hasNext() ? left.next() : null;
313 Item r = right.hasNext() ? right.next() : null;
314
315
316 int result = l == null ? ( r == null ? 0 : -1 * r.compareTo( l ) ) : l.compareTo( r );
317
318 if ( result != 0 )
319 {
320 return result;
321 }
322 }
323
324 return 0;
325
326 default:
327 throw new RuntimeException( "invalid item: " + item.getClass() );
328 }
329 }
330
331 public String toString()
332 {
333 StringBuilder buffer = new StringBuilder();
334 for ( Iterator<Item> iter = iterator(); iter.hasNext(); )
335 {
336 Item item = iter.next();
337 if ( buffer.length() > 0 )
338 {
339 buffer.append( ( item instanceof ListItem ) ? '-' : '.' );
340 }
341 buffer.append( item );
342 }
343 return buffer.toString();
344 }
345 }
346
347 public ComparableVersion( String version )
348 {
349 parseVersion( version );
350 }
351
352 public final void parseVersion( String version )
353 {
354 this.value = version;
355
356 items = new ListItem();
357
358 version = version.toLowerCase( Locale.ENGLISH );
359
360 ListItem list = items;
361
362 Stack<Item> stack = new Stack<Item>();
363 stack.push( list );
364
365 boolean isDigit = false;
366
367 int startIndex = 0;
368
369 for ( int i = 0; i < version.length(); i++ )
370 {
371 char c = version.charAt( i );
372
373 if ( c == '.' )
374 {
375 if ( i == startIndex )
376 {
377 list.add( IntegerItem.ZERO );
378 }
379 else
380 {
381 list.add( parseItem( isDigit, version.substring( startIndex, i ) ) );
382 }
383 startIndex = i + 1;
384 }
385 else if ( c == '-' )
386 {
387 if ( i == startIndex )
388 {
389 list.add( IntegerItem.ZERO );
390 }
391 else
392 {
393 list.add( parseItem( isDigit, version.substring( startIndex, i ) ) );
394 }
395 startIndex = i + 1;
396
397 list.add( list = new ListItem() );
398 stack.push( list );
399 }
400 else if ( Character.isDigit( c ) )
401 {
402 if ( !isDigit && i > startIndex )
403 {
404 list.add( new StringItem( version.substring( startIndex, i ), true ) );
405 startIndex = i;
406
407 list.add( list = new ListItem() );
408 stack.push( list );
409 }
410
411 isDigit = true;
412 }
413 else
414 {
415 if ( isDigit && i > startIndex )
416 {
417 list.add( parseItem( true, version.substring( startIndex, i ) ) );
418 startIndex = i;
419
420 list.add( list = new ListItem() );
421 stack.push( list );
422 }
423
424 isDigit = false;
425 }
426 }
427
428 if ( version.length() > startIndex )
429 {
430 list.add( parseItem( isDigit, version.substring( startIndex ) ) );
431 }
432
433 while ( !stack.isEmpty() )
434 {
435 list = (ListItem) stack.pop();
436 list.normalize();
437 }
438
439 canonical = items.toString();
440 }
441
442 private static Item parseItem( boolean isDigit, String buf )
443 {
444 return isDigit ? new IntegerItem( buf ) : new StringItem( buf, false );
445 }
446
447 public int compareTo( ComparableVersion o )
448 {
449 return items.compareTo( o.items );
450 }
451
452 public String toString()
453 {
454 return value;
455 }
456
457 public String getCanonical()
458 {
459 return canonical;
460 }
461
462 public boolean equals( Object o )
463 {
464 return ( o instanceof ComparableVersion ) && canonical.equals( ( (ComparableVersion) o ).canonical );
465 }
466
467 public int hashCode()
468 {
469 return canonical.hashCode();
470 }
471
472
473
474
475
476
477 public static void main( String... args )
478 {
479 System.out.println( "Display parameters as parsed by Maven (in canonical form) and comparison result:" );
480 if ( args.length == 0 )
481 {
482 return;
483 }
484
485 ComparableVersion prev = null;
486 int i = 1;
487 for ( String version : args )
488 {
489 ComparableVersion c = new ComparableVersion( version );
490
491 if ( prev != null )
492 {
493 int compare = prev.compareTo( c );
494 System.out.println( " " + prev.toString() + ' '
495 + ( ( compare == 0 ) ? "==" : ( ( compare < 0 ) ? "<" : ">" ) ) + ' ' + version );
496 }
497
498 System.out.println( String.valueOf( i++ ) + ". " + version + " == " + c.getCanonical() );
499
500 prev = c;
501 }
502 }
503 }