Spotlight uses NSScanner to parse search strings, eliminating most crashes resulting from unusual search strings.

CQTexperiment
matthewleon 2008-02-13 18:47:24 +00:00
parent f64ebf597f
commit 62c446160c
3 changed files with 56 additions and 39 deletions

View File

@ -15,7 +15,7 @@
NSNumber *length; NSNumber *length;
} }
+(SpotlightPlaylistEntry *)playlistEntryWithMetadataItem:(NSMetadataItem *)metadataItem; + (SpotlightPlaylistEntry *)playlistEntryWithMetadataItem:(NSMetadataItem *)metadataItem;
+ (NSArray *)allmdKeys; + (NSArray *)allmdKeys;
@property(copy) NSNumber *length; @property(copy) NSNumber *length;

View File

@ -87,5 +87,11 @@ static NSDictionary * tags;
return self; return self;
} }
- (void)dealloc
{
[length release];
[super dealloc];
}
@synthesize length; @synthesize length;
@end @end

View File

@ -11,6 +11,9 @@
#import "PlaylistLoader.h" #import "PlaylistLoader.h"
#import "SpotlightPlaylistEntry.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 // Store a class predicate for searching for music
static NSPredicate * musicOnlyPredicate = nil; static NSPredicate * musicOnlyPredicate = nil;
@ -45,46 +48,51 @@ static NSPredicate * musicOnlyPredicate = nil;
- (void)performSearch - (void)performSearch
{ {
// Process the search string into a compound predicate NSPredicate *searchPredicate;
NSPredicate *searchPredicate = [self processSearchString]; // 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 NSPredicate *spotlightPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:
[self.query setSearchScopes:[NSArray arrayWithObjects:pathControl.URL, nil]]; [NSArray arrayWithObjects: musicOnlyPredicate,
searchPredicate,
// spotlightPredicate, which is what will finally be used for the spotlight search nil]];
// is the union of the (potentially) compound searchPredicate and the static // Only preform a new search if the predicate has changed
// musicOnlyPredicate if(![self.query.predicate isEqual:spotlightPredicate])
{
NSPredicate *spotlightPredicate = [NSCompoundPredicate andPredicateWithSubpredicates: if([self.query isStarted])
[NSArray arrayWithObjects: musicOnlyPredicate, [self.query stopQuery];
searchPredicate, self.query.predicate = spotlightPredicate;
nil]]; // Set scope to contents of pathControl
if([self.query isStarted]) self.query.searchScopes = [NSArray arrayWithObjects:pathControl.URL, nil];
[self.query stopQuery]; [self.query startQuery];
self.query.predicate = spotlightPredicate; NSLog(@"Started query: %@", [self.query.predicate description]);
[self.query startQuery]; }
NSLog(@"Started query: %@", [self.query.predicate description], [[self.query class]description]); }
} }
- (NSPredicate *)processSearchString - (NSPredicate *)processSearchString
{ {
// a somewhat dumb way to collapse the whitespace in searchString NSMutableArray *searchComponents = [NSMutableArray arrayWithCapacity:10];
// 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:@" "];
}
// break the string up into an array of each word NSScanner *scanner = [NSScanner scannerWithString:self.searchString];
NSArray * searchComponents = [collapsedString
componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; 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 // create an array of all the predicates to join together
NSMutableArray * subpredicates = [NSMutableArray NSMutableArray * subpredicates = [NSMutableArray
@ -105,13 +113,14 @@ static NSPredicate * musicOnlyPredicate = nil;
rightExpression:[NSExpression expressionForConstantValue:processedKey] rightExpression:[NSExpression expressionForConstantValue:processedKey]
modifier:NSDirectPredicateModifier modifier:NSDirectPredicateModifier
type:NSLikePredicateOperatorType type:NSLikePredicateOperatorType
options:options]; options:options];
//TODO: Ability to search only artist, albums, etc. //TODO: Ability to search only artist, albums, etc.
[subpredicates addObject: predicate]; [subpredicates addObject: predicate];
} }
if ([subpredicates count] == 0)
if ([subpredicates count] == 1) return Nil;
else if ([subpredicates count] == 1)
return [subpredicates objectAtIndex: 0]; return [subpredicates objectAtIndex: 0];
// Create a compound predicate from subPredicates // Create a compound predicate from subPredicates
@ -161,7 +170,9 @@ replacementObjectForResultObject:(NSMetadataItem*)result
@synthesize searchString; @synthesize searchString;
- (void)setSearchString:(NSString *)aString - (void)setSearchString:(NSString *)aString
{ {
if (searchString != aString) { // Make sure the string is changed
if (searchString != aString)
{
searchString = [aString copy]; searchString = [aString copy];
[self performSearch]; [self performSearch];
} }