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.ListIterator;
28 import java.util.Locale;
29 import java.util.Properties;
30 import java.util.Stack;
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
61 public class ComparableVersion
62 implements Comparable<ComparableVersion>
63 {
64 private String value;
65
66 private String canonical;
67
68 private ListItem items;
69
70 private interface Item
71 {
72 int INTEGER_ITEM = 0;
73 int STRING_ITEM = 1;
74 int LIST_ITEM = 2;
75
76 int compareTo( Item item );
77
78 int getType();
79
80 boolean isNull();
81 }
82
83
84
85
86 private static class IntegerItem
87 implements Item
88 {
89 private static final BigInteger BIG_INTEGER_ZERO = new BigInteger( "0" );
90
91 private final BigInteger value;
92
93 public static final IntegerItem ZERO = new IntegerItem();
94
95 private IntegerItem()
96 {
97 this.value = BIG_INTEGER_ZERO;
98 }
99
100 public IntegerItem( String str )
101 {
102 this.value = new BigInteger( str );
103 }
104
105 public int getType()
106 {
107 return INTEGER_ITEM;
108 }
109
110 public boolean isNull()
111 {
112 return BIG_INTEGER_ZERO.equals( value );
113 }
114
115 public int compareTo( Item item )
116 {
117 if ( item == null )
118 {
119 return BIG_INTEGER_ZERO.equals( value ) ? 0 : 1;
120 }
121
122 switch ( item.getType() )
123 {
124 case INTEGER_ITEM:
125 return value.compareTo( ( (IntegerItem) item ).value );
126
127 case STRING_ITEM:
128 return 1;
129
130 case LIST_ITEM:
131 return 1;
132
133 default:
134 throw new RuntimeException( "invalid item: " + item.getClass() );
135 }
136 }
137
138 public String toString()
139 {
140 return value.toString();
141 }
142 }
143
144
145
146
147 private static class StringItem
148 implements Item
149 {
150 private static final String[] QUALIFIERS = { "alpha", "beta", "milestone", "rc", "snapshot", "", "sp" };
151
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 }
187 }
188 this.value = ALIASES.getProperty( value , value );
189 }
190
191 public int getType()
192 {
193 return STRING_ITEM;
194 }
195
196 public boolean isNull()
197 {
198 return ( comparableQualifier( value ).compareTo( RELEASE_VERSION_INDEX ) == 0 );
199 }
200
201
202
203
204
205
206
207
208
209
210
211
212
213 public static String comparableQualifier( String qualifier )
214 {
215 int i = _QUALIFIERS.indexOf( qualifier );
216
217 return i == -1 ? ( _QUALIFIERS.size() + "-" + qualifier ) : String.valueOf( i );
218 }
219
220 public int compareTo( Item item )
221 {
222 if ( item == null )
223 {
224
225 return comparableQualifier( value ).compareTo( RELEASE_VERSION_INDEX );
226 }
227 switch ( item.getType() )
228 {
229 case INTEGER_ITEM:
230 return -1;
231
232 case STRING_ITEM:
233 return comparableQualifier( value ).compareTo( comparableQualifier( ( (StringItem) item ).value ) );
234
235 case LIST_ITEM:
236 return -1;
237
238 default:
239 throw new RuntimeException( "invalid item: " + item.getClass() );
240 }
241 }
242
243 public String toString()
244 {
245 return value;
246 }
247 }
248
249
250
251
252
253 private static class ListItem
254 extends ArrayList<Item>
255 implements Item
256 {
257 public int getType()
258 {
259 return LIST_ITEM;
260 }
261
262 public boolean isNull()
263 {
264 return ( size() == 0 );
265 }
266
267 void normalize()
268 {
269 for ( ListIterator<Item> iterator = listIterator( size() ); iterator.hasPrevious(); )
270 {
271 Item item = iterator.previous();
272 if ( item.isNull() )
273 {
274 iterator.remove();
275 }
276 else
277 {
278 break;
279 }
280 }
281 }
282
283 public int compareTo( Item item )
284 {
285 if ( item == null )
286 {
287 if ( size() == 0 )
288 {
289 return 0;
290 }
291 Item first = get( 0 );
292 return first.compareTo( null );
293 }
294 switch ( item.getType() )
295 {
296 case INTEGER_ITEM:
297 return -1;
298
299 case STRING_ITEM:
300 return 1;
301
302 case LIST_ITEM:
303 Iterator<Item> left = iterator();
304 Iterator<Item> right = ( (ListItem) item ).iterator();
305
306 while ( left.hasNext() || right.hasNext() )
307 {
308 Item l = left.hasNext() ? left.next() : null;
309 Item r = right.hasNext() ? right.next() : null;
310
311
312 int result = l == null ? ( r == null ? 0 : -1 * r.compareTo( l ) ) : l.compareTo( r );
313
314 if ( result != 0 )
315 {
316 return result;
317 }
318 }
319
320 return 0;
321
322 default:
323 throw new RuntimeException( "invalid item: " + item.getClass() );
324 }
325 }
326
327 public String toString()
328 {
329 StringBuilder buffer = new StringBuilder( "(" );
330 for ( Iterator<Item> iter = iterator(); iter.hasNext(); )
331 {
332 buffer.append( iter.next() );
333 if ( iter.hasNext() )
334 {
335 buffer.append( ',' );
336 }
337 }
338 buffer.append( ')' );
339 return buffer.toString();
340 }
341 }
342
343 public ComparableVersion( String version )
344 {
345 parseVersion( version );
346 }
347
348 public final void parseVersion( String version )
349 {
350 this.value = version;
351
352 items = new ListItem();
353
354 version = version.toLowerCase( Locale.ENGLISH );
355
356 ListItem list = items;
357
358 Stack<Item> stack = new Stack<Item>();
359 stack.push( list );
360
361 boolean isDigit = false;
362
363 int startIndex = 0;
364
365 for ( int i = 0; i < version.length(); i++ )
366 {
367 char c = version.charAt( i );
368
369 if ( c == '.' )
370 {
371 if ( i == startIndex )
372 {
373 list.add( IntegerItem.ZERO );
374 }
375 else
376 {
377 list.add( parseItem( isDigit, version.substring( startIndex, i ) ) );
378 }
379 startIndex = i + 1;
380 }
381 else if ( c == '-' )
382 {
383 if ( i == startIndex )
384 {
385 list.add( IntegerItem.ZERO );
386 }
387 else
388 {
389 list.add( parseItem( isDigit, version.substring( startIndex, i ) ) );
390 }
391 startIndex = i + 1;
392
393 if ( isDigit )
394 {
395 list.normalize();
396
397 if ( ( i + 1 < version.length() ) && Character.isDigit( version.charAt( i + 1 ) ) )
398 {
399
400
401 list.add( list = new ListItem() );
402
403 stack.push( list );
404 }
405 }
406 }
407 else if ( Character.isDigit( c ) )
408 {
409 if ( !isDigit && i > startIndex )
410 {
411 list.add( new StringItem( version.substring( startIndex, i ), true ) );
412 startIndex = i;
413 }
414
415 isDigit = true;
416 }
417 else
418 {
419 if ( isDigit && i > startIndex )
420 {
421 list.add( parseItem( true, version.substring( startIndex, i ) ) );
422 startIndex = i;
423 }
424
425 isDigit = false;
426 }
427 }
428
429 if ( version.length() > startIndex )
430 {
431 list.add( parseItem( isDigit, version.substring( startIndex ) ) );
432 }
433
434 while ( !stack.isEmpty() )
435 {
436 list = (ListItem) stack.pop();
437 list.normalize();
438 }
439
440 canonical = items.toString();
441 }
442
443 private static Item parseItem( boolean isDigit, String buf )
444 {
445 return isDigit ? new IntegerItem( buf ) : new StringItem( buf, false );
446 }
447
448 public int compareTo( ComparableVersion o )
449 {
450 return items.compareTo( o.items );
451 }
452
453 public String toString()
454 {
455 return value;
456 }
457
458 public boolean equals( Object o )
459 {
460 return ( o instanceof ComparableVersion ) && canonical.equals( ( (ComparableVersion) o ).canonical );
461 }
462
463 public int hashCode()
464 {
465 return canonical.hashCode();
466 }
467 }