From 62c446160c11ad7d9defcee3fd6ae76e35bee32e Mon Sep 17 00:00:00 2001 From: matthewleon Date: Wed, 13 Feb 2008 18:47:24 +0000 Subject: [PATCH] Spotlight uses NSScanner to parse search strings, eliminating most crashes resulting from unusual search strings. --- Spotlight/SpotlightPlaylistEntry.h | 2 +- Spotlight/SpotlightPlaylistEntry.m | 6 ++ Spotlight/SpotlightSearchController.m | 87 +++++++++++++++------------ 3 files changed, 56 insertions(+), 39 deletions(-) diff --git a/Spotlight/SpotlightPlaylistEntry.h b/Spotlight/SpotlightPlaylistEntry.h index 345ec2182..a3405be44 100644 --- a/Spotlight/SpotlightPlaylistEntry.h +++ b/Spotlight/SpotlightPlaylistEntry.h @@ -15,7 +15,7 @@ NSNumber *length; } -+(SpotlightPlaylistEntry *)playlistEntryWithMetadataItem:(NSMetadataItem *)metadataItem; ++ (SpotlightPlaylistEntry *)playlistEntryWithMetadataItem:(NSMetadataItem *)metadataItem; + (NSArray *)allmdKeys; @property(copy) NSNumber *length; diff --git a/Spotlight/SpotlightPlaylistEntry.m b/Spotlight/SpotlightPlaylistEntry.m index e021c555a..9fa35eef5 100644 --- a/Spotlight/SpotlightPlaylistEntry.m +++ b/Spotlight/SpotlightPlaylistEntry.m @@ -87,5 +87,11 @@ static NSDictionary * tags; return self; } +- (void)dealloc +{ + [length release]; + [super dealloc]; +} + @synthesize length; @end \ No newline at end of file diff --git a/Spotlight/SpotlightSearchController.m b/Spotlight/SpotlightSearchController.m index 963549c20..99bef9c0f 100644 --- a/Spotlight/SpotlightSearchController.m +++ b/Spotlight/SpotlightSearchController.m @@ -11,6 +11,9 @@ #import "PlaylistLoader.h" #import "SpotlightPlaylistEntry.h" +// Minimum length of a search string (searching for very small strings gets ugly) +#define MINIMUM_SEARCH_STRING_LENGTH 3 + // Store a class predicate for searching for music static NSPredicate * musicOnlyPredicate = nil; @@ -45,46 +48,51 @@ static NSPredicate * musicOnlyPredicate = nil; - (void)performSearch { - // Process the search string into a compound predicate - NSPredicate *searchPredicate = [self processSearchString]; + NSPredicate *searchPredicate; + // Process the search string into a compound predicate. If Nil is returned do nothing + if(searchPredicate = [self processSearchString]) + { + // spotlightPredicate, which is what will finally be used for the spotlight search + // is the union of the (potentially) compound searchPredicate and the static + // musicOnlyPredicate - // Set scope to contents of pathControl - [self.query setSearchScopes:[NSArray arrayWithObjects:pathControl.URL, nil]]; - - // spotlightPredicate, which is what will finally be used for the spotlight search - // is the union of the (potentially) compound searchPredicate and the static - // musicOnlyPredicate - - NSPredicate *spotlightPredicate = [NSCompoundPredicate andPredicateWithSubpredicates: - [NSArray arrayWithObjects: musicOnlyPredicate, - searchPredicate, - nil]]; - if([self.query isStarted]) - [self.query stopQuery]; - self.query.predicate = spotlightPredicate; - [self.query startQuery]; - NSLog(@"Started query: %@", [self.query.predicate description], [[self.query class]description]); + NSPredicate *spotlightPredicate = [NSCompoundPredicate andPredicateWithSubpredicates: + [NSArray arrayWithObjects: musicOnlyPredicate, + searchPredicate, + nil]]; + // Only preform a new search if the predicate has changed + if(![self.query.predicate isEqual:spotlightPredicate]) + { + if([self.query isStarted]) + [self.query stopQuery]; + self.query.predicate = spotlightPredicate; + // Set scope to contents of pathControl + self.query.searchScopes = [NSArray arrayWithObjects:pathControl.URL, nil]; + [self.query startQuery]; + NSLog(@"Started query: %@", [self.query.predicate description]); + } + } } - (NSPredicate *)processSearchString { - // a somewhat dumb way to collapse the whitespace in searchString - // TODO: write a more elegant way of accomplishing this - NSString * compareString = [self.searchString - stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; - - NSString * collapsedString = [compareString stringByReplacingOccurrencesOfString:@" " - withString:@" "]; - while (![collapsedString isEqualToString:compareString]) - { - compareString = [collapsedString copy]; - collapsedString = [compareString stringByReplacingOccurrencesOfString:@" " - withString:@" "]; - } + NSMutableArray *searchComponents = [NSMutableArray arrayWithCapacity:10]; - // break the string up into an array of each word - NSArray * searchComponents = [collapsedString - componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; + NSScanner *scanner = [NSScanner scannerWithString:self.searchString]; + + while (![scanner isAtEnd]) + { + NSString *scannedString; + if ([scanner scanUpToString:@" " intoString:&scannedString]) + { + // don't add tiny strings in... They're make the system go nuts + // TODO: Maybe consider a better solution to the issue? + if ([scannedString length] >= MINIMUM_SEARCH_STRING_LENGTH) + { + [searchComponents addObject:scannedString]; + } + } + } // create an array of all the predicates to join together NSMutableArray * subpredicates = [NSMutableArray @@ -105,13 +113,14 @@ static NSPredicate * musicOnlyPredicate = nil; rightExpression:[NSExpression expressionForConstantValue:processedKey] modifier:NSDirectPredicateModifier type:NSLikePredicateOperatorType - options:options]; + options:options]; //TODO: Ability to search only artist, albums, etc. [subpredicates addObject: predicate]; } - - if ([subpredicates count] == 1) + if ([subpredicates count] == 0) + return Nil; + else if ([subpredicates count] == 1) return [subpredicates objectAtIndex: 0]; // Create a compound predicate from subPredicates @@ -161,7 +170,9 @@ replacementObjectForResultObject:(NSMetadataItem*)result @synthesize searchString; - (void)setSearchString:(NSString *)aString { - if (searchString != aString) { + // Make sure the string is changed + if (searchString != aString) + { searchString = [aString copy]; [self performSearch]; }