Skip to content

Instantly share code, notes, and snippets.

@jimkang
Created July 23, 2010 03:12
Show Gist options
  • Save jimkang/486951 to your computer and use it in GitHub Desktop.
Save jimkang/486951 to your computer and use it in GitHub Desktop.
//
// OGKeyPath.h
//
// Created by Jim on 3/11/10.
//
// Copyright (c) 2010 Jim Kang/Phalange Software.
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
@interface OGKeyPath : NSObject
{
}
// Cocoa KVC key paths let you get at elements with an object hierarchy, provided
// everything in your desired path is KVC-compliant. Unfortunately, NSArray
// elements cannot be referenced in key path strings.
// OGKeyPath provides a way to traverse those hierarchies containing arrays.
// "Extended key path" arguments for OGKeyPath methods are like KVC key paths but are
// arrays instead of strings. A key path segment may be a NSNumber representing an index
// in an NSArray or an NSString representing a property of an object.
//
// e.g. This extended key path
//
// [@"configs", [NSNumber numberWithInt:3], @"color", @"alpha"]
//
// would map to the value 0.5 on the hierarchy in the diagram below, which has "foo" at
// the top level:
//
// foo
// {
// users: { ... },
// something: "whatever",
// configs:
// [
// config1: { ... },
// config2: { ... },
// config3: { ... },
// config4:
// {
// size: 50,
// color:
// {
// r: 0,
// g: 255,
// b: 255,
// alpha: 0.5 (*)
// }
// }
// ]
//}
// Example:
// [OGKeyPath set:myObject value:[NSNumber numberWithFloat:1.0f] forExtendedKeyPath:
// [NSArray arrayWithObjects:@"configs", [NSNumber numberWithInt:3], @"color", @"alpha", nil]];
+ (void)set:(id)kvcObject value:(id)value forExtendedKeyPath:(NSArray *)path;
// Alternate version that puts the array together for you.
// Example:
// [OGKeyPath set:myObject value:[NSNumber numberWithFloat:1.0f] forExtendedKeyPathWithObjects:
// @"configs", [NSNumber numberWithInt:3], @"color", @"alpha", nil];
+ (void)set:(id)kvcObject value:(id)value forExtendedKeyPathWithObjects:(id)firstPathSegment, ...;
// Example:
// [OGKeyPath valueFrom:myObject forExtendedKeyPath:
// [NSArray arrayWithObjects:@"configs", [NSNumber numberWithInt:3], @"color", @"alpha", nil]];
+ (id)valueFrom:(id)kvcObject forExtendedKeyPath:(NSArray *)path;
// Alternate version that puts the array together for you.
// Example:
// [OGKeyPath valueFrom:myObject forExtendedKeyPathWithObjects:
// @"configs", [NSNumber numberWithInt:3], @"color", @"alpha", nil];
+ (id)valueFrom:(id)kvcObject forExtendedKeyPathWithObjects:(id)firstPathSegment, ...;
@end
//
// OGKeyPath.m
//
// Created by Jim on 3/11/10.
//
// Copyright (c) 2010 Jim Kang/Phalange Software.
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
#import "OGKeyPath.h"
@implementation OGKeyPath
+ (void)set:(id)kvcObject value:(id)value forExtendedKeyPathWithObjects:(id)object, ...
{
NSMutableArray* path = [NSMutableArray array];
va_list ap;
va_start(ap, object);
while (object)
{
[path addObject:object];
object = va_arg(ap, id);
}
va_end(ap);
[OGKeyPath set:kvcObject value:value forExtendedKeyPath:path];
}
+ (void)set:(id)kvcObject value:(id)value forExtendedKeyPath:(NSArray *)path
{
if ([path isKindOfClass:[NSArray class]] && [path count] > 0)
{
NSArray *penultimatePath = [path subarrayWithRange:NSMakeRange(0, [path count] - 1)];
id objectAtPenultimatePath = [OGKeyPath valueFrom:kvcObject forExtendedKeyPath:penultimatePath];
id lastPathSegment = [path objectAtIndex:[path count] - 1];
if ([lastPathSegment isKindOfClass:[NSString class]])
{
if ([objectAtPenultimatePath isKindOfClass:[NSMutableArray class]] &&
([lastPathSegment intValue] < [objectAtPenultimatePath count]))
{
[objectAtPenultimatePath replaceObjectAtIndex:[lastPathSegment intValue] withObject:value];
}
else
{
[objectAtPenultimatePath setValue:value forKey:lastPathSegment];
}
}
else if ([lastPathSegment isKindOfClass:[NSNumber class]])
{
if ([lastPathSegment isKindOfClass:[NSMutableArray class]])
{
[objectAtPenultimatePath replaceObjectAtIndex:[lastPathSegment intValue] withObject:value];
}
else
{
NSLog(@"set:value:forExtendedKeyPath: is in the position of applying an array index %d \
to a non-mutable-array object, which it can't do.", [lastPathSegment intValue]);
}
}
else
{
NSLog(@"set:value:forExtendedKeyPath: encountered a path segment it can't handle.");
}
}
}
+ (id)valueFrom:(id)kvcObject forExtendedKeyPath:(NSArray *)path
{
id currentValue = kvcObject;
for (id pathSegment in path)
{
if ([pathSegment isKindOfClass:[NSString class]])
{
currentValue = [currentValue valueForKey:pathSegment];
}
else if ([pathSegment isKindOfClass:[NSNumber class]])
{
if ([currentValue isKindOfClass:[NSArray class]])
{
currentValue = [currentValue objectAtIndex:[currentValue intValue]];
}
else
{
NSLog(@"valueFrom:forExtendedKeyPath: is in the position of applying an array index %d \
to a non-array object, which it can't do.", [currentValue intValue]);
break;
}
}
else
{
NSLog(@"valueFrom:forExtendedKeyPath: encountered a path segment it can't handle.");
break;
}
}
return currentValue;
}
+ (id)valueFrom:(id)kvcObject forExtendedKeyPathWithObjects:(id)object, ...
{
NSMutableArray* path = [NSMutableArray array];
va_list ap;
va_start(ap, object);
while (object)
{
[path addObject:object];
object = va_arg(ap, id);
}
va_end(ap);
return [OGKeyPath valueFrom:kvcObject forExtendedKeyPath:path];
}
@end
@cgyy
Copy link

cgyy commented Jul 23, 2010

hello

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment