| //--------------------------------------------------------------------------------------- |
| // $Id$ |
| // Copyright (c) 2006-2009 by Mulle Kybernetik. See License file for details. |
| //--------------------------------------------------------------------------------------- |
| |
| #import "NSInvocation+OCMAdditions.h" |
| |
| |
| @implementation NSInvocation(OCMAdditions) |
| |
| - (id)getArgumentAtIndexAsObject:(int)argIndex |
| { |
| const char* argType; |
| |
| argType = [[self methodSignature] getArgumentTypeAtIndex:argIndex]; |
| while(strchr("rnNoORV", argType[0]) != NULL) |
| argType += 1; |
| |
| if((strlen(argType) > 1) && (strchr("{^", argType[0]) == NULL) && (strcmp("@?", argType) != 0)) |
| [NSException raise:NSInvalidArgumentException format:@"Cannot handle argument type '%s'.", argType]; |
| |
| switch (argType[0]) |
| { |
| case '#': |
| case '@': |
| { |
| id value; |
| [self getArgument:&value atIndex:argIndex]; |
| return value; |
| } |
| case ':': |
| { |
| SEL s = (SEL)0; |
| [self getArgument:&s atIndex:argIndex]; |
| id value = NSStringFromSelector(s); |
| return value; |
| } |
| case 'i': |
| { |
| int value; |
| [self getArgument:&value atIndex:argIndex]; |
| return [NSNumber numberWithInt:value]; |
| } |
| case 's': |
| { |
| short value; |
| [self getArgument:&value atIndex:argIndex]; |
| return [NSNumber numberWithShort:value]; |
| } |
| case 'l': |
| { |
| long value; |
| [self getArgument:&value atIndex:argIndex]; |
| return [NSNumber numberWithLong:value]; |
| } |
| case 'q': |
| { |
| long long value; |
| [self getArgument:&value atIndex:argIndex]; |
| return [NSNumber numberWithLongLong:value]; |
| } |
| case 'c': |
| { |
| char value; |
| [self getArgument:&value atIndex:argIndex]; |
| return [NSNumber numberWithChar:value]; |
| } |
| case 'C': |
| { |
| unsigned char value; |
| [self getArgument:&value atIndex:argIndex]; |
| return [NSNumber numberWithUnsignedChar:value]; |
| } |
| case 'I': |
| { |
| unsigned int value; |
| [self getArgument:&value atIndex:argIndex]; |
| return [NSNumber numberWithUnsignedInt:value]; |
| } |
| case 'S': |
| { |
| unsigned short value; |
| [self getArgument:&value atIndex:argIndex]; |
| return [NSNumber numberWithUnsignedShort:value]; |
| } |
| case 'L': |
| { |
| unsigned long value; |
| [self getArgument:&value atIndex:argIndex]; |
| return [NSNumber numberWithUnsignedLong:value]; |
| } |
| case 'Q': |
| { |
| unsigned long long value; |
| [self getArgument:&value atIndex:argIndex]; |
| return [NSNumber numberWithUnsignedLongLong:value]; |
| } |
| case 'f': |
| { |
| float value; |
| [self getArgument:&value atIndex:argIndex]; |
| return [NSNumber numberWithFloat:value]; |
| } |
| case 'd': |
| { |
| double value; |
| [self getArgument:&value atIndex:argIndex]; |
| return [NSNumber numberWithDouble:value]; |
| } |
| case 'B': |
| { |
| bool value; |
| [self getArgument:&value atIndex:argIndex]; |
| return [NSNumber numberWithBool:value]; |
| } |
| case '^': |
| { |
| void *value = NULL; |
| [self getArgument:&value atIndex:argIndex]; |
| return [NSValue valueWithPointer:value]; |
| } |
| case '{': // structure |
| { |
| NSUInteger maxArgSize = [[self methodSignature] frameLength]; |
| NSMutableData *argumentData = [[[NSMutableData alloc] initWithLength:maxArgSize] autorelease]; |
| [self getArgument:[argumentData mutableBytes] atIndex:argIndex]; |
| return [NSValue valueWithBytes:[argumentData bytes] objCType:argType]; |
| } |
| |
| } |
| [NSException raise:NSInvalidArgumentException format:@"Argument type '%s' not supported", argType]; |
| return nil; |
| } |
| |
| - (NSString *)invocationDescription |
| { |
| NSMethodSignature *methodSignature = [self methodSignature]; |
| NSUInteger numberOfArgs = [methodSignature numberOfArguments]; |
| |
| if (numberOfArgs == 2) |
| return NSStringFromSelector([self selector]); |
| |
| NSArray *selectorParts = [NSStringFromSelector([self selector]) componentsSeparatedByString:@":"]; |
| NSMutableString *description = [[NSMutableString alloc] init]; |
| unsigned int i; |
| for(i = 2; i < numberOfArgs; i++) |
| { |
| [description appendFormat:@"%@%@:", (i > 2 ? @" " : @""), [selectorParts objectAtIndex:(i - 2)]]; |
| [description appendString:[self argumentDescriptionAtIndex:i]]; |
| } |
| |
| return [description autorelease]; |
| } |
| |
| - (NSString *)argumentDescriptionAtIndex:(int)argIndex |
| { |
| const char *argType = [[self methodSignature] getArgumentTypeAtIndex:argIndex]; |
| if(strchr("rnNoORV", argType[0]) != NULL) |
| argType += 1; |
| |
| switch(*argType) |
| { |
| case '@': return [self objectDescriptionAtIndex:argIndex]; |
| case 'B': return [self boolDescriptionAtIndex:argIndex]; |
| case 'c': return [self charDescriptionAtIndex:argIndex]; |
| case 'C': return [self unsignedCharDescriptionAtIndex:argIndex]; |
| case 'i': return [self intDescriptionAtIndex:argIndex]; |
| case 'I': return [self unsignedIntDescriptionAtIndex:argIndex]; |
| case 's': return [self shortDescriptionAtIndex:argIndex]; |
| case 'S': return [self unsignedShortDescriptionAtIndex:argIndex]; |
| case 'l': return [self longDescriptionAtIndex:argIndex]; |
| case 'L': return [self unsignedLongDescriptionAtIndex:argIndex]; |
| case 'q': return [self longLongDescriptionAtIndex:argIndex]; |
| case 'Q': return [self unsignedLongLongDescriptionAtIndex:argIndex]; |
| case 'd': return [self doubleDescriptionAtIndex:argIndex]; |
| case 'f': return [self floatDescriptionAtIndex:argIndex]; |
| // Why does this throw EXC_BAD_ACCESS when appending the string? |
| // case NSObjCStructType: return [self structDescriptionAtIndex:index]; |
| case '^': return [self pointerDescriptionAtIndex:argIndex]; |
| case '*': return [self cStringDescriptionAtIndex:argIndex]; |
| case ':': return [self selectorDescriptionAtIndex:argIndex]; |
| default: return [@"<??" stringByAppendingString:@">"]; // avoid confusion with trigraphs... |
| } |
| |
| } |
| |
| |
| - (NSString *)objectDescriptionAtIndex:(int)anInt |
| { |
| id object; |
| |
| [self getArgument:&object atIndex:anInt]; |
| if (object == nil) |
| return @"nil"; |
| else if(![object isProxy] && [object isKindOfClass:[NSString class]]) |
| return [NSString stringWithFormat:@"@\"%@\"", [object description]]; |
| else |
| return [object description]; |
| } |
| |
| - (NSString *)boolDescriptionAtIndex:(int)anInt |
| { |
| bool value; |
| |
| [self getArgument:&value atIndex:anInt]; |
| return value ? @"YES" : @"NO"; |
| } |
| |
| - (NSString *)charDescriptionAtIndex:(int)anInt |
| { |
| unsigned char buffer[128]; |
| memset(buffer, 0x0, 128); |
| |
| [self getArgument:&buffer atIndex:anInt]; |
| |
| // If there's only one character in the buffer, and it's 0 or 1, then we have a BOOL |
| if (buffer[1] == '\0' && (buffer[0] == 0 || buffer[0] == 1)) |
| return [NSString stringWithFormat:@"%@", (buffer[0] == 1 ? @"YES" : @"NO")]; |
| else |
| return [NSString stringWithFormat:@"'%c'", *buffer]; |
| } |
| |
| - (NSString *)unsignedCharDescriptionAtIndex:(int)anInt |
| { |
| unsigned char buffer[128]; |
| memset(buffer, 0x0, 128); |
| |
| [self getArgument:&buffer atIndex:anInt]; |
| return [NSString stringWithFormat:@"'%c'", *buffer]; |
| } |
| |
| - (NSString *)intDescriptionAtIndex:(int)anInt |
| { |
| int intValue; |
| |
| [self getArgument:&intValue atIndex:anInt]; |
| return [NSString stringWithFormat:@"%d", intValue]; |
| } |
| |
| - (NSString *)unsignedIntDescriptionAtIndex:(int)anInt |
| { |
| unsigned int intValue; |
| |
| [self getArgument:&intValue atIndex:anInt]; |
| return [NSString stringWithFormat:@"%d", intValue]; |
| } |
| |
| - (NSString *)shortDescriptionAtIndex:(int)anInt |
| { |
| short shortValue; |
| |
| [self getArgument:&shortValue atIndex:anInt]; |
| return [NSString stringWithFormat:@"%hi", shortValue]; |
| } |
| |
| - (NSString *)unsignedShortDescriptionAtIndex:(int)anInt |
| { |
| unsigned short shortValue; |
| |
| [self getArgument:&shortValue atIndex:anInt]; |
| return [NSString stringWithFormat:@"%hu", shortValue]; |
| } |
| |
| - (NSString *)longDescriptionAtIndex:(int)anInt |
| { |
| long longValue; |
| |
| [self getArgument:&longValue atIndex:anInt]; |
| return [NSString stringWithFormat:@"%ld", longValue]; |
| } |
| |
| - (NSString *)unsignedLongDescriptionAtIndex:(int)anInt |
| { |
| unsigned long longValue; |
| |
| [self getArgument:&longValue atIndex:anInt]; |
| return [NSString stringWithFormat:@"%lu", longValue]; |
| } |
| |
| - (NSString *)longLongDescriptionAtIndex:(int)anInt |
| { |
| long long longLongValue; |
| |
| [self getArgument:&longLongValue atIndex:anInt]; |
| return [NSString stringWithFormat:@"%qi", longLongValue]; |
| } |
| |
| - (NSString *)unsignedLongLongDescriptionAtIndex:(int)anInt |
| { |
| unsigned long long longLongValue; |
| |
| [self getArgument:&longLongValue atIndex:anInt]; |
| return [NSString stringWithFormat:@"%qu", longLongValue]; |
| } |
| |
| - (NSString *)doubleDescriptionAtIndex:(int)anInt; |
| { |
| double doubleValue; |
| |
| [self getArgument:&doubleValue atIndex:anInt]; |
| return [NSString stringWithFormat:@"%f", doubleValue]; |
| } |
| |
| - (NSString *)floatDescriptionAtIndex:(int)anInt |
| { |
| float floatValue; |
| |
| [self getArgument:&floatValue atIndex:anInt]; |
| return [NSString stringWithFormat:@"%f", floatValue]; |
| } |
| |
| - (NSString *)structDescriptionAtIndex:(int)anInt; |
| { |
| void *buffer; |
| |
| [self getArgument:&buffer atIndex:anInt]; |
| return [NSString stringWithFormat:@":(struct)%p", buffer]; |
| } |
| |
| - (NSString *)pointerDescriptionAtIndex:(int)anInt |
| { |
| void *buffer; |
| |
| [self getArgument:&buffer atIndex:anInt]; |
| return [NSString stringWithFormat:@"%p", buffer]; |
| } |
| |
| - (NSString *)cStringDescriptionAtIndex:(int)anInt |
| { |
| char buffer[128]; |
| |
| memset(buffer, 0x0, 128); |
| |
| [self getArgument:&buffer atIndex:anInt]; |
| return [NSString stringWithFormat:@"\"%s\"", buffer]; |
| } |
| |
| - (NSString *)selectorDescriptionAtIndex:(int)anInt |
| { |
| SEL selectorValue; |
| |
| [self getArgument:&selectorValue atIndex:anInt]; |
| return [NSString stringWithFormat:@"@selector(%@)", NSStringFromSelector(selectorValue)]; |
| } |
| |
| @end |