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.util.ArrayList;
23 import java.util.Collections;
24 import java.util.Iterator;
25 import java.util.List;
26
27 import org.apache.maven.artifact.Artifact;
28
29
30
31
32
33
34
35 public class VersionRange
36 {
37 private final ArtifactVersion recommendedVersion;
38
39 private final List restrictions;
40
41 private VersionRange( ArtifactVersion recommendedVersion, List restrictions )
42 {
43 this.recommendedVersion = recommendedVersion;
44 this.restrictions = restrictions;
45 }
46
47 public ArtifactVersion getRecommendedVersion()
48 {
49 return recommendedVersion;
50 }
51
52 public List getRestrictions()
53 {
54 return restrictions;
55 }
56
57 public VersionRange cloneOf()
58 {
59 List copiedRestrictions = null;
60
61 if ( restrictions != null )
62 {
63 copiedRestrictions = new ArrayList();
64
65 if ( !restrictions.isEmpty() )
66 {
67 copiedRestrictions.addAll( restrictions );
68 }
69 }
70
71 return new VersionRange( recommendedVersion, copiedRestrictions );
72 }
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90 public static VersionRange createFromVersionSpec( String spec )
91 throws InvalidVersionSpecificationException
92 {
93 if ( spec == null )
94 {
95 return null;
96 }
97
98 List restrictions = new ArrayList();
99 String process = spec;
100 ArtifactVersion version = null;
101 ArtifactVersion upperBound = null;
102 ArtifactVersion lowerBound = null;
103
104 while ( process.startsWith( "[" ) || process.startsWith( "(" ) )
105 {
106 int index1 = process.indexOf( ")" );
107 int index2 = process.indexOf( "]" );
108
109 int index = index2;
110 if ( index2 < 0 || index1 < index2 )
111 {
112 if ( index1 >= 0 )
113 {
114 index = index1;
115 }
116 }
117
118 if ( index < 0 )
119 {
120 throw new InvalidVersionSpecificationException( "Unbounded range: " + spec );
121 }
122
123 Restriction restriction = parseRestriction( process.substring( 0, index + 1 ) );
124 if ( lowerBound == null )
125 {
126 lowerBound = restriction.getLowerBound();
127 }
128 if ( upperBound != null )
129 {
130 if ( restriction.getLowerBound() == null || restriction.getLowerBound().compareTo( upperBound ) < 0 )
131 {
132 throw new InvalidVersionSpecificationException( "Ranges overlap: " + spec );
133 }
134 }
135 restrictions.add( restriction );
136 upperBound = restriction.getUpperBound();
137
138 process = process.substring( index + 1 ).trim();
139
140 if ( process.length() > 0 && process.startsWith( "," ) )
141 {
142 process = process.substring( 1 ).trim();
143 }
144 }
145
146 if ( process.length() > 0 )
147 {
148 if ( restrictions.size() > 0 )
149 {
150 throw new InvalidVersionSpecificationException(
151 "Only fully-qualified sets allowed in multiple set scenario: " + spec );
152 }
153 else
154 {
155 version = new DefaultArtifactVersion( process );
156 restrictions.add( Restriction.EVERYTHING );
157 }
158 }
159
160 return new VersionRange( version, restrictions );
161 }
162
163 private static Restriction parseRestriction( String spec )
164 throws InvalidVersionSpecificationException
165 {
166 boolean lowerBoundInclusive = spec.startsWith( "[" );
167 boolean upperBoundInclusive = spec.endsWith( "]" );
168
169 String process = spec.substring( 1, spec.length() - 1 ).trim();
170
171 Restriction restriction;
172
173 int index = process.indexOf( "," );
174
175 if ( index < 0 )
176 {
177 if ( !lowerBoundInclusive || !upperBoundInclusive )
178 {
179 throw new InvalidVersionSpecificationException( "Single version must be surrounded by []: " + spec );
180 }
181
182 ArtifactVersion version = new DefaultArtifactVersion( process );
183
184 restriction = new Restriction( version, lowerBoundInclusive, version, upperBoundInclusive );
185 }
186 else
187 {
188 String lowerBound = process.substring( 0, index ).trim();
189 String upperBound = process.substring( index + 1 ).trim();
190 if ( lowerBound.equals( upperBound ) )
191 {
192 throw new InvalidVersionSpecificationException( "Range cannot have identical boundaries: " + spec );
193 }
194
195 ArtifactVersion lowerVersion = null;
196 if ( lowerBound.length() > 0 )
197 {
198 lowerVersion = new DefaultArtifactVersion( lowerBound );
199 }
200 ArtifactVersion upperVersion = null;
201 if ( upperBound.length() > 0 )
202 {
203 upperVersion = new DefaultArtifactVersion( upperBound );
204 }
205
206 if ( upperVersion != null && lowerVersion != null && upperVersion.compareTo( lowerVersion ) < 0 )
207 {
208 throw new InvalidVersionSpecificationException( "Range defies version ordering: " + spec );
209 }
210
211 restriction = new Restriction( lowerVersion, lowerBoundInclusive, upperVersion, upperBoundInclusive );
212 }
213
214 return restriction;
215 }
216
217 public static VersionRange createFromVersion( String version )
218 {
219 return new VersionRange( new DefaultArtifactVersion( version ), Collections.EMPTY_LIST );
220 }
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249 public VersionRange restrict( VersionRange restriction )
250 {
251 List r1 = this.restrictions;
252 List r2 = restriction.restrictions;
253 List restrictions;
254 if ( r1.isEmpty() || r2.isEmpty() )
255 {
256 restrictions = Collections.EMPTY_LIST;
257 }
258 else
259 {
260 restrictions = intersection( r1, r2 );
261 }
262
263 ArtifactVersion version = null;
264 if ( restrictions.size() > 0 )
265 {
266 boolean found = false;
267 for ( Iterator i = restrictions.iterator(); i.hasNext() && !found; )
268 {
269 Restriction r = (Restriction) i.next();
270
271 if ( recommendedVersion != null && r.containsVersion( recommendedVersion ) )
272 {
273
274 version = recommendedVersion;
275 found = true;
276 }
277 else if ( version == null && restriction.getRecommendedVersion() != null &&
278 r.containsVersion( restriction.getRecommendedVersion() ) )
279 {
280
281 version = restriction.getRecommendedVersion();
282 }
283 }
284 }
285
286 else if ( recommendedVersion != null )
287 {
288
289 version = recommendedVersion;
290 }
291 else if (restriction.recommendedVersion != null)
292 {
293
294
295 version = restriction.recommendedVersion;
296 }
297
298
299
300
301
302
303
304 return new VersionRange( version, restrictions );
305 }
306
307 private List intersection( List r1, List r2 )
308 {
309 List restrictions = new ArrayList( r1.size() + r2.size() );
310 Iterator i1 = r1.iterator();
311 Iterator i2 = r2.iterator();
312 Restriction res1 = (Restriction) i1.next();
313 Restriction res2 = (Restriction) i2.next();
314
315 boolean done = false;
316 while ( !done )
317 {
318 if ( res1.getLowerBound() == null || res2.getUpperBound() == null ||
319 res1.getLowerBound().compareTo( res2.getUpperBound() ) <= 0 )
320 {
321 if ( res1.getUpperBound() == null || res2.getLowerBound() == null ||
322 res1.getUpperBound().compareTo( res2.getLowerBound() ) >= 0 )
323 {
324 ArtifactVersion lower;
325 ArtifactVersion upper;
326 boolean lowerInclusive;
327 boolean upperInclusive;
328
329
330 if ( res1.getLowerBound() == null )
331 {
332 lower = res2.getLowerBound();
333 lowerInclusive = res2.isLowerBoundInclusive();
334 }
335 else if ( res2.getLowerBound() == null )
336 {
337 lower = res1.getLowerBound();
338 lowerInclusive = res1.isLowerBoundInclusive();
339 }
340 else
341 {
342 int comparison = res1.getLowerBound().compareTo( res2.getLowerBound() );
343 if ( comparison < 0 )
344 {
345 lower = res2.getLowerBound();
346 lowerInclusive = res2.isLowerBoundInclusive();
347 }
348 else if ( comparison == 0 )
349 {
350 lower = res1.getLowerBound();
351 lowerInclusive = res1.isLowerBoundInclusive() && res2.isLowerBoundInclusive();
352 }
353 else
354 {
355 lower = res1.getLowerBound();
356 lowerInclusive = res1.isLowerBoundInclusive();
357 }
358 }
359
360 if ( res1.getUpperBound() == null )
361 {
362 upper = res2.getUpperBound();
363 upperInclusive = res2.isUpperBoundInclusive();
364 }
365 else if ( res2.getUpperBound() == null )
366 {
367 upper = res1.getUpperBound();
368 upperInclusive = res1.isUpperBoundInclusive();
369 }
370 else
371 {
372 int comparison = res1.getUpperBound().compareTo( res2.getUpperBound() );
373 if ( comparison < 0 )
374 {
375 upper = res1.getUpperBound();
376 upperInclusive = res1.isUpperBoundInclusive();
377 }
378 else if ( comparison == 0 )
379 {
380 upper = res1.getUpperBound();
381 upperInclusive = res1.isUpperBoundInclusive() && res2.isUpperBoundInclusive();
382 }
383 else
384 {
385 upper = res2.getUpperBound();
386 upperInclusive = res2.isUpperBoundInclusive();
387 }
388 }
389
390
391 if ( lower == null || upper == null || lower.compareTo( upper ) != 0 )
392 {
393 restrictions.add( new Restriction( lower, lowerInclusive, upper, upperInclusive ) );
394 }
395 else if ( lowerInclusive && upperInclusive )
396 {
397 restrictions.add( new Restriction( lower, lowerInclusive, upper, upperInclusive ) );
398 }
399
400
401 if ( upper == res2.getUpperBound() )
402 {
403
404 if ( i2.hasNext() )
405 {
406 res2 = (Restriction) i2.next();
407 }
408 else
409 {
410 done = true;
411 }
412 }
413 else
414 {
415
416 if ( i1.hasNext() )
417 {
418 res1 = (Restriction) i1.next();
419 }
420 else
421 {
422 done = true;
423 }
424 }
425 }
426 else
427 {
428
429 if ( i1.hasNext() )
430 {
431 res1 = (Restriction) i1.next();
432 }
433 else
434 {
435 done = true;
436 }
437 }
438 }
439 else
440 {
441
442 if ( i2.hasNext() )
443 {
444 res2 = (Restriction) i2.next();
445 }
446 else
447 {
448 done = true;
449 }
450 }
451 }
452
453 return restrictions;
454 }
455
456 public ArtifactVersion getSelectedVersion( Artifact artifact )
457 throws OverConstrainedVersionException
458 {
459 ArtifactVersion version;
460 if ( recommendedVersion != null )
461 {
462 version = recommendedVersion;
463 }
464 else
465 {
466 if ( restrictions.size() == 0 )
467 {
468 throw new OverConstrainedVersionException( "The artifact has no valid ranges", artifact );
469 }
470
471 version = null;
472 }
473 return version;
474 }
475
476 public boolean isSelectedVersionKnown( Artifact artifact )
477 throws OverConstrainedVersionException
478 {
479 boolean value = false;
480 if ( recommendedVersion != null )
481 {
482 value = true;
483 }
484 else
485 {
486 if ( restrictions.size() == 0 )
487 {
488 throw new OverConstrainedVersionException( "The artifact has no valid ranges", artifact );
489 }
490 }
491 return value;
492 }
493
494 public String toString()
495 {
496 if ( recommendedVersion != null )
497 {
498 return recommendedVersion.toString();
499 }
500 else
501 {
502 StringBuffer buf = new StringBuffer();
503 for ( Iterator i = restrictions.iterator(); i.hasNext(); )
504 {
505 Restriction r = (Restriction) i.next();
506
507 buf.append( r.toString() );
508
509 if ( i.hasNext() )
510 {
511 buf.append( "," );
512 }
513 }
514 return buf.toString();
515 }
516 }
517
518 public ArtifactVersion matchVersion( List versions )
519 {
520
521
522 ArtifactVersion matched = null;
523 for ( Iterator i = versions.iterator(); i.hasNext(); )
524 {
525 ArtifactVersion version = (ArtifactVersion) i.next();
526 if ( containsVersion( version ) )
527 {
528
529 if ( matched == null || version.compareTo( matched ) > 0 )
530 {
531 matched = version;
532 }
533 }
534 }
535 return matched;
536 }
537
538 public boolean containsVersion( ArtifactVersion version )
539 {
540 for ( Iterator i = restrictions.iterator(); i.hasNext(); )
541 {
542 Restriction restriction = (Restriction) i.next();
543 if ( restriction.containsVersion( version ) )
544 {
545 return true;
546 }
547 }
548 return false;
549 }
550
551 public boolean hasRestrictions()
552 {
553 return !restrictions.isEmpty() && recommendedVersion == null;
554 }
555
556 public boolean equals( Object obj )
557 {
558 if (this == obj){
559 return true;
560 }
561 if (!(obj instanceof VersionRange ))
562 {
563 return false;
564 }
565 VersionRange other = (VersionRange) obj;
566
567 boolean equals =
568 recommendedVersion == other.recommendedVersion ||
569 ( ( recommendedVersion != null ) && recommendedVersion.equals( other.recommendedVersion ) );
570 equals &=
571 restrictions == other.restrictions ||
572 ( ( restrictions != null ) && restrictions.equals( other.restrictions ) );
573 return equals;
574 }
575
576 public int hashCode()
577 {
578 int hash = 7;
579 hash = 31 * hash + ( recommendedVersion == null ? 0 : recommendedVersion.hashCode() );
580 hash = 31 * hash + ( restrictions == null ? 0 : restrictions.hashCode() );
581 return hash;
582 }
583 }