| 1 | // |
|---|
| 2 | // SUStandardVersionComparator.m |
|---|
| 3 | // Sparkle |
|---|
| 4 | // |
|---|
| 5 | // Created by Andy Matuschak on 12/21/07. |
|---|
| 6 | // Copyright 2007 Andy Matuschak. All rights reserved. |
|---|
| 7 | // |
|---|
| 8 | |
|---|
| 9 | #import "Sparkle.h" |
|---|
| 10 | #import "SUStandardVersionComparator.h" |
|---|
| 11 | |
|---|
| 12 | @implementation SUStandardVersionComparator |
|---|
| 13 | |
|---|
| 14 | + (SUStandardVersionComparator *)defaultComparator |
|---|
| 15 | { |
|---|
| 16 | static SUStandardVersionComparator *defaultComparator = nil; |
|---|
| 17 | if (defaultComparator == nil) |
|---|
| 18 | defaultComparator = [[SUStandardVersionComparator alloc] init]; |
|---|
| 19 | return defaultComparator; |
|---|
| 20 | } |
|---|
| 21 | |
|---|
| 22 | typedef enum { |
|---|
| 23 | kNumberType, |
|---|
| 24 | kStringType, |
|---|
| 25 | kPeriodType |
|---|
| 26 | } SUCharacterType; |
|---|
| 27 | |
|---|
| 28 | - (SUCharacterType)typeOfCharacter:(NSString *)character |
|---|
| 29 | { |
|---|
| 30 | if ([character isEqualToString:@"."]) { |
|---|
| 31 | return kPeriodType; |
|---|
| 32 | } else if ([[NSCharacterSet decimalDigitCharacterSet] characterIsMember:[character characterAtIndex:0]]) { |
|---|
| 33 | return kNumberType; |
|---|
| 34 | } else { |
|---|
| 35 | return kStringType; |
|---|
| 36 | } |
|---|
| 37 | } |
|---|
| 38 | |
|---|
| 39 | - (NSArray *)splitVersionString:(NSString *)version |
|---|
| 40 | { |
|---|
| 41 | NSString *character; |
|---|
| 42 | NSMutableString *s; |
|---|
| 43 | NSInteger i, n, oldType, newType; |
|---|
| 44 | NSMutableArray *parts = [NSMutableArray array]; |
|---|
| 45 | if ([version length] == 0) { |
|---|
| 46 | // Nothing to do here |
|---|
| 47 | return parts; |
|---|
| 48 | } |
|---|
| 49 | s = [[[version substringToIndex:1] mutableCopy] autorelease]; |
|---|
| 50 | oldType = [self typeOfCharacter:s]; |
|---|
| 51 | n = [version length] - 1; |
|---|
| 52 | for (i = 1; i <= n; ++i) { |
|---|
| 53 | character = [version substringWithRange:NSMakeRange(i, 1)]; |
|---|
| 54 | newType = [self typeOfCharacter:character]; |
|---|
| 55 | if (oldType != newType || oldType == kPeriodType) { |
|---|
| 56 | // We've reached a new segment |
|---|
| 57 | NSString *aPart = [[NSString alloc] initWithString:s]; |
|---|
| 58 | [parts addObject:aPart]; |
|---|
| 59 | [aPart release]; |
|---|
| 60 | [s setString:character]; |
|---|
| 61 | } else { |
|---|
| 62 | // Add character to string and continue |
|---|
| 63 | [s appendString:character]; |
|---|
| 64 | } |
|---|
| 65 | oldType = newType; |
|---|
| 66 | } |
|---|
| 67 | |
|---|
| 68 | // Add the last part onto the array |
|---|
| 69 | [parts addObject:[NSString stringWithString:s]]; |
|---|
| 70 | return parts; |
|---|
| 71 | } |
|---|
| 72 | |
|---|
| 73 | - (NSComparisonResult)compareVersion:(NSString *)versionA toVersion:(NSString *)versionB; |
|---|
| 74 | { |
|---|
| 75 | NSArray *partsA = [self splitVersionString:versionA]; |
|---|
| 76 | NSArray *partsB = [self splitVersionString:versionB]; |
|---|
| 77 | |
|---|
| 78 | NSString *partA, *partB; |
|---|
| 79 | NSInteger i, n, typeA, typeB, intA, intB; |
|---|
| 80 | |
|---|
| 81 | n = MIN([partsA count], [partsB count]); |
|---|
| 82 | for (i = 0; i < n; ++i) { |
|---|
| 83 | partA = [partsA objectAtIndex:i]; |
|---|
| 84 | partB = [partsB objectAtIndex:i]; |
|---|
| 85 | |
|---|
| 86 | typeA = [self typeOfCharacter:partA]; |
|---|
| 87 | typeB = [self typeOfCharacter:partB]; |
|---|
| 88 | |
|---|
| 89 | // Compare types |
|---|
| 90 | if (typeA == typeB) { |
|---|
| 91 | // Same type; we can compare |
|---|
| 92 | if (typeA == kNumberType) { |
|---|
| 93 | intA = [partA intValue]; |
|---|
| 94 | intB = [partB intValue]; |
|---|
| 95 | if (intA > intB) { |
|---|
| 96 | return NSOrderedDescending; |
|---|
| 97 | } else if (intA < intB) { |
|---|
| 98 | return NSOrderedAscending; |
|---|
| 99 | } |
|---|
| 100 | } else if (typeA == kStringType) { |
|---|
| 101 | NSComparisonResult result = [partA compare:partB]; |
|---|
| 102 | if (result != NSOrderedSame) { |
|---|
| 103 | return result; |
|---|
| 104 | } |
|---|
| 105 | } |
|---|
| 106 | } else { |
|---|
| 107 | // Not the same type? Now we have to do some validity checking |
|---|
| 108 | if (typeA != kStringType && typeB == kStringType) { |
|---|
| 109 | // typeA wins |
|---|
| 110 | return NSOrderedDescending; |
|---|
| 111 | } else if (typeA == kStringType && typeB != kStringType) { |
|---|
| 112 | // typeB wins |
|---|
| 113 | return NSOrderedAscending; |
|---|
| 114 | } else { |
|---|
| 115 | // One is a number and the other is a period. The period is invalid |
|---|
| 116 | if (typeA == kNumberType) { |
|---|
| 117 | return NSOrderedDescending; |
|---|
| 118 | } else { |
|---|
| 119 | return NSOrderedAscending; |
|---|
| 120 | } |
|---|
| 121 | } |
|---|
| 122 | } |
|---|
| 123 | } |
|---|
| 124 | // The versions are equal up to the point where they both still have parts |
|---|
| 125 | // Lets check to see if one is larger than the other |
|---|
| 126 | if ([partsA count] != [partsB count]) { |
|---|
| 127 | // Yep. Lets get the next part of the larger |
|---|
| 128 | // n holds the index of the part we want. |
|---|
| 129 | NSString *missingPart; |
|---|
| 130 | SUCharacterType missingType; |
|---|
| 131 | NSComparisonResult shorterResult, largerResult; |
|---|
| 132 | |
|---|
| 133 | if ([partsA count] > [partsB count]) { |
|---|
| 134 | missingPart = [partsA objectAtIndex:n]; |
|---|
| 135 | shorterResult = NSOrderedAscending; |
|---|
| 136 | largerResult = NSOrderedDescending; |
|---|
| 137 | } else { |
|---|
| 138 | missingPart = [partsB objectAtIndex:n]; |
|---|
| 139 | shorterResult = NSOrderedDescending; |
|---|
| 140 | largerResult = NSOrderedAscending; |
|---|
| 141 | } |
|---|
| 142 | |
|---|
| 143 | missingType = [self typeOfCharacter:missingPart]; |
|---|
| 144 | // Check the type |
|---|
| 145 | if (missingType == kStringType) { |
|---|
| 146 | // It's a string. Shorter version wins |
|---|
| 147 | return shorterResult; |
|---|
| 148 | } else { |
|---|
| 149 | // It's a number/period. Larger version wins |
|---|
| 150 | return largerResult; |
|---|
| 151 | } |
|---|
| 152 | } |
|---|
| 153 | |
|---|
| 154 | // The 2 strings are identical |
|---|
| 155 | return NSOrderedSame; |
|---|
| 156 | } |
|---|
| 157 | |
|---|
| 158 | |
|---|
| 159 | @end |
|---|