123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219 |
- //
- // Licensed under the terms in License.txt
- //
- // Copyright 2010 Allen Ding. All rights reserved.
- //
- #import "KWMessagePattern.h"
- #import "KWAny.h"
- #import "KWFormatter.h"
- #import "KWNull.h"
- #import "KWObjCUtilities.h"
- #import "KWValue.h"
- #import "NSInvocation+KiwiAdditions.h"
- #import "NSMethodSignature+KiwiAdditions.h"
- #import "KWGenericMatchEvaluator.h"
- @implementation KWMessagePattern
- #pragma mark - Initializing
- - (id)initWithSelector:(SEL)aSelector {
- return [self initWithSelector:aSelector argumentFilters:nil];
- }
- - (id)initWithSelector:(SEL)aSelector argumentFilters:(NSArray *)anArray {
- self = [super init];
- if (self) {
- selector = aSelector;
- if ([anArray count] > 0)
- argumentFilters = [anArray copy];
- }
- return self;
- }
- - (id)initWithSelector:(SEL)aSelector firstArgumentFilter:(id)firstArgumentFilter argumentList:(va_list)argumentList {
- NSUInteger count = KWSelectorParameterCount(aSelector);
- NSMutableArray *array = [NSMutableArray arrayWithCapacity:count];
- [array addObject:(firstArgumentFilter != nil) ? firstArgumentFilter : [KWNull null]];
- for (NSUInteger i = 1; i < count; ++i)
- {
- id object = va_arg(argumentList, id);
- [array addObject:(object != nil) ? object : [KWNull null]];
- }
- va_end(argumentList);
- return [self initWithSelector:aSelector argumentFilters:array];
- }
- + (id)messagePatternWithSelector:(SEL)aSelector {
- return [self messagePatternWithSelector:aSelector argumentFilters:nil];
- }
- + (id)messagePatternWithSelector:(SEL)aSelector argumentFilters:(NSArray *)anArray {
- return [[self alloc] initWithSelector:aSelector argumentFilters:anArray];
- }
- + (id)messagePatternWithSelector:(SEL)aSelector firstArgumentFilter:(id)firstArgumentFilter argumentList:(va_list)argumentList {
- return [[self alloc] initWithSelector:aSelector firstArgumentFilter:firstArgumentFilter argumentList:argumentList];
- }
- + (id)messagePatternFromInvocation:(NSInvocation *)anInvocation {
- NSMethodSignature *signature = [anInvocation methodSignature];
- NSUInteger numberOfMessageArguments = [signature numberOfMessageArguments];
- NSMutableArray *argumentFilters = nil;
- if (numberOfMessageArguments > 0) {
- argumentFilters = [[NSMutableArray alloc] initWithCapacity:numberOfMessageArguments];
- for (NSUInteger i = 0; i < numberOfMessageArguments; ++i) {
- const char *type = [signature messageArgumentTypeAtIndex:i];
- void* argumentDataBuffer = malloc(KWObjCTypeLength(type));
- [anInvocation getMessageArgument:argumentDataBuffer atIndex:i];
- id object = nil;
- if(*(__unsafe_unretained id*)argumentDataBuffer != [KWAny any] && !KWObjCTypeIsObject(type)) {
- NSData *data = [anInvocation messageArgumentDataAtIndex:i];
- object = [KWValue valueWithBytes:[data bytes] objCType:type];
- } else {
- object = *(__unsafe_unretained id*)argumentDataBuffer;
- if (object != [KWAny any] && KWObjCTypeIsBlock(type)) {
- object = [object copy]; // Converting NSStackBlock to NSMallocBlock
- }
- }
-
- [argumentFilters addObject:(object != nil) ? object : [KWNull null]];
- free(argumentDataBuffer);
- }
- }
- return [self messagePatternWithSelector:[anInvocation selector] argumentFilters:argumentFilters];
- }
- #pragma mark - Properties
- @synthesize selector;
- @synthesize argumentFilters;
- #pragma mark - Matching Invocations
- - (BOOL)argumentFiltersMatchInvocationArguments:(NSInvocation *)anInvocation {
- if (self.argumentFilters == nil)
- return YES;
- NSMethodSignature *signature = [anInvocation methodSignature];
- NSUInteger numberOfArgumentFilters = [self.argumentFilters count];
- NSUInteger numberOfMessageArguments = [signature numberOfMessageArguments];
- for (NSUInteger i = 0; i < numberOfMessageArguments && i < numberOfArgumentFilters; ++i) {
- const char *objCType = [signature messageArgumentTypeAtIndex:i];
- id __autoreleasing object = nil;
- // Extract message argument into object (wrapping values if neccesary)
- if (KWObjCTypeIsObject(objCType) || KWObjCTypeIsClass(objCType)) {
- [anInvocation getMessageArgument:&object atIndex:i];
- } else {
- NSData *data = [anInvocation messageArgumentDataAtIndex:i];
- object = [KWValue valueWithBytes:[data bytes] objCType:objCType];
- }
- // Match argument filter to object
- id argumentFilter = (self.argumentFilters)[i];
- if ([argumentFilter isEqual:[KWAny any]]) {
- continue;
- }
- if ([KWGenericMatchEvaluator isGenericMatcher:argumentFilter]) {
- id matcher = argumentFilter;
- if ([object isKindOfClass:[KWValue class]] && [object isNumeric]) {
- NSNumber *number = [object numberValue];
- if (![KWGenericMatchEvaluator genericMatcher:matcher matches:number]) {
- return NO;
- }
- } else if (![KWGenericMatchEvaluator genericMatcher:matcher matches:object]) {
- return NO;
- }
- } else if ([argumentFilter isEqual:[KWNull null]]) {
- if (!KWObjCTypeIsPointerLike(objCType)) {
- [NSException raise:@"KWMessagePatternException" format:@"nil was specified as an argument filter, but argument(%d) is not a pointer for @selector(%@)", (int)(i + 1), NSStringFromSelector([anInvocation selector])];
- }
- void *p = nil;
- [anInvocation getMessageArgument:&p atIndex:i];
- if (p != nil)
- return NO;
- } else if (![argumentFilter isEqual:object]) {
- return NO;
- }
- }
- return YES;
- }
- - (BOOL)matchesInvocation:(NSInvocation *)anInvocation {
- return self.selector == [anInvocation selector] && [self argumentFiltersMatchInvocationArguments:anInvocation];
- }
- #pragma mark - Comparing Message Patterns
- - (NSUInteger)hash {
- return [NSStringFromSelector(self.selector) hash];
- }
- - (BOOL)isEqual:(id)object {
- if (![object isKindOfClass:[KWMessagePattern class]])
- return NO;
- return [self isEqualToMessagePattern:object];
- }
- - (BOOL)isEqualToMessagePattern:(KWMessagePattern *)aMessagePattern {
- if (self.selector != aMessagePattern.selector)
- return NO;
- if (self.argumentFilters == nil && aMessagePattern.argumentFilters == nil)
- return YES;
- return [self.argumentFilters isEqualToArray:aMessagePattern.argumentFilters];
- }
- #pragma mark - Retrieving String Representations
- - (NSString *)selectorString {
- return NSStringFromSelector(self.selector);
- }
- - (NSString *)selectorAndArgumentFiltersString {
- NSMutableString *description = [[NSMutableString alloc] init];
- NSArray *components = [NSStringFromSelector(self.selector) componentsSeparatedByString:@":"];
- NSUInteger count = [components count] - 1;
- for (NSUInteger i = 0; i < count; ++i) {
- NSString *selectorComponent = components[i];
- NSString *argumentFilterString = [KWFormatter formatObject:(self.argumentFilters)[i]];
- [description appendFormat:@"%@:%@ ", selectorComponent, argumentFilterString];
- }
- return description;
- }
- - (NSString *)stringValue {
- if (self.argumentFilters == nil)
- return [self selectorString];
- else
- return [self selectorAndArgumentFiltersString];
- }
- #pragma mark - Debugging
- - (NSString *)description {
- return [NSString stringWithFormat:@"selector: %@\nargumentFilters: %@",
- NSStringFromSelector(self.selector),
- self.argumentFilters];
- }
- @end
|