diff options
Diffstat (limited to 'Foundation/GTMNSAppleScript+Handler.m')
-rw-r--r-- | Foundation/GTMNSAppleScript+Handler.m | 283 |
1 files changed, 283 insertions, 0 deletions
diff --git a/Foundation/GTMNSAppleScript+Handler.m b/Foundation/GTMNSAppleScript+Handler.m new file mode 100644 index 0000000..d3eeffd --- /dev/null +++ b/Foundation/GTMNSAppleScript+Handler.m @@ -0,0 +1,283 @@ +// +// NSAppleScript+Handler.m +// +// Copyright 2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +#import <Carbon/Carbon.h> +#import "GTMNSAppleScript+Handler.h" +#import "GTMNSAppleEventDescriptor+Foundation.h" +#import "GTMNSAppleEventDescriptor+Handler.h" +#import "GTMFourCharCode.h" +#import "GTMMethodCheck.h" + +// Some private methods that we need to call +@interface NSAppleScript (NSPrivate) ++ (ComponentInstance)_defaultScriptingComponent; +- (OSAID) _compiledScriptID; +- (id)_initWithData:(NSData*)data error:(NSDictionary**)error; +@end + +@interface NSMethodSignature (NSPrivate) ++ (id)signatureWithObjCTypes:(const char *)fp8; +@end + +@implementation NSAppleScript(GTMAppleScriptHandlerAdditions) +GTM_METHOD_CHECK(NSAppleEventDescriptor, gtm_descriptorWithPositionalHandler:parametersArray:); // COV_NF_LINE +GTM_METHOD_CHECK(NSAppleEventDescriptor, gtm_descriptorWithLabeledHandler:labels:parameters:count:); // COV_NF_LINE +GTM_METHOD_CHECK(NSAppleEventDescriptor, gtm_registerSelector:forTypes:count:); // COV_NF_LINE + ++ (void)load { + DescType types[] = { + typeScript + }; + + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + [NSAppleEventDescriptor gtm_registerSelector:@selector(gtm_scriptValue) + forTypes:types + count:sizeof(types)/sizeof(DescType)]; + + DescType types2[] = { + 'evnt' // No type code for this one + }; + + [NSAppleEventDescriptor gtm_registerSelector:@selector(gtm_eventValue) + forTypes:types2 + count:sizeof(types2)/sizeof(DescType)]; + [pool release]; +} + +- (OSAID)gtm_realIDAndComponent:(ComponentInstance*)component { + if (![self isCompiled]) { + NSDictionary *error; + if (![self compileAndReturnError:&error]) { + _GTMDevLog(@"Unable to compile script: %@ %@", self, error); + return kOSANullScript; + } + } + OSAID genericID = [self _compiledScriptID]; + ComponentInstance genericComponent = [NSAppleScript _defaultScriptingComponent]; + OSAError err = OSAGenericToRealID(genericComponent, &genericID, component); + if (err) { + _GTMDevLog(@"Unable to get real id script: %@ %@", self, err); // COV_NF_LINE + genericID = kOSANullScript; // COV_NF_LINE + } + return genericID; +} + +- (NSAppleEventDescriptor*)gtm_executePositionalHandler:(NSString*)handler + parameters:(NSArray*)params + error:(NSDictionary**)error { + NSAppleEventDescriptor *event + = [NSAppleEventDescriptor gtm_descriptorWithPositionalHandler:handler + parametersArray:params]; + return [self executeAppleEvent:event error:error]; +} + +- (NSAppleEventDescriptor*)gtm_executeLabeledHandler:(NSString*)handler + labels:(AEKeyword*)labels + parameters:(id*)params + count:(NSUInteger)count + error:(NSDictionary **)error { + NSAppleEventDescriptor *event + = [NSAppleEventDescriptor gtm_descriptorWithLabeledHandler:handler + labels:labels + parameters:params + count:count]; + return [self executeAppleEvent:event error:error]; +} + +- (NSSet*)gtm_handlers { + AEDescList names = { typeNull, NULL }; + NSArray *array = nil; + ComponentInstance component; + OSAID osaID = [self gtm_realIDAndComponent:&component]; + OSAError err = OSAGetHandlerNames(component, kOSAModeNull, osaID, &names); + if (!err) { + NSAppleEventDescriptor *desc + = [[[NSAppleEventDescriptor alloc] initWithAEDescNoCopy:&names] autorelease]; + array = [desc gtm_objectValue]; + } + if (err) { + _GTMDevLog(@"Error getting handlers: %d", err); + } + return [NSSet setWithArray:array]; +} + +- (NSSet*)gtm_properties { + AEDescList names = { typeNull, NULL }; + NSArray *array = nil; + ComponentInstance component; + OSAID osaID = [self gtm_realIDAndComponent:&component]; + OSAError err = OSAGetPropertyNames(component, kOSAModeNull, osaID, &names); + if (!err) { + NSAppleEventDescriptor *desc + = [[[NSAppleEventDescriptor alloc] initWithAEDescNoCopy:&names] autorelease]; + array = [desc gtm_objectValue]; + } + if (err) { + _GTMDevLog(@"Error getting properties: %d", err); + } + return [NSSet setWithArray:array]; +} + +- (BOOL)gtm_setValue:(id)value forProperty:(NSString*)property { + BOOL wasGood = NO; + NSAppleEventDescriptor *desc = [value gtm_appleEventDescriptor]; + NSAppleEventDescriptor *propertyName = [property gtm_appleEventDescriptor]; + OSAError error = paramErr; + if (desc && propertyName) { + OSAID valueID = kOSANullScript; + ComponentInstance component; + OSAID scriptID = [self gtm_realIDAndComponent:&component]; + error = OSACoerceFromDesc(component, + [desc aeDesc], + kOSAModeNull, + &valueID); + if (!error) { + error = OSASetProperty(component, kOSAModeNull, + scriptID, [propertyName aeDesc], valueID); + if (!error) { + wasGood = YES; + } + } + } + if (!wasGood) { + _GTMDevLog(@"Unable to setValue:%@ forProperty:%@ from %@ (%d)", + value, property, self, error); + } + return wasGood; +} + +- (id)gtm_valueForProperty:(NSString*)property { + id value = nil; + NSAppleEventDescriptor *propertyName = [property gtm_appleEventDescriptor]; + OSAError error = paramErr; + if (propertyName) { + ComponentInstance component; + OSAID scriptID = [self gtm_realIDAndComponent:&component]; + OSAID valueID = kOSANullScript; + error = OSAGetProperty(component, + kOSAModeNull, + scriptID, + [propertyName aeDesc], + &valueID); + if (!error) { + AEDesc aeDesc; + error = OSACoerceToDesc(component, + valueID, + typeWildCard, + kOSAModeNull, + &aeDesc); + if (!error) { + NSAppleEventDescriptor *desc + = [[[NSAppleEventDescriptor alloc] initWithAEDescNoCopy:&aeDesc] autorelease]; + value = [desc gtm_objectValue]; + } + } + } + if (error) { + _GTMDevLog(@"Unable to get valueForProperty:%@ from %@ (%d)", + property, self, error); + } + return value; +} + +- (NSAppleEventDescriptor*)gtm_appleEventDescriptor { + ComponentInstance component; + OSAID osaID = [self gtm_realIDAndComponent:&component]; + AEDesc result = { typeNull, NULL }; + NSAppleEventDescriptor *desc = nil; + OSAError err = OSACoerceToDesc(component, + osaID, + typeScript, + kOSAModeNull, + &result); + if (!err) { + desc = [[[NSAppleEventDescriptor alloc] initWithAEDescNoCopy:&result] autorelease]; + } else { + _GTMDevLog(@"Unable to coerce script %d", err); // COV_NF_LINE + } + return desc; +} + +- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector { + NSMethodSignature *signature = [super methodSignatureForSelector:aSelector]; + if (!signature) { + NSMutableString *types = [NSMutableString stringWithString:@"@@:"]; + NSString *selName = NSStringFromSelector(aSelector); + NSArray *selArray = [selName componentsSeparatedByString:@":"]; + NSUInteger count = [selArray count]; + for (NSUInteger i = 1; i < count; i++) { + [types appendString:@"@"]; + } + signature = [NSMethodSignature signatureWithObjCTypes:[types UTF8String]]; + } + return signature; +} + +- (void)forwardInvocation:(NSInvocation *)invocation { + SEL sel = [invocation selector]; + NSMutableString *handlerName = [NSStringFromSelector(sel) mutableCopy]; + NSUInteger handlerOrigLength = [handlerName length]; + [handlerName replaceOccurrencesOfString:@":" + withString:@"" + options:0 + range:NSMakeRange(0,handlerOrigLength)]; + NSUInteger argCount = handlerOrigLength - [handlerName length]; + NSMutableArray *args = [NSMutableArray arrayWithCapacity:argCount]; + for (NSUInteger i = 0; i < argCount; ++i) { + id arg; + // +2 to ignore _sel and _cmd + [invocation getArgument:&arg atIndex:i + 2]; + [args addObject:arg]; + } + NSDictionary *error = nil; + NSAppleEventDescriptor *desc = [self gtm_executePositionalHandler:handlerName + parameters:args + error:&error]; + if ([[invocation methodSignature] methodReturnLength] > 0) { + id returnValue = [desc gtm_objectValue]; + [invocation setReturnValue:&returnValue]; + } +} +@end + +@implementation NSAppleEventDescriptor(GMAppleEventDescriptorScriptAdditions) + +- (NSAppleScript*)gtm_scriptValue { + NSDictionary *error; + NSAppleScript *script = [[[NSAppleScript alloc] _initWithData:[self data] + error:&error] autorelease]; + if (!script) { + _GTMDevLog(@"Unable to create script: %@", error); // COV_NF_LINE + } + return script; +} + +- (NSString*)gtm_eventValue { + struct AEEventRecordStruct { + AEEventClass eventClass; + AEEventID eventID; + }; + NSData *data = [self data]; + const struct AEEventRecordStruct *record + = (const struct AEEventRecordStruct*)[data bytes]; + NSString *eClass = [GTMFourCharCode stringWithFourCharCode:record->eventClass]; + NSString *eID = [GTMFourCharCode stringWithFourCharCode:record->eventID]; + return [eClass stringByAppendingString:eID]; +} +@end + |