aboutsummaryrefslogtreecommitdiff
path: root/Foundation/GTMNSAppleEventDescriptor+Foundation.m
diff options
context:
space:
mode:
Diffstat (limited to 'Foundation/GTMNSAppleEventDescriptor+Foundation.m')
-rw-r--r--Foundation/GTMNSAppleEventDescriptor+Foundation.m488
1 files changed, 488 insertions, 0 deletions
diff --git a/Foundation/GTMNSAppleEventDescriptor+Foundation.m b/Foundation/GTMNSAppleEventDescriptor+Foundation.m
new file mode 100644
index 0000000..2b6acd4
--- /dev/null
+++ b/Foundation/GTMNSAppleEventDescriptor+Foundation.m
@@ -0,0 +1,488 @@
+//
+// GTMNSAppleEventDescriptor+Foundation.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 "GTMNSAppleEventDescriptor+Foundation.h"
+#import "GTMFourCharCode.h"
+#import <Carbon/Carbon.h> // Needed Solely For keyASUserRecordFields
+
+// Map of types to selectors.
+static NSMutableDictionary *gTypeMap = nil;
+
+@implementation NSAppleEventDescriptor (GTMAppleEventDescriptorArrayAdditions)
+
++ (void)gtm_registerSelector:(SEL)selector
+ forTypes:(DescType*)types
+ count:(NSUInteger)count {
+ if (selector && types && count > 0) {
+ @synchronized(self) {
+ if (!gTypeMap) {
+ gTypeMap = [[NSMutableDictionary alloc] init];
+ }
+ NSString *selString = NSStringFromSelector(selector);
+ for (NSUInteger i = 0; i < count; ++i) {
+ NSNumber *key = [NSNumber numberWithUnsignedInt:types[i]];
+ NSString *exists = [gTypeMap objectForKey:key];
+ if (exists) {
+ _GTMDevLog(@"%@ being replaced with %@ exists for type: %@",
+ exists, selString, key);
+ }
+ [gTypeMap setObject:selString forKey:key];
+ }
+ }
+ }
+}
+
+- (id)gtm_objectValue {
+ id value = nil;
+
+ // Check our registered types to see if we have anything
+ if (gTypeMap) {
+ @synchronized(gTypeMap) {
+ DescType type = [self descriptorType];
+ NSNumber *key = [NSNumber numberWithUnsignedInt:type];
+ NSString *selectorString = [gTypeMap objectForKey:key];
+ if (selectorString) {
+ SEL selector = NSSelectorFromString(selectorString);
+ value = [self performSelector:selector];
+ } else {
+ value = [self stringValue];
+ }
+ }
+ }
+ return value;
+}
+
+- (NSArray*)gtm_arrayValue {
+ NSUInteger count = [self numberOfItems];
+ NSAppleEventDescriptor *workingDesc = self;
+ if (count == 0) {
+ // Create a list to work with.
+ workingDesc = [self coerceToDescriptorType:typeAEList];
+ count = [workingDesc numberOfItems];
+ }
+ NSMutableArray *items = [NSMutableArray arrayWithCapacity:count];
+ for (NSUInteger i = 1; i <= count; ++i) {
+ NSAppleEventDescriptor *desc = [workingDesc descriptorAtIndex:i];
+ id value = [desc gtm_objectValue];
+ if (!value) {
+ _GTMDevLog(@"Unknown type of descriptor %@", [desc description]);
+ return nil;
+ }
+ [items addObject:value];
+ }
+ return items;
+}
+
+- (NSDictionary*)gtm_dictionaryValue {
+ NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
+ NSAppleEventDescriptor *userRecord = [self descriptorForKeyword:keyASUserRecordFields];
+ if (userRecord) {
+ NSEnumerator *userItems = [[userRecord gtm_arrayValue] objectEnumerator];
+ NSString *key;
+ while ((key = [userItems nextObject])) {
+ NSString *value = [userItems nextObject];
+ if (!value) {
+ _GTMDevLog(@"Got a key %@ with no value in %@", key, userItems);
+ return nil;
+ }
+ [dictionary setObject:value forKey:key];
+ }
+ } else {
+ NSUInteger count = [self numberOfItems];
+ for (NSUInteger i = 1; i <= count; ++i) {
+ AEKeyword key = [self keywordForDescriptorAtIndex:i];
+ NSAppleEventDescriptor *desc = [self descriptorForKeyword:key];
+ id value = [desc gtm_objectValue];
+ if (!value) {
+ _GTMDevLog(@"Unknown type of descriptor %@", [desc description]);
+ return nil;
+ }
+ [dictionary setObject:value
+ forKey:[GTMFourCharCode fourCharCodeWithFourCharCode:key]];
+ }
+ }
+ return dictionary;
+}
+
+- (NSNull*)gtm_nullValue {
+ return [NSNull null];
+}
+
++ (NSAppleEventDescriptor*)gtm_descriptorWithDouble:(double)real {
+ return [NSAppleEventDescriptor descriptorWithDescriptorType:typeIEEE64BitFloatingPoint
+ bytes:&real
+ length:sizeof(real)];
+}
+
++ (NSAppleEventDescriptor*)gtm_descriptorWithFloat:(float)real {
+ return [NSAppleEventDescriptor descriptorWithDescriptorType:typeIEEE32BitFloatingPoint
+ bytes:&real
+ length:sizeof(real)];
+}
+
+
++ (NSAppleEventDescriptor*)gtm_descriptorWithCGFloat:(CGFloat)real {
+#if CGFLOAT_IS_DOUBLE
+ return [self gtm_descriptorWithDouble:real];
+#else
+ return [self gtm_descriptorWithFloat:real];
+#endif
+}
+
+- (double)gtm_doubleValue {
+ double value = NAN;
+ NSNumber *number = [self gtm_numberValue];
+ if (number) {
+ value = [number doubleValue];
+ }
+ return value;
+}
+
+- (float)gtm_floatValue {
+ float value = NAN;
+ NSNumber *number = [self gtm_numberValue];
+ if (number) {
+ value = [number floatValue];
+ }
+ return value;
+}
+
+- (CGFloat)gtm_cgFloatValue {
+#if CGFLOAT_IS_DOUBLE
+ return [self gtm_doubleValue];
+#else
+ return [self gtm_floatValue];
+#endif
+}
+
+- (NSNumber*)gtm_numberValue {
+ typedef struct {
+ DescType type;
+ SEL selector;
+ } TypeSelectorMap;
+ TypeSelectorMap typeSelectorMap[] = {
+ { typeFalse, @selector(numberWithBool:) },
+ { typeTrue, @selector(numberWithBool:) },
+ { typeBoolean, @selector(numberWithBool:) },
+ { typeSInt16, @selector(numberWithShort:) },
+ { typeSInt32, @selector(numberWithInt:) },
+ { typeUInt32, @selector(numberWithUnsignedInt:) },
+ { typeSInt64, @selector(numberWithLongLong:) },
+ { typeIEEE32BitFloatingPoint, @selector(numberWithFloat:) },
+ { typeIEEE64BitFloatingPoint, @selector(numberWithDouble:) }
+ };
+ DescType type = [self descriptorType];
+ SEL selector = nil;
+ for (size_t i = 0; i < sizeof(typeSelectorMap) / sizeof(TypeSelectorMap); ++i) {
+ if (type == typeSelectorMap[i].type) {
+ selector = typeSelectorMap[i].selector;
+ break;
+ }
+ }
+ NSAppleEventDescriptor *desc = self;
+ if (!selector) {
+ // COV_NF_START - Don't know how to force this in a unittest
+ _GTMDevLog(@"Didn't get a valid selector?");
+ desc = [self coerceToDescriptorType:typeIEEE64BitFloatingPoint];
+ selector = @selector(numberWithDouble:);
+ // COV_NF_END
+ }
+ NSData *descData = [desc data];
+ const void *bytes = [descData bytes];
+ if (!bytes) {
+ // COV_NF_START - Don't know how to force this in a unittest
+ _GTMDevLog(@"Unable to get bytes from %@", desc);
+ return nil;
+ // COV_NF_END
+ }
+ Class numberClass = [NSNumber class];
+ NSMethodSignature *signature = [numberClass methodSignatureForSelector:selector];
+ NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
+ [invocation setSelector:selector];
+ [invocation setArgument:(void*)bytes atIndex:2];
+ [invocation setTarget:numberClass];
+ [invocation invoke];
+ NSNumber *value = nil;
+ [invocation getReturnValue:&value];
+ return value;
+}
+
+@end
+
+@implementation NSObject (GTMAppleEventDescriptorObjectAdditions)
+- (NSAppleEventDescriptor*)gtm_appleEventDescriptor {
+ return [NSAppleEventDescriptor descriptorWithString:[self description]];
+}
+@end
+
+@implementation NSArray (GTMAppleEventDescriptorObjectAdditions)
+
++ (void)load {
+ DescType types[] = {
+ typeAEList,
+ };
+
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ [NSAppleEventDescriptor gtm_registerSelector:@selector(gtm_arrayValue)
+ forTypes:types
+ count:sizeof(types)/sizeof(DescType)];
+ [pool release];
+}
+
+- (NSAppleEventDescriptor*)gtm_appleEventDescriptor {
+ NSAppleEventDescriptor *desc = [NSAppleEventDescriptor listDescriptor];
+ NSUInteger count = [self count];
+ for (NSUInteger i = 1; i <= count; ++i) {
+ id item = [self objectAtIndex:i-1];
+ NSAppleEventDescriptor *itemDesc = [item gtm_appleEventDescriptor];
+ if (!itemDesc) {
+ _GTMDevLog(@"Unable to create Apple Event Descriptor for %@", [self description]);
+ return nil;
+ }
+ [desc insertDescriptor:itemDesc atIndex:i];
+ }
+ return desc;
+}
+@end
+
+@implementation NSDictionary (GTMAppleEventDescriptorObjectAdditions)
+
++ (void)load {
+ DescType types[] = {
+ typeAERecord,
+ };
+
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ [NSAppleEventDescriptor gtm_registerSelector:@selector(gtm_dictionaryValue)
+ forTypes:types
+ count:sizeof(types)/sizeof(DescType)];
+ [pool release];
+}
+
+- (NSAppleEventDescriptor*)gtm_appleEventDescriptor {
+ NSEnumerator* keys = [self keyEnumerator];
+ Class keyClass = nil;
+ id key = nil;
+ while ((key = [keys nextObject])) {
+ if (!keyClass) {
+ if ([key isKindOfClass:[GTMFourCharCode class]]) {
+ keyClass = [GTMFourCharCode class];
+ } else if ([key isKindOfClass:[NSString class]]) {
+ keyClass = [NSString class];
+ } else {
+ _GTMDevLog(@"Keys must be of type NSString or GTMFourCharCode: %@", key);
+ return nil;
+ }
+ }
+ if (![key isKindOfClass:keyClass]) {
+ _GTMDevLog(@"Keys must be homogenous (first key was of type %@) "
+ "and of type NSString or GTMFourCharCode: %@", keyClass, key);
+ return nil;
+ }
+ }
+ NSAppleEventDescriptor *desc = [NSAppleEventDescriptor recordDescriptor];
+ if ([keyClass isEqual:[NSString class]]) {
+ NSMutableArray *array = [NSMutableArray arrayWithCapacity:[self count] * 2];
+ keys = [self keyEnumerator];
+ while ((key = [keys nextObject])) {
+ [array addObject:key];
+ [array addObject:[self objectForKey:key]];
+ }
+ NSAppleEventDescriptor *userRecord = [array gtm_appleEventDescriptor];
+ if (!userRecord) {
+ return nil;
+ }
+ [desc setDescriptor:userRecord forKeyword:keyASUserRecordFields];
+ } else {
+ keys = [self keyEnumerator];
+ while ((key = [keys nextObject])) {
+ id value = [self objectForKey:key];
+ NSAppleEventDescriptor *valDesc = [value gtm_appleEventDescriptor];
+ if (!valDesc) {
+ return nil;
+ }
+ [desc setDescriptor:valDesc forKeyword:[key fourCharCode]];
+ }
+ }
+ return desc;
+}
+
+@end
+
+@implementation NSNull (GTMAppleEventDescriptorObjectAdditions)
++ (void)load {
+ DescType types[] = {
+ typeNull
+ };
+
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ [NSAppleEventDescriptor gtm_registerSelector:@selector(gtm_nullValue)
+ forTypes:types
+ count:sizeof(types)/sizeof(DescType)];
+ [pool release];
+}
+
+- (NSAppleEventDescriptor*)gtm_appleEventDescriptor {
+ return [NSAppleEventDescriptor nullDescriptor];
+}
+@end
+
+@implementation NSString (GTMAppleEventDescriptorObjectAdditions)
+
++ (void)load {
+ DescType types[] = {
+ typeUTF16ExternalRepresentation,
+ typeUnicodeText,
+ typeUTF8Text,
+ typeCString,
+ typePString,
+ typeChar,
+ typeIntlText };
+
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ [NSAppleEventDescriptor gtm_registerSelector:@selector(stringValue)
+ forTypes:types
+ count:sizeof(types)/sizeof(DescType)];
+ [pool release];
+}
+
+- (NSAppleEventDescriptor*)gtm_appleEventDescriptor {
+ return [NSAppleEventDescriptor descriptorWithString:self];
+}
+@end
+
+@implementation NSNumber (GTMAppleEventDescriptorObjectAdditions)
+
++ (void)load {
+ DescType types[] = {
+ typeTrue,
+ typeFalse,
+ typeBoolean,
+ typeSInt16,
+ typeSInt32,
+ typeUInt32,
+ typeSInt64,
+ typeIEEE32BitFloatingPoint,
+ typeIEEE64BitFloatingPoint };
+
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ [NSAppleEventDescriptor gtm_registerSelector:@selector(gtm_numberValue)
+ forTypes:types
+ count:sizeof(types)/sizeof(DescType)];
+ [pool release];
+}
+
+- (NSAppleEventDescriptor*)gtm_appleEventDescriptor {
+ const char *type = [self objCType];
+ if (!type || strlen(type) != 1) return nil;
+
+ DescType desiredType = typeNull;
+ NSAppleEventDescriptor *desc = nil;
+ switch (type[0]) {
+ // COV_NF_START
+ // I can't seem to convince objcType to return something of this type
+ case 'B':
+ desc = [NSAppleEventDescriptor descriptorWithBoolean:[self boolValue]];
+ break;
+ // COV_NF_END
+
+ case 'c':
+ case 'C':
+ case 's':
+ case 'S':
+ desiredType = typeSInt16;
+ break;
+
+ case 'i':
+ case 'l':
+ desiredType = typeSInt32;
+ break;
+
+ // COV_NF_START
+ // I can't seem to convince objcType to return something of this type
+ case 'I':
+ case 'L':
+ desiredType = typeUInt32;
+ break;
+ // COV_NF_END
+
+ case 'q':
+ case 'Q':
+ desiredType = typeSInt64;
+ break;
+
+ case 'f':
+ desiredType = typeIEEE32BitFloatingPoint;
+ break;
+
+ case 'd':
+ default:
+ desiredType = typeIEEE64BitFloatingPoint;
+ break;
+ }
+
+ if (!desc) {
+ desc = [NSAppleEventDescriptor gtm_descriptorWithDouble:[self doubleValue]];
+ if (desc && desiredType != typeIEEE64BitFloatingPoint) {
+ desc = [desc coerceToDescriptorType:desiredType];
+ }
+ }
+ return desc;
+}
+
+@end
+
+@implementation NSProcessInfo (GTMAppleEventDescriptorObjectAdditions)
+
+- (NSAppleEventDescriptor*)gtm_appleEventDescriptor {
+ ProcessSerialNumber psn = { 0, kCurrentProcess };
+ return [NSAppleEventDescriptor descriptorWithDescriptorType:typeProcessSerialNumber
+ bytes:&psn
+ length:sizeof(ProcessSerialNumber)];
+}
+
+@end
+
+@implementation NSAppleEventDescriptor (GTMAppleEventDescriptorAdditions)
+
+- (BOOL)gtm_sendEventWithMode:(AESendMode)mode
+ timeOut:(NSTimeInterval)timeout
+ reply:(NSAppleEventDescriptor**)reply {
+ BOOL isGood = YES;
+ AppleEvent replyEvent = { typeNull, NULL };
+ OSStatus err = AESendMessage([self aeDesc], &replyEvent, mode, timeout * 60);
+ if (err) {
+ isGood = NO;
+ _GTMDevLog(@"Unable to send message: %@ %d", self, err);
+ replyEvent.descriptorType = typeNull;
+ replyEvent.dataHandle = NULL;
+ }
+ NSAppleEventDescriptor *replyDesc = [[[NSAppleEventDescriptor alloc] initWithAEDescNoCopy:&replyEvent] autorelease];
+ if (isGood) {
+ NSAppleEventDescriptor *errorDesc = [replyDesc descriptorForKeyword:keyErrorNumber];
+ if (errorDesc && [errorDesc int32Value]) {
+ isGood = NO;
+ }
+ }
+ if (reply) {
+ *reply = replyDesc;
+ }
+ return isGood;
+}
+
+@end