1951FDG
5/17/2012 - 10:15 PM

Version number comparison with NSScanner (Objective-C + Foundation)

Version number comparison with NSScanner (Objective-C + Foundation)

#import "DTVersionComparison.h"

int compareVersions(NSString *version1, NSString *version2){
	
	if(!version1 || !version2 || ![version1 isKindOfClass:[NSString class]] || ![version2 isKindOfClass:[NSString class]])
		return 0;
	
	NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
		
	NSScanner *firstScanner  = [NSScanner scannerWithString:version1];
	NSScanner *secondScanner= [NSScanner scannerWithString:version2];
	
	unsigned int winner = 0;
	int firstComponent;
	int secondComponent;
	BOOL firstDidScan;
	BOOL secondDidScan;
	NSDictionary *devPhases = [NSDictionary dictionaryWithObjectsAndKeys:
							   [NSNumber numberWithInt:1], @"d",
							   [NSNumber numberWithInt:2], @"a",
							   [NSNumber numberWithInt:3], @"b",
							   [NSNumber numberWithInt:4], @"f", nil];
	
	while (winner == 0) {
		// Scan the next component of each version number
		firstDidScan  = [firstScanner scanInt:&firstComponent];
		secondDidScan = [secondScanner scanInt:&secondComponent];
		
		// Check if either scanner has been exhausted and exit the loop accordingly
		if (!firstDidScan && !secondDidScan){
			break;
		} else if (!firstDidScan && secondDidScan){
			winner = 2;
			break;
		} else if (!secondDidScan && firstDidScan){
			winner = 1;
			break;
		}
		
		// Otherwise, compare what we have just scanned
		if (firstComponent > secondComponent) winner = 1;
		else if (secondComponent > firstComponent) winner = 2;
		
		// If we're going to go around the loop again, try to advance past the next dot
		[firstScanner  scanString:@"." intoString:NULL];
		[secondScanner scanString:@"." intoString:NULL];
	}
	// If we have not found a winner yet, look for dev phase components after the version number proper
	if(winner == 0){
		
		NSCharacterSet *devPhaseCharacterSet = [NSCharacterSet characterSetWithCharactersInString:@"dabf"];
		NSString *firstDevComponent;
		firstDidScan  = [firstScanner scanCharactersFromSet:devPhaseCharacterSet intoString:&firstDevComponent];
		NSString *secondDevComponent;
		secondDidScan = [secondScanner scanCharactersFromSet:devPhaseCharacterSet intoString:&secondDevComponent];
		
		if (firstDidScan && !secondDidScan)
			winner = 2;
		else if (secondDidScan && !firstDidScan)
			winner = 1;
		else if (firstDidScan && secondDidScan){
			int firstDevWeight = [[devPhases objectForKey:[firstDevComponent substringToIndex:1]] intValue];
			int secondDevWeight = [[devPhases objectForKey:[secondDevComponent substringToIndex:1]] intValue];
			if (firstDevWeight > secondDevWeight)
				winner = 1;
			else if (secondDevWeight > firstDevWeight)
				winner = 2;
			else if (firstDevWeight == secondDevWeight){
				firstDidScan = [firstScanner scanInt:&firstComponent];
				secondDidScan = [secondScanner scanInt:&secondComponent];
				if(firstDidScan && !secondDidScan)
					winner = 1;
				else if (secondDidScan && !firstDidScan)
					winner = 2;
				else if (firstDidScan && secondDidScan){
					if (firstComponent > secondComponent)
						winner = 1;
					else if (secondComponent > firstComponent)
						winner = 2;
				}
			}
		}
		
	}
	// If we have not found a winner yet, look for a count of commits since the version (a la `git describe`)
	if(winner == 0){
        int firstCommitCount = 0;
        int secondCommitCount = 0;
        if ((firstDidScan  = [firstScanner scanString:@"-" intoString:NULL])) {
            firstDidScan = [firstScanner scanInt:&firstCommitCount];
        }
        if ((secondDidScan = [secondScanner scanString:@"-" intoString:NULL])) {
            secondDidScan = [secondScanner scanInt:&secondCommitCount];
        }

        if (firstDidScan || secondDidScan) {
            if (firstCommitCount > secondCommitCount) {
                winner = 1;
            } else if (firstCommitCount < secondCommitCount) {
                winner = 2;
            }
        }
    }
	[pool drain];
	
	return winner;
}
#import <Foundation/Foundation.h>

int compareVersions(NSString *version1, NSString *version2);