Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
Sample Applications/CoreRecipesApp/AutomatorAction/Sources/GetRecipesSummaryAction.m
/* |
File: GetRecipesSummaryAction.m |
Abstract: The class to provide the CoreRecipes Automator action. |
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple |
Computer, Inc. ("Apple") in consideration of your agreement to the |
following terms, and your use, installation, modification or |
redistribution of this Apple software constitutes acceptance of these |
terms. If you do not agree with these terms, please do not use, |
install, modify or redistribute this Apple software. |
In consideration of your agreement to abide by the following terms, and |
subject to these terms, Apple grants you a personal, non-exclusive |
license, under Apple's copyrights in this original Apple software (the |
"Apple Software"), to use, reproduce, modify and redistribute the Apple |
Software, with or without modifications, in source and/or binary forms; |
provided that if you redistribute the Apple Software in its entirety and |
without modifications, you must retain this notice and the following |
text and disclaimers in all such redistributions of the Apple Software. |
Neither the name, trademarks, service marks or logos of Apple Computer, |
Inc. may be used to endorse or promote products derived from the Apple |
Software without specific prior written permission from Apple. Except |
as expressly stated in this notice, no other rights or licenses, express |
or implied, are granted by Apple herein, including but not limited to |
any patent rights that may be infringed by your derivative works or by |
other works in which the Apple Software may be incorporated. |
The Apple Software is provided by Apple on an "AS IS" basis. APPLE |
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION |
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS |
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND |
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. |
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL |
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, |
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED |
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), |
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE |
POSSIBILITY OF SUCH DAMAGE. |
Copyright © 2005 Apple Computer, Inc., All Rights Reserved |
*/ |
#import "GetRecipesSummaryAction.h" |
#import <OSAKit/OSAKit.h> |
#import "Recipe.h" |
#import "RecipeUtilities.h" |
#pragma mark - |
#pragma mark Static Definitions |
#pragma mark |
// Name of the application support folder |
static NSString * LIBRARY_FOLDER_NAME = @"CoreRecipes"; |
// Name of the store file |
static NSString * LIBRARY_FILE_NAME = @"CoreRecipesLibrary.crx"; |
// Indexes of the sarch types, these correspond to the popup menu item indexes |
enum SearchTypes { |
RecipeSearch = 0, |
GroupSearch, |
CuisineSearch |
}; |
/** |
GetRecipesSummaryAction is a custom AMBundleAction that provides |
an Automator action that searches for recipes based on user configured |
search parameters and generates text summaries for matching recipes. |
*/ |
@implementation GetRecipesSummaryAction |
#pragma mark - |
#pragma mark Automator Action Hook |
#pragma mark |
/** |
Generates and returns an array of recipe summaries as strings, matching |
recipes based on the user-configured parameters searchType and searchText. |
Any input passed into this method is preserved and returned with the recipe |
summary text output. |
*/ |
- (id)runWithInput:(id)input fromAction:(AMAction *)anAction error:(NSDictionary **)errorInfo { |
NSMutableArray *output = [NSMutableArray array]; |
NSString *actionMessage = nil; |
NSArray *recipes = nil; |
NSArray *summaries = nil; |
// automator actions should pass through any input they do not consume/modify |
if (input != nil) { |
if ([input isKindOfClass:[NSArray class]]) { |
[output addObjectsFromArray: input]; |
} else { |
[output addObject: input]; |
} |
} |
// create the core data stack, find the recipes and generate the summaries |
// within an exception handler so we can give error feedback to Automator |
@try { |
if (managedObjectContext == nil) { |
actionMessage = @"accessing user recipe library"; |
[self initCoreDataStack]; |
} |
actionMessage = @"finding recipes"; |
recipes = [self recipesMatchingSearchParameters]; |
actionMessage = @"generating recipe summaries"; |
summaries = [self summariesFromRecipes:recipes]; |
} |
@catch (NSException *exception) { |
NSMutableDictionary *errorDict = [NSMutableDictionary dictionary]; |
[errorDict setObject:[NSString stringWithFormat:@"Error %@: %@", actionMessage, [exception reason]] forKey:OSAScriptErrorMessage]; |
[errorDict setObject:[NSNumber numberWithInt:errOSAGeneralError] forKey:OSAScriptErrorNumber]; |
*errorInfo = errorDict; |
return input; |
} |
// invalidate and clear the core data stack |
[managedObjectContext release]; |
managedObjectContext = nil; |
[persistentStoreCoordinator release]; |
persistentStoreCoordinator = nil; |
[output addObjectsFromArray:summaries]; |
return output; |
} |
#pragma mark - |
#pragma mark Recipe Searching |
#pragma mark |
/** |
Examines the searchType and searchText parameters (configured via bindings in |
the user interface), returns the recipes matched by the searchType-appropriate |
recipe search method. |
*/ |
- (NSArray *)recipesMatchingSearchParameters { |
NSString *searchText = [[self parameters] valueForKey:@"searchText"]; |
NSNumber *searchType = [[self parameters] valueForKey:@"searchType"]; |
switch ([searchType intValue]) { |
case RecipeSearch: |
return [self recipesWhoseRecipeNameContainsString:searchText]; |
case GroupSearch: |
return [self recipesWhoseGroupNameContainsString:searchText]; |
case CuisineSearch: |
return [self recipesWhoseCuisineNameContainsString:searchText]; |
} |
return nil; |
} |
/** |
Searches for Recipes with a name containing the searchText and |
returns a name-ordered array of matching recipes. |
*/ |
- (NSArray *)recipesWhoseRecipeNameContainsString:(NSString *)searchText { |
NSFetchRequest *request = [[NSFetchRequest alloc] init]; |
NSSortDescriptor *sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES] autorelease]; |
NSPredicate *searchPredicate = [NSPredicate predicateWithFormat:@"name contains[cd] %@", searchText]; |
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Recipe" inManagedObjectContext:managedObjectContext]; |
[request setEntity:entity]; |
[request setPredicate:searchPredicate]; |
[request setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]]; |
NSError *error = nil; |
NSArray *recipes = [managedObjectContext executeFetchRequest:request error:&error]; |
if (error != nil) |
[NSException raise:@"RecipeSearchError" format:@"Recipe search failed - %@", [error localizedDescription]]; |
return recipes; |
} |
/** |
Searches for Cuisines with a name containing the searchText and |
returns a name-ordered array of distinct recipes containined by them. |
*/ |
- (NSArray *)recipesWhoseCuisineNameContainsString:(NSString *)searchText { |
NSFetchRequest *request = [[NSFetchRequest alloc] init]; |
NSSortDescriptor *sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES] autorelease]; |
NSPredicate *searchPredicate = [NSPredicate predicateWithFormat:@"name contains[cd] %@", searchText]; |
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Cuisine" inManagedObjectContext:managedObjectContext]; |
[request setEntity:entity]; |
[request setPredicate:searchPredicate]; |
NSError *error = nil; |
NSArray *cuisines = [managedObjectContext executeFetchRequest:request error:&error]; |
if (error != nil) |
[NSException raise:@"RecipeSearchError" format:@"Recipe search failed - %@", [error localizedDescription]]; |
NSArray *recipes = [cuisines valueForKeyPath:@"@distinctUnionOfSets.recipes"]; |
return [recipes sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]]; |
} |
/** |
Searches for Groups (SmartGroups) with a name containing the searchText and |
returns a name-ordered array of distinct recipes containined by them. Provides |
special case behavior for the searchText "Library" which returns the entire |
set of recipes ("Library" is the name a SmartGroup that contains every recipe but |
only exists in memory) |
*/ |
- (NSArray *)recipesWhoseGroupNameContainsString:(NSString *)searchText { |
NSFetchRequest *request = [[NSFetchRequest alloc] init]; |
NSSortDescriptor *sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES] autorelease]; |
NSPredicate *searchPredicate = [NSPredicate predicateWithFormat:@"name contains[cd] %@", searchText]; |
// special case the "Library" group - since it only exists in-memory |
if ([searchText isEqualToString:@"Library"]) { |
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Recipe" inManagedObjectContext:managedObjectContext]; |
[request setEntity:entity]; |
[request setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]]; |
NSError *error = nil; |
NSArray *recipes = [managedObjectContext executeFetchRequest:request error:&error]; |
if (error != nil) |
[NSException raise:@"RecipeSearchError" format:@"Recipe search failed - %@", [error localizedDescription]]; |
return recipes; |
} |
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Group" inManagedObjectContext:managedObjectContext]; |
[request setEntity:entity]; |
[request setPredicate:searchPredicate]; |
NSError *error = nil; |
NSArray *groups = [managedObjectContext executeFetchRequest:request error:&error]; |
if (error != nil) |
[NSException raise:@"RecipeSearchError" format:@"Recipe search failed - %@", [error localizedDescription]]; |
NSArray *recipes = [groups valueForKeyPath:@"@distinctUnionOfSets.recipes"]; |
return [recipes sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]]; |
} |
#pragma mark - |
#pragma mark Text Summary Generation |
#pragma mark |
/** |
Returns an array of recipe summaries as NSStrings by iterating through the |
recipes and using RecipeUtilities -RTFDataForRecipe:includesHyperlink: to |
generate the RTF text which is then converted to a simple string. |
*/ |
- (NSArray *)summariesFromRecipes:(NSArray *)recipes { |
NSEnumerator *recipeEnumerator = [recipes objectEnumerator]; |
NSMutableArray *summaries = [NSMutableArray array]; |
Recipe *recipe = nil; |
while (recipe = [recipeEnumerator nextObject]) { |
NSError *error = nil; |
NSDictionary *attributes = nil; |
NSData *summaryData = [RecipeUtilities RTFDataForRecipe:recipe includesHyperlink:NO]; |
NSString *summary = [[[NSAttributedString alloc] initWithData:summaryData options:nil documentAttributes:&attributes error:&error] string]; |
if (error != nil) |
[NSException raise:@"RecipeSummaryError" format:@"Recipe summary creation failed - %@", [error localizedDescription]]; |
[summaries addObject:summary]; |
} |
return summaries; |
} |
#pragma mark - |
#pragma mark CoreData Stack Initialization |
#pragma mark |
/** |
Initializes the default store URL and adds the persistent store to the |
coordinator for the application (which is heretofore known as the "default |
store".) This implementation looks for the store file in the search path |
location (the application support folder). |
*/ |
- (void)initDefaultStore { |
// open up the default store |
NSArray *searchpaths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES); |
if ([searchpaths count] > 0) { |
// create the path to the library folder |
NSString *path = [[searchpaths objectAtIndex:0] stringByAppendingPathComponent: LIBRARY_FOLDER_NAME]; |
// set up the path for the "CoreRecipes.crx" file |
path = [[path stringByAppendingPathComponent: LIBRARY_FILE_NAME] stringByResolvingSymlinksInPath]; |
// if the store doesn't exist, raise an exception |
if (![[NSFileManager defaultManager] fileExistsAtPath:path]) |
[NSException raise:@"MissingResourceException" format:@"Missing recipe library"]; |
NSURL *defaultStoreURL = [[[NSURL alloc] initFileURLWithPath: path] autorelease]; |
// load the store |
[persistentStoreCoordinator addPersistentStoreWithType:NSXMLStoreType configuration:nil URL:defaultStoreURL options:nil error:nil]; |
} |
} |
/** |
Initializes the default Core Data stack variables for the application: the |
model we are going to use (from the application bundle), the persistent |
store coordinator, and the managed object context. |
*/ |
- (void)initCoreDataStack { |
// get the bundles |
NSArray *bundles = [NSArray arrayWithObject:[NSBundle bundleForClass:[self class]]]; |
// create the model and coordinator |
NSManagedObjectModel *model = [NSManagedObjectModel mergedModelFromBundles:bundles]; |
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model]; |
// craete the context and set its coordinator and merge policy |
managedObjectContext = [[NSManagedObjectContext alloc] init]; |
[managedObjectContext setPersistentStoreCoordinator:persistentStoreCoordinator]; |
// initialize the default store |
[self initDefaultStore]; |
} |
@end |
Copyright © 2005 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2005-06-01