Skip to content

Instantly share code, notes, and snippets.

@leeprobert
Created July 3, 2013 11:29
Show Gist options
  • Select an option

  • Save leeprobert/5917162 to your computer and use it in GitHub Desktop.

Select an option

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.
//
// 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
//
// 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