Created
July 3, 2013 11:29
-
-
Save leeprobert/5917162 to your computer and use it in GitHub Desktop.
AGNContainerWalker - class for traversing an Array or Dictionary and broadcasting notifications with the results.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // | |
| // AGNContainerWalker.h | |
| // Agnitio iPlanner | |
| // | |
| // Created by Matt Gough on 14/02/2013. | |
| // Copyright (c) 2013 Agnitio. All rights reserved. | |
| // | |
| #import <Foundation/Foundation.h> | |
| @class AGNContainerWalker; | |
| @protocol AGNContainerWalkerDelegate <NSObject> | |
| @optional | |
| - (BOOL)containerWalker:(AGNContainerWalker*)walker shouldWalkDictionary:(NSDictionary*)dictionary withKey:(NSString*)key; | |
| - (void)containerWalker:(AGNContainerWalker*)walker willStartDictionary:(NSDictionary*)dictionary withKey:(NSString*)key; | |
| - (void)containerWalker:(AGNContainerWalker*)walker didEndDictionary:(NSDictionary*)dictionary withKey:(NSString*)key; | |
| - (BOOL)containerWalker:(AGNContainerWalker*)walker shouldRewindDictionary:(NSDictionary*)dictionary withKey:(NSString*)key; | |
| - (BOOL)containerWalker:(AGNContainerWalker*)walker shouldWalkArray:(NSArray*)array withKey:(NSString*)key; | |
| - (void)containerWalker:(AGNContainerWalker*)walker willStartArray:(NSArray*)array withKey:(NSString*)key; | |
| - (void)containerWalker:(AGNContainerWalker*)walker didEndArray:(NSArray*)array withKey:(NSString*)key; | |
| - (void)containerWalker:(AGNContainerWalker*)walker didFindNonContainer:(NSObject*)object withKey:(NSString*)key; | |
| @end | |
| @interface AGNContainerWalker : NSObject | |
| - (void)walkContainer:(id)dictOrArray; | |
| - (id)valueForItemAtPathStack:(NSArray*)pathStack; | |
| @property (nonatomic, weak) id<AGNContainerWalkerDelegate> delegate; | |
| @property (nonatomic, readonly) id containerBeingWalked; | |
| @property (nonatomic, readonly) NSArray* pathStack; | |
| @end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // | |
| // AGNContainerWalker.m | |
| // Agnitio iPlanner | |
| // | |
| // Created by Matt Gough on 14/02/2013. | |
| // Copyright (c) 2013 Agnitio. All rights reserved. | |
| // | |
| #if ! __has_feature(objc_arc) | |
| #warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC). | |
| #endif | |
| #import "AGNContainerWalker.h" | |
| @interface AGNContainerWalker() | |
| { | |
| NSMutableArray* _pathStack; | |
| id _containerBeingWalked; | |
| struct { | |
| unsigned int shouldWalkDictionary:1; | |
| unsigned int willStartDictionary:1; | |
| unsigned int didEndDictionary:1; | |
| unsigned int shouldRewindDictionary:1; | |
| unsigned int shouldWalkArray:1; | |
| unsigned int willStartArray:1; | |
| unsigned int didEndArray:1; | |
| unsigned int didFindNonContainer:1; | |
| } _delegateSupports; | |
| } | |
| @end | |
| @implementation AGNContainerWalker | |
| - (id)valueForItemAtPathStack:(NSArray *)pathStack | |
| { | |
| id result = _containerBeingWalked; | |
| NSUInteger index = 0; | |
| NSUInteger count = [pathStack count]; | |
| for (id component in pathStack) | |
| { | |
| BOOL isLastItem = index == count - 1 ; | |
| if ([component isKindOfClass:[NSString class]]) | |
| { // Its a key in a dictionary | |
| if (!(isLastItem || [result isKindOfClass:[NSDictionary class]])) | |
| return nil; | |
| result = [result objectForKey:component]; | |
| } | |
| else if ([component isKindOfClass:[NSNumber class]]) | |
| { // Its an array index | |
| if (!(isLastItem || [result isKindOfClass:[NSArray class]])) | |
| return nil; | |
| result = [result objectAtIndex:[component integerValue]]; | |
| } | |
| ++index; | |
| } | |
| return result; | |
| } | |
| - (void)walkNonContainer:(id)value withKey:(NSString*)key | |
| { | |
| if (_delegateSupports.didFindNonContainer) | |
| { | |
| if (key) | |
| [_pathStack addObject:key]; | |
| id<AGNContainerWalkerDelegate> delegate = _delegate; | |
| [delegate containerWalker:self didFindNonContainer:value withKey:key]; | |
| if (key) | |
| [_pathStack removeLastObject]; | |
| } | |
| } | |
| - (void)walkDictionary:(NSDictionary*)dictionary withKey:(NSString*)dictKey | |
| { | |
| id<AGNContainerWalkerDelegate> delegate = _delegate; | |
| if (_delegateSupports.shouldWalkDictionary) | |
| { | |
| if (![delegate containerWalker:self shouldWalkDictionary:dictionary withKey:dictKey]) | |
| return; | |
| } | |
| if (dictKey) | |
| [_pathStack addObject:dictKey]; | |
| if (_delegateSupports.willStartDictionary) | |
| [delegate containerWalker:self willStartDictionary:dictionary withKey:dictKey]; | |
| for (BOOL keepGoing = YES; keepGoing;) | |
| { | |
| // [dictionary enumerateKeysAndObjectsUsingBlock:^(NSString* key, id value, BOOL* stop) { | |
| for (NSString*key in dictionary) { | |
| id value = dictionary[key]; | |
| if ([value isKindOfClass:[NSDictionary class]]) | |
| [self walkDictionary:(NSDictionary*)value withKey:key]; | |
| else if ([value isKindOfClass:[NSArray class]]) | |
| [self walkArray:(NSArray*)value withKey:key]; | |
| else | |
| [self walkNonContainer:value withKey:key]; | |
| } | |
| // ]; | |
| keepGoing = _delegateSupports.shouldRewindDictionary && [delegate containerWalker:self shouldRewindDictionary:dictionary withKey:dictKey]; | |
| } | |
| if (_delegateSupports.didEndDictionary) | |
| [delegate containerWalker:self didEndDictionary:dictionary withKey:dictKey]; | |
| if (dictKey) | |
| [_pathStack removeLastObject]; | |
| } | |
| - (void)walkArray:(NSArray*)array withKey:(NSString*)arrayKey | |
| { | |
| id<AGNContainerWalkerDelegate> delegate = _delegate; | |
| if (_delegateSupports.shouldWalkArray) | |
| { | |
| if (![delegate containerWalker:self shouldWalkArray:array withKey:arrayKey]) | |
| return; | |
| } | |
| if (arrayKey) | |
| [_pathStack addObject:arrayKey]; | |
| if (_delegateSupports.willStartArray) | |
| [delegate containerWalker:self willStartArray:array withKey:arrayKey]; | |
| NSUInteger index = 0; | |
| for (id value in array) | |
| { | |
| [_pathStack addObject:@(index)]; | |
| if ([value isKindOfClass:[NSDictionary class]]) | |
| [self walkDictionary:(NSDictionary*)value withKey:nil]; | |
| else if ([value isKindOfClass:[NSArray class]]) | |
| [self walkArray:(NSArray*)value withKey:nil]; | |
| else | |
| [self walkNonContainer:value withKey:nil]; | |
| ++index; | |
| [_pathStack removeLastObject]; | |
| } | |
| if (_delegateSupports.didEndArray) | |
| [delegate containerWalker:self didEndArray:array withKey:arrayKey]; | |
| if (arrayKey) | |
| [_pathStack removeLastObject]; | |
| } | |
| - (void)walkContainer:(id)dictOrArray | |
| { | |
| _containerBeingWalked = dictOrArray; | |
| _pathStack = [[NSMutableArray alloc] init]; | |
| if ([dictOrArray isKindOfClass:[NSDictionary class]]) | |
| [self walkDictionary:(NSDictionary*)dictOrArray withKey:nil]; | |
| else if ([dictOrArray isKindOfClass:[NSArray class]]) | |
| [self walkArray:(NSArray*)dictOrArray withKey:nil]; | |
| else | |
| { | |
| AGNLogLWarning(@"Unexpected object:%@", dictOrArray); | |
| [self walkNonContainer:dictOrArray withKey:nil]; | |
| } | |
| } | |
| - (void)setDelegate:(id<AGNContainerWalkerDelegate>)delegate | |
| { | |
| _delegate = delegate; | |
| _delegateSupports.shouldWalkDictionary = [delegate respondsToSelector:@selector(containerWalker:shouldWalkDictionary:withKey:)]; | |
| _delegateSupports.willStartDictionary = [delegate respondsToSelector:@selector(containerWalker:willStartDictionary:withKey:)]; | |
| _delegateSupports.didEndDictionary = [delegate respondsToSelector:@selector(containerWalker:didEndDictionary:withKey:)]; | |
| _delegateSupports.shouldRewindDictionary = [delegate respondsToSelector:@selector(containerWalker:shouldRewindDictionary:withKey:)]; | |
| _delegateSupports.shouldWalkArray = [delegate respondsToSelector:@selector(containerWalker:shouldWalkArray:withKey:)]; | |
| _delegateSupports.willStartArray = [delegate respondsToSelector:@selector(containerWalker:willStartArray:withKey:)]; | |
| _delegateSupports.didEndArray = [delegate respondsToSelector:@selector(containerWalker:didEndArray:withKey:)]; | |
| _delegateSupports.didFindNonContainer = [delegate respondsToSelector:@selector(containerWalker:didFindNonContainer:withKey:)]; | |
| } | |
| @end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment