LNStandardVersionComparator.m 4.5 KB

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