| | 1 | | // Ignore Spelling: Alphanum |
| | 2 | |
|
| | 3 | | using System.Collections; |
| | 4 | | using System.Text; |
| | 5 | |
|
| | 6 | | namespace Configuration.Helper; |
| | 7 | |
|
| | 8 | | /// <summary>Exposes a method that compares two objects.</summary> |
| | 9 | | public class AlphanumComparator : IComparer |
| | 10 | | { |
| | 11 | | #region Private Enumeration and Methods |
| | 12 | |
|
| | 13 | | private enum ChunkType { Alphanumeric, Numeric }; |
| | 14 | |
|
| | 15 | | private const int clt = -1; |
| | 16 | | private const int ceq = 0; |
| | 17 | | private const int cgt = 1; |
| | 18 | |
|
| | 19 | | private static bool InChunk( char ch, char otherCh ) |
| 254 | 20 | | { |
| 254 | 21 | | ChunkType type = ChunkType.Alphanumeric; |
| | 22 | |
|
| 266 | 23 | | if( char.IsDigit( otherCh ) ) { type = ChunkType.Numeric; } |
| | 24 | |
|
| 254 | 25 | | return ( type != ChunkType.Alphanumeric || !char.IsDigit( ch ) ) |
| 254 | 26 | | && ( type != ChunkType.Numeric || char.IsDigit( ch ) ); |
| 254 | 27 | | } |
| | 28 | |
|
| | 29 | | private static StringBuilder GetStringBuilder( ref string str, ref int marker ) |
| 66 | 30 | | { |
| 66 | 31 | | char thisCh = str[marker]; |
| 66 | 32 | | StringBuilder rtn = new(); |
| | 33 | |
|
| 383 | 34 | | while( ( marker < str.Length ) && ( rtn.Length == 0 || InChunk( thisCh, rtn[0] ) ) ) |
| 317 | 35 | | { |
| 317 | 36 | | rtn.Append( thisCh ); |
| 317 | 37 | | marker++; |
| | 38 | |
|
| 1079 | 39 | | if( marker < str.Length ) { thisCh = str[marker]; } |
| 317 | 40 | | } |
| | 41 | |
|
| 66 | 42 | | return rtn; |
| 66 | 43 | | } |
| | 44 | |
|
| | 45 | | private static bool IsNumeric( char x, char y ) |
| 33 | 46 | | { |
| 33 | 47 | | return char.IsDigit( x ) && char.IsDigit( y ); |
| 33 | 48 | | } |
| | 49 | |
|
| | 50 | | private static int SortNumeric( string x, string y ) |
| 3 | 51 | | { |
| 3 | 52 | | int xNumber = Convert.ToInt32( x ); |
| 3 | 53 | | int yNumber = Convert.ToInt32( y ); |
| | 54 | |
|
| 5 | 55 | | if( xNumber < yNumber ) { return clt; } |
| 4 | 56 | | else if( xNumber > yNumber ) { return cgt; } |
| 1 | 57 | | return ceq; |
| 3 | 58 | | } |
| | 59 | |
|
| | 60 | | private static bool MoreToCheck( int xPos, string s1, int yPos, string s2 ) |
| 37 | 61 | | { |
| 37 | 62 | | return ( xPos < s1.Length ) || ( yPos < s2.Length ); |
| 37 | 63 | | } |
| | 64 | |
|
| | 65 | | #endregion |
| | 66 | |
|
| | 67 | | #region IComparer Implementation |
| | 68 | |
|
| | 69 | | /// <summary> |
| | 70 | | /// Compares two objects and returns a value indicating whether one is less than, |
| | 71 | | /// equal to, or greater than the other. |
| | 72 | | /// </summary> |
| | 73 | | /// <param name="x">The first object to compare.</param> |
| | 74 | | /// <param name="y">The second object to compare.</param> |
| | 75 | | /// <returns>A signed integer that indicates the relative values of x and y: |
| | 76 | | /// - If less than 0, x is less than y. |
| | 77 | | /// - If 0, x equals y. |
| | 78 | | /// - If greater than 0, x is greater than y. |
| | 79 | | /// </returns> |
| | 80 | | public int Compare( object? x, object? y ) |
| 39 | 81 | | { |
| 45 | 82 | | if( x is not string s1 || y is not string s2 ) { return ceq; } |
| | 83 | |
|
| 36 | 84 | | int xPosition = 0; |
| 36 | 85 | | int yPosition = 0; |
| | 86 | |
|
| 37 | 87 | | while( MoreToCheck( xPosition, s1, yPosition, s2 ) ) |
| 33 | 88 | | { |
| 33 | 89 | | if( xPosition >= s1.Length ) { return clt; } |
| 33 | 90 | | if( yPosition >= s2.Length ) { return cgt; } |
| | 91 | |
|
| 33 | 92 | | StringBuilder xChunk = GetStringBuilder( ref s1, ref xPosition ); |
| 33 | 93 | | StringBuilder yChunk = GetStringBuilder( ref s2, ref yPosition ); |
| | 94 | |
|
| 33 | 95 | | string xStr = xChunk.ToString(); |
| 33 | 96 | | string yStr = yChunk.ToString(); |
| | 97 | |
|
| | 98 | | // If both chunks contain numeric characters, sort them numerically |
| 33 | 99 | | int result = IsNumeric( xChunk[0], yChunk[0] ) |
| 33 | 100 | | ? SortNumeric( xStr, yStr ) |
| 33 | 101 | | : string.Compare( xStr, yStr, StringComparison.Ordinal ); |
| | 102 | |
|
| 97 | 103 | | if( result != ceq ) { return result; } |
| 1 | 104 | | } |
| | 105 | |
|
| 4 | 106 | | return ceq; |
| 39 | 107 | | } |
| | 108 | |
|
| | 109 | | #endregion |
| | 110 | | } |