From c53ec5520e39096e0804ce8d89a21378c0904481 Mon Sep 17 00:00:00 2001 From: thomasvl Date: Fri, 13 Jun 2008 19:21:50 +0000 Subject: Landing a log of AppleScript/AppleEvent support code. Landing GTMHTTPServer as a simple server but mainly for use in unittesting. _GTMCompileAssert for doing compile time assertions to GTMDefines.h Lots of improvments for UnitTesting, Dave's gonna put up a wiki page shortly with the full details of what can be done. --- Foundation/GTMNSAppleEventDescriptor+Foundation.m | 488 ++++++++++++++++++++++ 1 file changed, 488 insertions(+) create mode 100644 Foundation/GTMNSAppleEventDescriptor+Foundation.m (limited to 'Foundation/GTMNSAppleEventDescriptor+Foundation.m') 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 // 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 -- cgit v1.2.3