main
1//
2// Licensed under the terms in License.txt
3//
4// Copyright 2010 Allen Ding. All rights reserved.
5//
6
7#import "KWMessagePattern.h"
8#import "KWFormatter.h"
9#import "KWNull.h"
10#import "KWObjCUtilities.h"
11#import "KWValue.h"
12#import "NSInvocation+KiwiAdditions.h"
13#import "NSMethodSignature+KiwiAdditions.h"
14#import "KWGenericMatchEvaluator.h"
15#import "Kiwi.h"
16
17@implementation KWMessagePattern
18
19#pragma mark -
20#pragma mark Initializing
21
22- (id)initWithSelector:(SEL)aSelector {
23 return [self initWithSelector:aSelector argumentFilters:nil];
24}
25
26- (id)initWithSelector:(SEL)aSelector argumentFilters:(NSArray *)anArray {
27 if ((self = [super init])) {
28 selector = aSelector;
29
30 if ([anArray count] > 0)
31 argumentFilters = [anArray copy];
32 }
33
34 return self;
35}
36
37- (id)initWithSelector:(SEL)aSelector firstArgumentFilter:(id)firstArgumentFilter argumentList:(va_list)argumentList {
38 NSUInteger count = KWSelectorParameterCount(aSelector);
39 NSMutableArray *array = [NSMutableArray arrayWithCapacity:count];
40 [array addObject:(firstArgumentFilter != nil) ? firstArgumentFilter : [KWNull null]];
41
42 for (NSUInteger i = 1; i < count; ++i)
43 {
44 id object = va_arg(argumentList, id);
45 [array addObject:(object != nil) ? object : [KWNull null]];
46 }
47
48 va_end(argumentList);
49 return [self initWithSelector:aSelector argumentFilters:array];
50}
51
52+ (id)messagePatternWithSelector:(SEL)aSelector {
53 return [self messagePatternWithSelector:aSelector argumentFilters:nil];
54}
55
56+ (id)messagePatternWithSelector:(SEL)aSelector argumentFilters:(NSArray *)anArray {
57 return [[[self alloc] initWithSelector:aSelector argumentFilters:anArray] autorelease];
58}
59
60+ (id)messagePatternWithSelector:(SEL)aSelector firstArgumentFilter:(id)firstArgumentFilter argumentList:(va_list)argumentList {
61 return [[[self alloc] initWithSelector:aSelector firstArgumentFilter:firstArgumentFilter argumentList:argumentList] autorelease];
62}
63
64+ (id)messagePatternFromInvocation:(NSInvocation *)anInvocation {
65 NSMethodSignature *signature = [anInvocation methodSignature];
66 NSUInteger numberOfMessageArguments = [signature numberOfMessageArguments];
67 NSMutableArray *argumentFilters = nil;
68
69 if (numberOfMessageArguments > 0) {
70 argumentFilters = [[NSMutableArray alloc] initWithCapacity:numberOfMessageArguments];
71
72 for (NSUInteger i = 0; i < numberOfMessageArguments; ++i) {
73 const char *type = [signature messageArgumentTypeAtIndex:i];
74 id object = nil;
75
76 if (KWObjCTypeIsObject(type)) {
77 [anInvocation getMessageArgument:&object atIndex:i];
78 } else {
79 NSData *data = [anInvocation messageArgumentDataAtIndex:i];
80 object = [KWValue valueWithBytes:[data bytes] objCType:type];
81 }
82
83
84 if (strcmp(type, "@?") == 0) object = [[object copy] autorelease]; // Converting NSStackBlock to NSMallocBlock
85 [argumentFilters addObject:(object != nil) ? object : [KWNull null]];
86 }
87 }
88
89 return [self messagePatternWithSelector:[anInvocation selector] argumentFilters:[argumentFilters autorelease]];
90}
91
92- (void)dealloc {
93 [argumentFilters release];
94 [super dealloc];
95}
96
97#pragma mark -
98#pragma mark Copying
99
100- (id)copyWithZone:(NSZone *)zone {
101 return [self retain];
102}
103
104#pragma mark -
105#pragma mark Properties
106
107@synthesize selector;
108@synthesize argumentFilters;
109
110#pragma mark -
111#pragma mark Matching Invocations
112
113- (BOOL)argumentFiltersMatchInvocationArguments:(NSInvocation *)anInvocation {
114 if (self.argumentFilters == nil)
115 return YES;
116
117 NSMethodSignature *signature = [anInvocation methodSignature];
118 NSUInteger numberOfArgumentFilters = [self.argumentFilters count];
119 NSUInteger numberOfMessageArguments = [signature numberOfMessageArguments];
120
121 for (NSUInteger i = 0; i < numberOfMessageArguments && i < numberOfArgumentFilters; ++i) {
122 const char *objCType = [signature messageArgumentTypeAtIndex:i];
123 id object = nil;
124
125 // Extract message argument into object (wrapping values if neccesary)
126 if (KWObjCTypeIsObject(objCType)) {
127 [anInvocation getMessageArgument:&object atIndex:i];
128 } else {
129 NSData *data = [anInvocation messageArgumentDataAtIndex:i];
130 object = [KWValue valueWithBytes:[data bytes] objCType:objCType];
131 }
132
133 // Match argument filter to object
134 id argumentFilter = (self.argumentFilters)[i];
135
136 if ([argumentFilter isEqual:[KWAny any]]) {
137 continue;
138 }
139
140 if ([KWGenericMatchEvaluator isGenericMatcher:argumentFilter]) {
141 id matcher = argumentFilter;
142 if ([object isKindOfClass:[KWValue class]] && [object isNumeric]) {
143 NSNumber *number = [object numberValue];
144 if (![KWGenericMatchEvaluator genericMatcher:matcher matches:number]) {
145 return NO;
146 }
147 } else if (![KWGenericMatchEvaluator genericMatcher:matcher matches:object]) {
148 return NO;
149 }
150 } else if ([argumentFilter isEqual:[KWNull null]]) {
151 if (!KWObjCTypeIsPointerLike(objCType)) {
152 [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])];
153 }
154 void *p = nil;
155 [anInvocation getMessageArgument:&p atIndex:i];
156 if (p != nil)
157 return NO;
158 } else if (![argumentFilter isEqual:object]) {
159 return NO;
160 }
161 }
162
163 return YES;
164}
165
166- (BOOL)matchesInvocation:(NSInvocation *)anInvocation {
167 return self.selector == [anInvocation selector] && [self argumentFiltersMatchInvocationArguments:anInvocation];
168}
169
170#pragma mark -
171#pragma mark Comparing Message Patterns
172
173- (NSUInteger)hash {
174 return [NSStringFromSelector(self.selector) hash];
175}
176
177- (BOOL)isEqual:(id)object {
178 if (![object isKindOfClass:[KWMessagePattern class]])
179 return NO;
180
181 return [self isEqualToMessagePattern:object];
182}
183
184- (BOOL)isEqualToMessagePattern:(KWMessagePattern *)aMessagePattern {
185 if (self.selector != aMessagePattern.selector)
186 return NO;
187
188 if (self.argumentFilters == nil && aMessagePattern.argumentFilters == nil)
189 return YES;
190
191 return [self.argumentFilters isEqualToArray:aMessagePattern.argumentFilters];
192}
193
194#pragma mark -
195#pragma mark Retrieving String Representations
196
197- (NSString *)selectorString {
198 return NSStringFromSelector(self.selector);
199}
200
201- (NSString *)selectorAndArgumentFiltersString {
202 NSMutableString *description = [[[NSMutableString alloc] init] autorelease];
203 NSArray *components = [NSStringFromSelector(self.selector) componentsSeparatedByString:@":"];
204 NSUInteger count = [components count] - 1;
205
206 for (NSUInteger i = 0; i < count; ++i) {
207 NSString *selectorComponent = components[i];
208 NSString *argumentFilterString = [KWFormatter formatObject:(self.argumentFilters)[i]];
209 [description appendFormat:@"%@:%@ ", selectorComponent, argumentFilterString];
210 }
211
212 return description;
213}
214
215- (NSString *)stringValue {
216 if (self.argumentFilters == nil)
217 return [self selectorString];
218 else
219 return [self selectorAndArgumentFiltersString];
220}
221
222#pragma mark -
223#pragma mark Debugging
224
225- (NSString *)description {
226 return [NSString stringWithFormat:@"selector: %@\nargumentFilters: %@",
227 NSStringFromSelector(self.selector),
228 self.argumentFilters];
229}
230
231@end