PageRenderTime 79ms CodeModel.GetById 6ms app.highlight 70ms RepoModel.GetById 1ms app.codeStats 0ms

/core/externals/google-toolbox-for-mac/Foundation/GTMValidatingContainers.m

http://macfuse.googlecode.com/
Objective C | 491 lines | 401 code | 61 blank | 29 comment | 28 complexity | 65d0a880f38cedd572302e3e6d371346 MD5 | raw file
  1//
  2//  GTMValidatingContainers.m
  3//
  4//  Mutable containers that do verification of objects being added to them
  5//  at runtime. Support for arrays, dictionaries and sets.
  6//
  7//  Documentation on subclassing class clusters (which we are doing) is here:
  8//  http://developer.apple.com/documentation/Cocoa/Conceptual/CocoaFundamentals/CocoaObjects/chapter_3_section_9.html#//apple_ref/doc/uid/TP40002974-CH4-DontLinkElementID_105
  9//
 10//  Copyright 2008 Google Inc.
 11//
 12//  Licensed under the Apache License, Version 2.0 (the "License"); you may not
 13//  use this file except in compliance with the License.  You may obtain a copy
 14//  of the License at
 15//
 16//  http://www.apache.org/licenses/LICENSE-2.0
 17//
 18//  Unless required by applicable law or agreed to in writing, software
 19//  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 20//  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 21//  License for the specific language governing permissions and limitations under
 22//  the License.
 23//
 24
 25#import "GTMValidatingContainers.h"
 26
 27#if GTM_CONTAINERS_VALIDATE
 28
 29#import "GTMDebugSelectorValidation.h"
 30#if GTM_IPHONE_SDK
 31#import <objc/message.h>
 32#import <objc/runtime.h>
 33#else  // GTM_IPHONE_SDK
 34#import <objc/objc-runtime.h>
 35#endif  // GTM_IPHONE_SDK
 36
 37GTM_INLINE BOOL VerifyObjectWithTargetAndSelectorForContainer(id anObject,
 38                                                              id target,
 39                                                              SEL selector,
 40                                                              id container) {
 41  // We must take care here, since Intel leaves junk in high bytes of return
 42  // register for predicates that return BOOL.
 43  // For details see:
 44  // http://developer.apple.com/documentation/MacOSX/Conceptual/universal_binary/universal_binary_tips/chapter_5_section_23.html
 45  // and
 46  // http://www.red-sweater.com/blog/320/abusing-objective-c-with-class#comment-83187
 47  BOOL isGood = ((BOOL (*)(id, SEL, id, id))objc_msgSend)(target, selector,
 48                                                          anObject, container);
 49  if (!isGood) {
 50#if GTM_CONTAINERS_VALIDATION_FAILED_ASSERT
 51    _GTMDevAssert(isGood, @"%@ failed container verification for %@",
 52                  anObject, [container description]);
 53#endif  // GTM_CONTAINERS_VALIDATION_FAILED_LOG
 54#if GTM_CONTAINERS_VALIDATION_FAILED_LOG
 55    _GTMDevLog(@"%@ failed container verification for %@", anObject,
 56               [container description]);
 57#endif  // GTM_CONTAINERS_VALIDATION_FAILED_LOG
 58  }
 59  return isGood;
 60}
 61
 62GTM_INLINE void VerifySelectorOnTarget(SEL sel, id target) {
 63  GTMAssertSelectorNilOrImplementedWithReturnTypeAndArguments(target,
 64                                                              sel,
 65                                                              @encode(BOOL),
 66                                                              @encode(id),
 67                                                              @encode(id),
 68                                                              nil);
 69}
 70
 71void _GTMValidateContainerContainsKindOfClass(id container, Class cls) {
 72  GTMKindOfClassValidator *validator;
 73  validator = [GTMKindOfClassValidator validateAgainstClass:cls];
 74  _GTMValidateContainer(container,
 75                        validator,
 76                        @selector(validateObject:forContainer:));
 77}
 78
 79void _GTMValidateContainerContainsMemberOfClass(id container, Class cls) {
 80  GTMMemberOfClassValidator *validator;
 81  validator = [GTMMemberOfClassValidator validateAgainstClass:cls];
 82  _GTMValidateContainer(container,
 83                        validator,
 84                        @selector(validateObject:forContainer:));
 85}
 86
 87void _GTMValidateContainerConformsToProtocol(id container, Protocol* prot) {
 88  GTMConformsToProtocolValidator *validator;
 89  validator = [GTMConformsToProtocolValidator validateAgainstProtocol:prot];
 90  _GTMValidateContainer(container,
 91                        validator,
 92                        @selector(validateObject:forContainer:));
 93}
 94
 95void _GTMValidateContainerItemsRespondToSelector(id container, SEL sel) {
 96  GTMRespondsToSelectorValidator *validator;
 97  validator = [GTMRespondsToSelectorValidator validateAgainstSelector:sel];
 98  _GTMValidateContainer(container,
 99                        validator,
100                        @selector(validateObject:forContainer:));
101}
102
103void _GTMValidateContainer(id container, id target, SEL selector) {
104  if ([container respondsToSelector:@selector(objectEnumerator)]) {
105    NSEnumerator *enumerator = [container objectEnumerator];
106    id val;
107    while ((val = [enumerator nextObject])) {
108      VerifyObjectWithTargetAndSelectorForContainer(val,
109                                                    target,
110                                                    selector,
111                                                    container);
112    }
113  } else {
114#if GTM_CONTAINERS_VALIDATION_FAILED_ASSERT
115    _GTMDevAssert(0, @"container %@ does not respond to -objectEnumerator",
116                  [container description]);
117#endif  // GTM_CONTAINERS_VALIDATION_FAILED_LOG
118#if GTM_CONTAINERS_VALIDATION_FAILED_LOG
119  _GTMDevLog(@"container does not respont to -objectEnumerator: %@",
120             [container description]);
121#endif  // GTM_CONTAINERS_VALIDATION_FAILED_LOG
122  }
123}
124#endif  // GTM_CONTAINERS_VALIDATE
125
126@implementation GTMValidatingArray
127
128+ (id)validatingArrayWithTarget:(id)target selector:(SEL)sel {
129  return [self validatingArrayWithCapacity:0 target:target selector:sel];
130}
131
132+ (id)validatingArrayWithCapacity:(NSUInteger)capacity
133                           target:(id)target
134                         selector:(SEL)sel {
135  return [[[self alloc] initValidatingWithCapacity:0
136                                            target:target
137                                          selector:sel] autorelease];
138}
139
140- (id)initValidatingWithTarget:(id)target selector:(SEL)sel {
141  return [self initValidatingWithCapacity:0 target:target selector:sel];
142}
143
144#if GTM_CONTAINERS_VALIDATE
145- (id)initValidatingWithCapacity:(NSUInteger)capacity
146                          target:(id)target
147                        selector:(SEL)sel {
148  if ((self = [super init])) {
149    embeddedContainer_ = [[NSMutableArray alloc] initWithCapacity:capacity];
150    target_ = [target retain];
151    selector_ = sel;
152    VerifySelectorOnTarget(selector_, target_);
153  }
154  return self;
155}
156
157- (void)dealloc {
158  [embeddedContainer_ release];
159  [target_ release];
160  [super dealloc];
161}
162
163- (NSUInteger)count {
164  return [embeddedContainer_ count];
165}
166
167- (id)objectAtIndex:(NSUInteger)idx {
168  return [embeddedContainer_ objectAtIndex:idx];
169}
170
171- (void)addObject:(id)anObject {
172  if (VerifyObjectWithTargetAndSelectorForContainer(anObject, target_,
173                                                    selector_, self)) {
174    [embeddedContainer_ addObject:anObject];
175  }
176}
177
178- (void)insertObject:(id)anObject atIndex:(NSUInteger)idx {
179  if (VerifyObjectWithTargetAndSelectorForContainer(anObject, target_,
180                                                    selector_, self)) {
181    [embeddedContainer_ insertObject:anObject atIndex:idx];
182  }
183}
184
185- (void)removeLastObject {
186  [embeddedContainer_ removeLastObject];
187}
188
189- (void)removeObjectAtIndex:(NSUInteger)idx {
190  [embeddedContainer_ removeObjectAtIndex:idx];
191}
192
193- (void)replaceObjectAtIndex:(NSUInteger)idx withObject:(id)anObject {
194    if (VerifyObjectWithTargetAndSelectorForContainer(anObject, target_,
195                                                      selector_, self)) {
196    [embeddedContainer_ replaceObjectAtIndex:idx withObject:anObject];
197  }
198}
199
200- (NSString*)description {
201  return [NSString stringWithFormat:@"%@ - %@",
202          NSStringFromClass([self class]),
203          [embeddedContainer_ description]];
204}
205
206#else  // GTM_CONTAINERS_VALIDATE
207- (id)initValidatingWithCapacity:(NSUInteger)capacity
208                          target:(id)target
209                        selector:(SEL)sel {
210  if ((self = [super init])) {
211    [self release];
212  }
213  return (GTMValidatingArray*)[[NSMutableArray alloc] initWithCapacity:capacity];
214}
215#endif  // GTM_CONTAINERS_VALIDATE
216@end
217
218@implementation GTMValidatingDictionary
219+ (id)validatingDictionaryWithTarget:(id)target selector:(SEL)sel {
220  return [self validatingDictionaryWithCapacity:0 target:target selector:sel];
221}
222
223+ (id)validatingDictionaryWithCapacity:(NSUInteger)capacity
224                                target:(id)target
225                              selector:(SEL)sel {
226  return [[[self alloc] initValidatingWithCapacity:0
227                                            target:target
228                                          selector:sel] autorelease];
229}
230
231- (id)initValidatingWithTarget:(id)target selector:(SEL)sel {
232  return [self initValidatingWithCapacity:0 target:target selector:sel];
233}
234
235#if GTM_CONTAINERS_VALIDATE
236- (id)initValidatingWithCapacity:(NSUInteger)capacity
237                          target:(id)target
238                        selector:(SEL)sel {
239  if ((self = [super init])) {
240    embeddedContainer_ = [[NSMutableDictionary alloc] initWithCapacity:capacity];
241    target_ = [target retain];
242    selector_ = sel;
243    VerifySelectorOnTarget(selector_, target_);
244  }
245  return self;
246}
247
248- (void)dealloc {
249  [target_ release];
250  [embeddedContainer_ release];
251  [super dealloc];
252}
253
254- (NSUInteger)count {
255  return [embeddedContainer_ count];
256}
257
258- (NSEnumerator *)keyEnumerator {
259  return [embeddedContainer_ keyEnumerator];
260}
261
262- (id)objectForKey:(id)aKey {
263  return [embeddedContainer_ objectForKey:aKey];
264}
265
266- (void)removeObjectForKey:(id)aKey {
267  [embeddedContainer_ removeObjectForKey:aKey];
268}
269
270- (void)setObject:(id)anObject forKey:(id)aKey {
271  if (VerifyObjectWithTargetAndSelectorForContainer(anObject, target_,
272                                                    selector_, self)) {
273    [embeddedContainer_ setObject:anObject forKey:aKey];
274  }
275}
276
277- (NSString*)description {
278  return [NSString stringWithFormat:@"%@ - %@",
279          NSStringFromClass([self class]),
280          [embeddedContainer_ description]];
281}
282
283#else  // GTM_CONTAINERS_VALIDATE
284- (id)initValidatingWithCapacity:(NSUInteger)capacity
285                          target:(id)target
286                        selector:(SEL)sel {
287  if ((self = [super init])) {
288    [self release];
289  }
290  return (GTMValidatingDictionary*)[[NSMutableDictionary alloc]
291                                    initWithCapacity:capacity];
292
293}
294#endif  // GTM_CONTAINERS_VALIDATE
295@end
296
297@implementation GTMValidatingSet
298+ (id)validatingSetWithTarget:(id)target selector:(SEL)sel {
299  return [self validatingSetWithCapacity:0 target:target selector:sel];
300}
301
302+ (id)validatingSetWithCapacity:(NSUInteger)capacity
303                         target:(id)target
304                       selector:(SEL)sel {
305  return [[[self alloc] initValidatingWithCapacity:0
306                                            target:target
307                                          selector:sel] autorelease];
308}
309- (id)initValidatingWithTarget:(id)target selector:(SEL)sel {
310  return [self initValidatingWithCapacity:0 target:target selector:sel];
311}
312
313#if GTM_CONTAINERS_VALIDATE
314- (id)initValidatingWithCapacity:(NSUInteger)capacity
315                          target:(id)target
316                        selector:(SEL)sel {
317  if ((self = [super init])) {
318    embeddedContainer_ = [[NSMutableSet alloc] initWithCapacity:capacity];
319    target_ = [target retain];
320    selector_ = sel;
321    VerifySelectorOnTarget(selector_, target_);
322  }
323  return self;
324}
325
326- (void)dealloc {
327  [target_ release];
328  [embeddedContainer_ release];
329  [super dealloc];
330}
331
332- (NSUInteger)count {
333  return [embeddedContainer_ count];
334}
335
336- (id)member:(id)object {
337  return [embeddedContainer_ member:object];
338}
339
340- (NSEnumerator *)objectEnumerator {
341  return [embeddedContainer_ objectEnumerator];
342}
343
344- (void)addObject:(id)object {
345  if (object && VerifyObjectWithTargetAndSelectorForContainer(object,
346                                                              target_,
347                                                              selector_,
348                                                              self)) {
349    [embeddedContainer_ addObject:object];
350  }
351}
352
353- (void)removeObject:(id)object {
354  [embeddedContainer_ removeObject:object];
355}
356
357- (NSString*)description {
358  return [NSString stringWithFormat:@"%@ - %@",
359          NSStringFromClass([self class]),
360          [embeddedContainer_ description]];
361}
362
363#else  // GTM_CONTAINERS_VALIDATE
364- (id)initValidatingWithCapacity:(NSUInteger)capacity
365                          target:(id)target
366                        selector:(SEL)sel {
367  if ((self = [super init])) {
368    [self release];
369  }
370  return (GTMValidatingSet*)[[NSMutableSet alloc] initWithCapacity:capacity];
371}
372#endif  // GTM_CONTAINERS_VALIDATE
373@end
374
375#pragma mark -
376#pragma mark Simple Common Validators
377@implementation GTMKindOfClassValidator
378+ (id)validateAgainstClass:(Class)cls {
379  return [[[self alloc] initWithClass:cls] autorelease];
380}
381
382- (id)initWithClass:(Class)cls {
383#if GTM_CONTAINERS_VALIDATE
384  if ((self = [super init])) {
385    if (!cls) {
386      _GTMDevLog(@"nil class");
387      [self release];
388      return nil;
389    }
390    cls_ = cls;
391  }
392  return self;
393#else  // GTM_CONTAINERS_VALIDATE
394  if ((self = [super init])) {
395    [self release];
396  }
397  return nil;
398#endif  // GTM_CONTAINERS_VALIDATE
399}
400
401- (BOOL)validateObject:(id)object forContainer:(id)container {
402  return [object isKindOfClass:cls_];
403}
404@end
405
406@implementation GTMMemberOfClassValidator
407+ (id)validateAgainstClass:(Class)cls {
408  return [[[self alloc] initWithClass:cls] autorelease];
409}
410
411- (id)initWithClass:(Class)cls {
412#if GTM_CONTAINERS_VALIDATE
413  if ((self = [super init])) {
414    if (!cls) {
415      _GTMDevLog(@"nil class");
416      [self release];
417      return nil;
418    }
419    cls_ = cls;
420  }
421  return self;
422#else  // GTM_CONTAINERS_VALIDATE
423  if ((self = [super init])) {
424    [self release];
425  }
426  return nil;
427#endif  // GTM_CONTAINERS_VALIDATE
428}
429
430- (BOOL)validateObject:(id)object forContainer:(id)container {
431  return [object isMemberOfClass:cls_];
432}
433@end
434
435@implementation GTMConformsToProtocolValidator
436+ (id)validateAgainstProtocol:(Protocol*)prot {
437  return [[[self alloc] initWithProtocol:prot] autorelease];
438}
439
440- (id)initWithProtocol:(Protocol*)prot {
441#if GTM_CONTAINERS_VALIDATE
442  if ((self = [super init])) {
443    if (!prot) {
444      _GTMDevLog(@"nil protocol");
445      [self release];
446      return nil;
447    }
448    prot_ = prot;
449  }
450  return self;
451#else  // GTM_CONTAINERS_VALIDATE
452  if ((self = [super init])) {
453    [self release];
454  }
455  return nil;
456#endif  // GTM_CONTAINERS_VALIDATE
457}
458
459- (BOOL)validateObject:(id)object forContainer:(id)container {
460  return [object conformsToProtocol:prot_];
461}
462@end
463
464@implementation GTMRespondsToSelectorValidator
465+ (id)validateAgainstSelector:(SEL)sel {
466  return [[[self alloc] initWithSelector:sel] autorelease];
467}
468
469- (id)initWithSelector:(SEL)sel {
470#if GTM_CONTAINERS_VALIDATE
471  if ((self = [super init])) {
472    if (!sel) {
473      _GTMDevLog(@"nil selector");
474      [self release];
475      return nil;
476    }
477    sel_ = sel;
478  }
479  return self;
480#else  // GTM_CONTAINERS_VALIDATE
481  if ((self = [super init])) {
482    [self release];
483  }
484  return nil;
485#endif  // GTM_CONTAINERS_VALIDATE
486}
487
488- (BOOL)validateObject:(id)object forContainer:(id)container {
489  return [object respondsToSelector:sel_];
490}
491@end