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