PageRenderTime 52ms CodeModel.GetById 12ms RepoModel.GetById 0ms 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
Possible License(s): Apache-2.0, BSD-3-Clause, GPL-2.0
  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. #import "GTMValidatingContainers.h"
  25. #if GTM_CONTAINERS_VALIDATE
  26. #import "GTMDebugSelectorValidation.h"
  27. #if GTM_IPHONE_SDK
  28. #import <objc/message.h>
  29. #import <objc/runtime.h>
  30. #else // GTM_IPHONE_SDK
  31. #import <objc/objc-runtime.h>
  32. #endif // GTM_IPHONE_SDK
  33. GTM_INLINE BOOL VerifyObjectWithTargetAndSelectorForContainer(id anObject,
  34. id target,
  35. SEL selector,
  36. id container) {
  37. // We must take care here, since Intel leaves junk in high bytes of return
  38. // register for predicates that return BOOL.
  39. // For details see:
  40. // http://developer.apple.com/documentation/MacOSX/Conceptual/universal_binary/universal_binary_tips/chapter_5_section_23.html
  41. // and
  42. // http://www.red-sweater.com/blog/320/abusing-objective-c-with-class#comment-83187
  43. BOOL isGood = ((BOOL (*)(id, SEL, id, id))objc_msgSend)(target, selector,
  44. anObject, container);
  45. if (!isGood) {
  46. #if GTM_CONTAINERS_VALIDATION_FAILED_ASSERT
  47. _GTMDevAssert(isGood, @"%@ failed container verification for %@",
  48. anObject, [container description]);
  49. #endif // GTM_CONTAINERS_VALIDATION_FAILED_LOG
  50. #if GTM_CONTAINERS_VALIDATION_FAILED_LOG
  51. _GTMDevLog(@"%@ failed container verification for %@", anObject,
  52. [container description]);
  53. #endif // GTM_CONTAINERS_VALIDATION_FAILED_LOG
  54. }
  55. return isGood;
  56. }
  57. GTM_INLINE void VerifySelectorOnTarget(SEL sel, id target) {
  58. GTMAssertSelectorNilOrImplementedWithReturnTypeAndArguments(target,
  59. sel,
  60. @encode(BOOL),
  61. @encode(id),
  62. @encode(id),
  63. nil);
  64. }
  65. void _GTMValidateContainerContainsKindOfClass(id container, Class cls) {
  66. GTMKindOfClassValidator *validator;
  67. validator = [GTMKindOfClassValidator validateAgainstClass:cls];
  68. _GTMValidateContainer(container,
  69. validator,
  70. @selector(validateObject:forContainer:));
  71. }
  72. void _GTMValidateContainerContainsMemberOfClass(id container, Class cls) {
  73. GTMMemberOfClassValidator *validator;
  74. validator = [GTMMemberOfClassValidator validateAgainstClass:cls];
  75. _GTMValidateContainer(container,
  76. validator,
  77. @selector(validateObject:forContainer:));
  78. }
  79. void _GTMValidateContainerConformsToProtocol(id container, Protocol* prot) {
  80. GTMConformsToProtocolValidator *validator;
  81. validator = [GTMConformsToProtocolValidator validateAgainstProtocol:prot];
  82. _GTMValidateContainer(container,
  83. validator,
  84. @selector(validateObject:forContainer:));
  85. }
  86. void _GTMValidateContainerItemsRespondToSelector(id container, SEL sel) {
  87. GTMRespondsToSelectorValidator *validator;
  88. validator = [GTMRespondsToSelectorValidator validateAgainstSelector:sel];
  89. _GTMValidateContainer(container,
  90. validator,
  91. @selector(validateObject:forContainer:));
  92. }
  93. void _GTMValidateContainer(id container, id target, SEL selector) {
  94. if ([container respondsToSelector:@selector(objectEnumerator)]) {
  95. NSEnumerator *enumerator = [container objectEnumerator];
  96. id val;
  97. while ((val = [enumerator nextObject])) {
  98. VerifyObjectWithTargetAndSelectorForContainer(val,
  99. target,
  100. selector,
  101. container);
  102. }
  103. } else {
  104. #if GTM_CONTAINERS_VALIDATION_FAILED_ASSERT
  105. _GTMDevAssert(0, @"container %@ does not respond to -objectEnumerator",
  106. [container description]);
  107. #endif // GTM_CONTAINERS_VALIDATION_FAILED_LOG
  108. #if GTM_CONTAINERS_VALIDATION_FAILED_LOG
  109. _GTMDevLog(@"container does not respont to -objectEnumerator: %@",
  110. [container description]);
  111. #endif // GTM_CONTAINERS_VALIDATION_FAILED_LOG
  112. }
  113. }
  114. #endif // GTM_CONTAINERS_VALIDATE
  115. @implementation GTMValidatingArray
  116. + (id)validatingArrayWithTarget:(id)target selector:(SEL)sel {
  117. return [self validatingArrayWithCapacity:0 target:target selector:sel];
  118. }
  119. + (id)validatingArrayWithCapacity:(NSUInteger)capacity
  120. target:(id)target
  121. selector:(SEL)sel {
  122. return [[[self alloc] initValidatingWithCapacity:0
  123. target:target
  124. selector:sel] autorelease];
  125. }
  126. - (id)initValidatingWithTarget:(id)target selector:(SEL)sel {
  127. return [self initValidatingWithCapacity:0 target:target selector:sel];
  128. }
  129. #if GTM_CONTAINERS_VALIDATE
  130. - (id)initValidatingWithCapacity:(NSUInteger)capacity
  131. target:(id)target
  132. selector:(SEL)sel {
  133. if ((self = [super init])) {
  134. embeddedContainer_ = [[NSMutableArray alloc] initWithCapacity:capacity];
  135. target_ = [target retain];
  136. selector_ = sel;
  137. VerifySelectorOnTarget(selector_, target_);
  138. }
  139. return self;
  140. }
  141. - (void)dealloc {
  142. [embeddedContainer_ release];
  143. [target_ release];
  144. [super dealloc];
  145. }
  146. - (NSUInteger)count {
  147. return [embeddedContainer_ count];
  148. }
  149. - (id)objectAtIndex:(NSUInteger)idx {
  150. return [embeddedContainer_ objectAtIndex:idx];
  151. }
  152. - (void)addObject:(id)anObject {
  153. if (VerifyObjectWithTargetAndSelectorForContainer(anObject, target_,
  154. selector_, self)) {
  155. [embeddedContainer_ addObject:anObject];
  156. }
  157. }
  158. - (void)insertObject:(id)anObject atIndex:(NSUInteger)idx {
  159. if (VerifyObjectWithTargetAndSelectorForContainer(anObject, target_,
  160. selector_, self)) {
  161. [embeddedContainer_ insertObject:anObject atIndex:idx];
  162. }
  163. }
  164. - (void)removeLastObject {
  165. [embeddedContainer_ removeLastObject];
  166. }
  167. - (void)removeObjectAtIndex:(NSUInteger)idx {
  168. [embeddedContainer_ removeObjectAtIndex:idx];
  169. }
  170. - (void)replaceObjectAtIndex:(NSUInteger)idx withObject:(id)anObject {
  171. if (VerifyObjectWithTargetAndSelectorForContainer(anObject, target_,
  172. selector_, self)) {
  173. [embeddedContainer_ replaceObjectAtIndex:idx withObject:anObject];
  174. }
  175. }
  176. - (NSString*)description {
  177. return [NSString stringWithFormat:@"%@ - %@",
  178. NSStringFromClass([self class]),
  179. [embeddedContainer_ description]];
  180. }
  181. #else // GTM_CONTAINERS_VALIDATE
  182. - (id)initValidatingWithCapacity:(NSUInteger)capacity
  183. target:(id)target
  184. selector:(SEL)sel {
  185. if ((self = [super init])) {
  186. [self release];
  187. }
  188. return (GTMValidatingArray*)[[NSMutableArray alloc] initWithCapacity:capacity];
  189. }
  190. #endif // GTM_CONTAINERS_VALIDATE
  191. @end
  192. @implementation GTMValidatingDictionary
  193. + (id)validatingDictionaryWithTarget:(id)target selector:(SEL)sel {
  194. return [self validatingDictionaryWithCapacity:0 target:target selector:sel];
  195. }
  196. + (id)validatingDictionaryWithCapacity:(NSUInteger)capacity
  197. target:(id)target
  198. selector:(SEL)sel {
  199. return [[[self alloc] initValidatingWithCapacity:0
  200. target:target
  201. selector:sel] autorelease];
  202. }
  203. - (id)initValidatingWithTarget:(id)target selector:(SEL)sel {
  204. return [self initValidatingWithCapacity:0 target:target selector:sel];
  205. }
  206. #if GTM_CONTAINERS_VALIDATE
  207. - (id)initValidatingWithCapacity:(NSUInteger)capacity
  208. target:(id)target
  209. selector:(SEL)sel {
  210. if ((self = [super init])) {
  211. embeddedContainer_ = [[NSMutableDictionary alloc] initWithCapacity:capacity];
  212. target_ = [target retain];
  213. selector_ = sel;
  214. VerifySelectorOnTarget(selector_, target_);
  215. }
  216. return self;
  217. }
  218. - (void)dealloc {
  219. [target_ release];
  220. [embeddedContainer_ release];
  221. [super dealloc];
  222. }
  223. - (NSUInteger)count {
  224. return [embeddedContainer_ count];
  225. }
  226. - (NSEnumerator *)keyEnumerator {
  227. return [embeddedContainer_ keyEnumerator];
  228. }
  229. - (id)objectForKey:(id)aKey {
  230. return [embeddedContainer_ objectForKey:aKey];
  231. }
  232. - (void)removeObjectForKey:(id)aKey {
  233. [embeddedContainer_ removeObjectForKey:aKey];
  234. }
  235. - (void)setObject:(id)anObject forKey:(id)aKey {
  236. if (VerifyObjectWithTargetAndSelectorForContainer(anObject, target_,
  237. selector_, self)) {
  238. [embeddedContainer_ setObject:anObject forKey:aKey];
  239. }
  240. }
  241. - (NSString*)description {
  242. return [NSString stringWithFormat:@"%@ - %@",
  243. NSStringFromClass([self class]),
  244. [embeddedContainer_ description]];
  245. }
  246. #else // GTM_CONTAINERS_VALIDATE
  247. - (id)initValidatingWithCapacity:(NSUInteger)capacity
  248. target:(id)target
  249. selector:(SEL)sel {
  250. if ((self = [super init])) {
  251. [self release];
  252. }
  253. return (GTMValidatingDictionary*)[[NSMutableDictionary alloc]
  254. initWithCapacity:capacity];
  255. }
  256. #endif // GTM_CONTAINERS_VALIDATE
  257. @end
  258. @implementation GTMValidatingSet
  259. + (id)validatingSetWithTarget:(id)target selector:(SEL)sel {
  260. return [self validatingSetWithCapacity:0 target:target selector:sel];
  261. }
  262. + (id)validatingSetWithCapacity:(NSUInteger)capacity
  263. target:(id)target
  264. selector:(SEL)sel {
  265. return [[[self alloc] initValidatingWithCapacity:0
  266. target:target
  267. selector:sel] autorelease];
  268. }
  269. - (id)initValidatingWithTarget:(id)target selector:(SEL)sel {
  270. return [self initValidatingWithCapacity:0 target:target selector:sel];
  271. }
  272. #if GTM_CONTAINERS_VALIDATE
  273. - (id)initValidatingWithCapacity:(NSUInteger)capacity
  274. target:(id)target
  275. selector:(SEL)sel {
  276. if ((self = [super init])) {
  277. embeddedContainer_ = [[NSMutableSet alloc] initWithCapacity:capacity];
  278. target_ = [target retain];
  279. selector_ = sel;
  280. VerifySelectorOnTarget(selector_, target_);
  281. }
  282. return self;
  283. }
  284. - (void)dealloc {
  285. [target_ release];
  286. [embeddedContainer_ release];
  287. [super dealloc];
  288. }
  289. - (NSUInteger)count {
  290. return [embeddedContainer_ count];
  291. }
  292. - (id)member:(id)object {
  293. return [embeddedContainer_ member:object];
  294. }
  295. - (NSEnumerator *)objectEnumerator {
  296. return [embeddedContainer_ objectEnumerator];
  297. }
  298. - (void)addObject:(id)object {
  299. if (object && VerifyObjectWithTargetAndSelectorForContainer(object,
  300. target_,
  301. selector_,
  302. self)) {
  303. [embeddedContainer_ addObject:object];
  304. }
  305. }
  306. - (void)removeObject:(id)object {
  307. [embeddedContainer_ removeObject:object];
  308. }
  309. - (NSString*)description {
  310. return [NSString stringWithFormat:@"%@ - %@",
  311. NSStringFromClass([self class]),
  312. [embeddedContainer_ description]];
  313. }
  314. #else // GTM_CONTAINERS_VALIDATE
  315. - (id)initValidatingWithCapacity:(NSUInteger)capacity
  316. target:(id)target
  317. selector:(SEL)sel {
  318. if ((self = [super init])) {
  319. [self release];
  320. }
  321. return (GTMValidatingSet*)[[NSMutableSet alloc] initWithCapacity:capacity];
  322. }
  323. #endif // GTM_CONTAINERS_VALIDATE
  324. @end
  325. #pragma mark -
  326. #pragma mark Simple Common Validators
  327. @implementation GTMKindOfClassValidator
  328. + (id)validateAgainstClass:(Class)cls {
  329. return [[[self alloc] initWithClass:cls] autorelease];
  330. }
  331. - (id)initWithClass:(Class)cls {
  332. #if GTM_CONTAINERS_VALIDATE
  333. if ((self = [super init])) {
  334. if (!cls) {
  335. _GTMDevLog(@"nil class");
  336. [self release];
  337. return nil;
  338. }
  339. cls_ = cls;
  340. }
  341. return self;
  342. #else // GTM_CONTAINERS_VALIDATE
  343. if ((self = [super init])) {
  344. [self release];
  345. }
  346. return nil;
  347. #endif // GTM_CONTAINERS_VALIDATE
  348. }
  349. - (BOOL)validateObject:(id)object forContainer:(id)container {
  350. return [object isKindOfClass:cls_];
  351. }
  352. @end
  353. @implementation GTMMemberOfClassValidator
  354. + (id)validateAgainstClass:(Class)cls {
  355. return [[[self alloc] initWithClass:cls] autorelease];
  356. }
  357. - (id)initWithClass:(Class)cls {
  358. #if GTM_CONTAINERS_VALIDATE
  359. if ((self = [super init])) {
  360. if (!cls) {
  361. _GTMDevLog(@"nil class");
  362. [self release];
  363. return nil;
  364. }
  365. cls_ = cls;
  366. }
  367. return self;
  368. #else // GTM_CONTAINERS_VALIDATE
  369. if ((self = [super init])) {
  370. [self release];
  371. }
  372. return nil;
  373. #endif // GTM_CONTAINERS_VALIDATE
  374. }
  375. - (BOOL)validateObject:(id)object forContainer:(id)container {
  376. return [object isMemberOfClass:cls_];
  377. }
  378. @end
  379. @implementation GTMConformsToProtocolValidator
  380. + (id)validateAgainstProtocol:(Protocol*)prot {
  381. return [[[self alloc] initWithProtocol:prot] autorelease];
  382. }
  383. - (id)initWithProtocol:(Protocol*)prot {
  384. #if GTM_CONTAINERS_VALIDATE
  385. if ((self = [super init])) {
  386. if (!prot) {
  387. _GTMDevLog(@"nil protocol");
  388. [self release];
  389. return nil;
  390. }
  391. prot_ = prot;
  392. }
  393. return self;
  394. #else // GTM_CONTAINERS_VALIDATE
  395. if ((self = [super init])) {
  396. [self release];
  397. }
  398. return nil;
  399. #endif // GTM_CONTAINERS_VALIDATE
  400. }
  401. - (BOOL)validateObject:(id)object forContainer:(id)container {
  402. return [object conformsToProtocol:prot_];
  403. }
  404. @end
  405. @implementation GTMRespondsToSelectorValidator
  406. + (id)validateAgainstSelector:(SEL)sel {
  407. return [[[self alloc] initWithSelector:sel] autorelease];
  408. }
  409. - (id)initWithSelector:(SEL)sel {
  410. #if GTM_CONTAINERS_VALIDATE
  411. if ((self = [super init])) {
  412. if (!sel) {
  413. _GTMDevLog(@"nil selector");
  414. [self release];
  415. return nil;
  416. }
  417. sel_ = sel;
  418. }
  419. return self;
  420. #else // GTM_CONTAINERS_VALIDATE
  421. if ((self = [super init])) {
  422. [self release];
  423. }
  424. return nil;
  425. #endif // GTM_CONTAINERS_VALIDATE
  426. }
  427. - (BOOL)validateObject:(id)object forContainer:(id)container {
  428. return [object respondsToSelector:sel_];
  429. }
  430. @end