22
22
*/
23
23
using System ;
24
24
using System . Reflection ;
25
+ using System . Text ;
25
26
using System . Collections . Generic ;
26
27
using Org . IdentityConnectors . Common ;
27
28
using Org . IdentityConnectors . Common . Security ;
@@ -379,4 +380,338 @@ public static Version GetFrameworkVersion()
379
380
return Assembly . GetExecutingAssembly ( ) . GetName ( ) . Version ;
380
381
}
381
382
}
383
+
384
+ /// <summary>
385
+ /// A version range is an interval describing a set of <seealso cref="Version versions"/>.
386
+ /// <p/>
387
+ /// A range has a left (lower) endpoint and a right (upper) endpoint. Each
388
+ /// endpoint can be open (excluded from the set) or closed (included in the set).
389
+ ///
390
+ /// <p>
391
+ /// {@code VersionRange} objects are immutable.
392
+ ///
393
+ /// @author Laszlo Hordos
394
+ /// @Immutable
395
+ /// </summary>
396
+ public class VersionRange
397
+ {
398
+
399
+ /// <summary>
400
+ /// The left endpoint is open and is excluded from the range.
401
+ /// <p>
402
+ /// The value of {@code LEFT_OPEN} is {@code '('}.
403
+ /// </summary>
404
+ public const char LEFT_OPEN = '(' ;
405
+ /// <summary>
406
+ /// The left endpoint is closed and is included in the range.
407
+ /// <p>
408
+ /// The value of {@code LEFT_CLOSED} is {@code '['}.
409
+ /// </summary>
410
+ public const char LEFT_CLOSED = '[' ;
411
+ /// <summary>
412
+ /// The right endpoint is open and is excluded from the range.
413
+ /// <p>
414
+ /// The value of {@code RIGHT_OPEN} is {@code ')'}.
415
+ /// </summary>
416
+ public const char RIGHT_OPEN = ')' ;
417
+ /// <summary>
418
+ /// The right endpoint is closed and is included in the range.
419
+ /// <p>
420
+ /// The value of {@code RIGHT_CLOSED} is {@code ']'}.
421
+ /// </summary>
422
+ public const char RIGHT_CLOSED = ']' ;
423
+
424
+ private const string ENDPOINT_DELIMITER = "," ;
425
+
426
+ private readonly Version floorVersion ;
427
+ private readonly bool isFloorInclusive ;
428
+ private readonly Version ceilingVersion ;
429
+ private readonly bool isCeilingInclusive ;
430
+ private readonly bool empty ;
431
+
432
+ /// <summary>
433
+ /// Parse version component into a Version.
434
+ /// </summary>
435
+ /// <param name="version">
436
+ /// version component string </param>
437
+ /// <param name="range">
438
+ /// Complete range string for exception message, if any </param>
439
+ /// <returns> Version </returns>
440
+ private static Version parseVersion ( string version , string range )
441
+ {
442
+ try
443
+ {
444
+ return Version . Parse ( version ) ;
445
+ }
446
+ catch ( System . ArgumentException e )
447
+ {
448
+ throw new System . ArgumentException ( "invalid range \" " + range + "\" : " + e . Message , e ) ;
449
+ }
450
+ }
451
+
452
+ /// <summary>
453
+ /// Creates a version range from the specified string.
454
+ ///
455
+ /// <p>
456
+ /// Version range string grammar:
457
+ ///
458
+ /// <pre>
459
+ /// range ::= interval | at least
460
+ /// interval ::= ( '[' | '(' ) left ',' right ( ']' | ')' )
461
+ /// left ::= version
462
+ /// right ::= version
463
+ /// at least ::= version
464
+ /// </pre>
465
+ /// </summary>
466
+ /// <param name="range">
467
+ /// String representation of the version range. The versions in
468
+ /// the range must contain no whitespace. Other whitespace in the
469
+ /// range string is ignored. </param>
470
+ /// <exception cref="IllegalArgumentException">
471
+ /// If {@code range} is improperly formatted. </exception>
472
+ public static VersionRange Parse ( string range )
473
+ {
474
+ Assertions . BlankCheck ( range , "range" ) ;
475
+ int idx = range . IndexOf ( ENDPOINT_DELIMITER ) ;
476
+ // Check if the version is an interval.
477
+ if ( idx > 1 && idx == range . LastIndexOf ( ENDPOINT_DELIMITER ) )
478
+ {
479
+ string vlo = range . Substring ( 0 , idx ) . Trim ( ) ;
480
+ string vhi = range . Substring ( idx + 1 ) . Trim ( ) ;
481
+
482
+ bool isLowInclusive = true ;
483
+ bool isHighInclusive = true ;
484
+ if ( vlo [ 0 ] == LEFT_OPEN )
485
+ {
486
+ isLowInclusive = false ;
487
+ }
488
+ else if ( vlo [ 0 ] != LEFT_CLOSED )
489
+ {
490
+ throw new System . ArgumentException ( "invalid range \" " + range + "\" : invalid format" ) ;
491
+ }
492
+ vlo = vlo . Substring ( 1 ) . Trim ( ) ;
493
+
494
+ if ( vhi [ vhi . Length - 1 ] == RIGHT_OPEN )
495
+ {
496
+ isHighInclusive = false ;
497
+ }
498
+ else if ( vhi [ vhi . Length - 1 ] != RIGHT_CLOSED )
499
+ {
500
+ throw new System . ArgumentException ( "invalid range \" " + range + "\" : invalid format" ) ;
501
+ }
502
+ vhi = vhi . Substring ( 0 , vhi . Length - 1 ) . Trim ( ) ;
503
+
504
+ return new VersionRange ( parseVersion ( vlo , range ) , isLowInclusive , parseVersion ( vhi , range ) , isHighInclusive ) ;
505
+ }
506
+ else if ( idx == - 1 )
507
+ {
508
+ return new VersionRange ( VersionRange . parseVersion ( range . Trim ( ) , range ) , true , null , false ) ;
509
+ }
510
+ else
511
+ {
512
+ throw new System . ArgumentException ( "invalid range \" " + range + "\" : invalid format" ) ;
513
+ }
514
+ }
515
+
516
+ public VersionRange ( Version low , bool isLowInclusive , Version high , bool isHighInclusive )
517
+ {
518
+ Assertions . NullCheck ( low , "floorVersion" ) ;
519
+ floorVersion = low ;
520
+ isFloorInclusive = isLowInclusive ;
521
+ ceilingVersion = high ;
522
+ isCeilingInclusive = isHighInclusive ;
523
+ empty = Empty0 ;
524
+ }
525
+
526
+ public virtual Version Floor
527
+ {
528
+ get
529
+ {
530
+ return floorVersion ;
531
+ }
532
+ }
533
+
534
+ public virtual bool FloorInclusive
535
+ {
536
+ get
537
+ {
538
+ return isFloorInclusive ;
539
+ }
540
+ }
541
+
542
+ public virtual Version Ceiling
543
+ {
544
+ get
545
+ {
546
+ return ceilingVersion ;
547
+ }
548
+ }
549
+
550
+ public virtual bool CeilingInclusive
551
+ {
552
+ get
553
+ {
554
+ return isCeilingInclusive ;
555
+ }
556
+ }
557
+
558
+ public virtual bool IsInRange ( Version version )
559
+ {
560
+ if ( empty )
561
+ {
562
+ return false ;
563
+ }
564
+ if ( floorVersion . CompareTo ( version ) >= ( isFloorInclusive ? 1 : 0 ) )
565
+ {
566
+ return false ;
567
+ }
568
+ if ( ceilingVersion == null )
569
+ {
570
+ return true ;
571
+ }
572
+ return ceilingVersion . CompareTo ( version ) >= ( isCeilingInclusive ? 0 : 1 ) ;
573
+
574
+ }
575
+
576
+ /// <summary>
577
+ /// Returns whether this version range contains only a single version.
578
+ /// </summary>
579
+ /// <returns> {@code true} if this version range contains only a single
580
+ /// version; {@code false} otherwise. </returns>
581
+ public virtual bool Exact
582
+ {
583
+ get
584
+ {
585
+ if ( empty )
586
+ {
587
+ return false ;
588
+ }
589
+ else if ( ceilingVersion == null )
590
+ {
591
+ return true ;
592
+ }
593
+ if ( isFloorInclusive )
594
+ {
595
+ if ( isCeilingInclusive )
596
+ {
597
+ // [f,c]: exact if f == c
598
+ return floorVersion . Equals ( ceilingVersion ) ;
599
+ }
600
+ else
601
+ {
602
+ // [f,c): exact if f++ >= c
603
+ Version adjacent1 = new Version ( floorVersion . Major , floorVersion . Minor , floorVersion . Build , floorVersion . Revision + 1 ) ;
604
+ return adjacent1 . CompareTo ( ceilingVersion ) >= 0 ;
605
+ }
606
+ }
607
+ else
608
+ {
609
+ if ( isCeilingInclusive )
610
+ {
611
+ // (f,c] is equivalent to [f++,c]: exact if f++ == c
612
+ Version adjacent1 = new Version ( floorVersion . Major , floorVersion . Minor , floorVersion . Build , floorVersion . Revision + 1 ) ;
613
+ return adjacent1 . Equals ( ceilingVersion ) ;
614
+ }
615
+ else
616
+ {
617
+ // (f,c) is equivalent to [f++,c): exact if (f++)++ >=c
618
+ Version adjacent2 = new Version ( floorVersion . Major , floorVersion . Minor , floorVersion . Build , floorVersion . Revision + 2 ) ;
619
+ return adjacent2 . CompareTo ( ceilingVersion ) >= 0 ;
620
+ }
621
+ }
622
+ }
623
+ }
624
+
625
+ /// <summary>
626
+ /// Returns whether this version range is empty. A version range is empty if
627
+ /// the set of versions defined by the interval is empty.
628
+ /// </summary>
629
+ /// <returns> {@code true} if this version range is empty; {@code false}
630
+ /// otherwise. </returns>
631
+ public virtual bool Empty
632
+ {
633
+ get
634
+ {
635
+ return empty ;
636
+ }
637
+ }
638
+
639
+ /// <summary>
640
+ /// Internal isEmpty behavior.
641
+ /// </summary>
642
+ /// <returns> {@code true} if this version range is empty; {@code false}
643
+ /// otherwise. </returns>
644
+ private bool Empty0
645
+ {
646
+ get
647
+ {
648
+ if ( ceilingVersion == null ) // infinity
649
+ {
650
+ return false ;
651
+ }
652
+ int comparison = floorVersion . CompareTo ( ceilingVersion ) ;
653
+ if ( comparison == 0 ) // endpoints equal
654
+ {
655
+ return ! isFloorInclusive || ! isCeilingInclusive ;
656
+ }
657
+ return comparison > 0 ; // true if left > right
658
+ }
659
+ }
660
+
661
+ public virtual bool Equals ( object obj )
662
+ {
663
+ if ( obj == null )
664
+ {
665
+ return false ;
666
+ }
667
+ if ( this . GetType ( ) != obj . GetType ( ) )
668
+ {
669
+ return false ;
670
+ }
671
+ VersionRange other = ( VersionRange ) obj ;
672
+ if ( floorVersion != other . floorVersion && ( floorVersion == null || ! floorVersion . Equals ( other . floorVersion ) ) )
673
+ {
674
+ return false ;
675
+ }
676
+ if ( isFloorInclusive != other . isFloorInclusive )
677
+ {
678
+ return false ;
679
+ }
680
+ if ( ceilingVersion != other . ceilingVersion && ( ceilingVersion == null || ! ceilingVersion . Equals ( other . ceilingVersion ) ) )
681
+ {
682
+ return false ;
683
+ }
684
+ if ( isCeilingInclusive != other . isCeilingInclusive )
685
+ {
686
+ return false ;
687
+ }
688
+ return true ;
689
+ }
690
+
691
+ public override int GetHashCode ( )
692
+ {
693
+ int result = floorVersion . GetHashCode ( ) ;
694
+ result = 31 * result + ( isFloorInclusive ? 1 : 0 ) ;
695
+ result = 31 * result + ( ceilingVersion != null ? ceilingVersion . GetHashCode ( ) : 0 ) ;
696
+ result = 31 * result + ( isCeilingInclusive ? 1 : 0 ) ;
697
+ return result ;
698
+ }
699
+
700
+ public virtual string ToString ( )
701
+ {
702
+ if ( ceilingVersion != null )
703
+ {
704
+ StringBuilder sb = new StringBuilder ( ) ;
705
+ sb . Append ( isFloorInclusive ? LEFT_CLOSED : LEFT_OPEN ) ;
706
+ sb . Append ( floorVersion . ToString ( ) ) . Append ( ENDPOINT_DELIMITER ) . Append ( ceilingVersion . ToString ( ) ) ;
707
+ sb . Append ( isCeilingInclusive ? RIGHT_CLOSED : RIGHT_OPEN ) ;
708
+ return sb . ToString ( ) ;
709
+ }
710
+ else
711
+ {
712
+ return floorVersion . ToString ( ) ;
713
+ }
714
+ }
715
+ }
716
+
382
717
}
0 commit comments