/* * CPArray+KVO.j * Foundation * * Created by Ross Boucher. * Copyright 2008, 280 North, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @import "CPArray.j" @import "CPNull.j" @implementation CPObject (CPArrayKVO) - (id)mutableArrayValueForKey:(id)aKey { return [[_CPKVCArray alloc] initWithKey:aKey forProxyObject:self]; } - (id)mutableArrayValueForKeyPath:(id)aKeyPath { var dotIndex = aKeyPath.indexOf("."); if (dotIndex < 0) return [self mutableArrayValueForKey:aKeyPath]; var firstPart = aKeyPath.substring(0, dotIndex), lastPart = aKeyPath.substring(dotIndex+1); return [[self valueForKeyPath:firstPart] valueForKeyPath:lastPart]; } @end @implementation _CPKVCArray : CPArray { id _proxyObject; id _key; SEL _insertSEL; Function _insert; SEL _removeSEL; Function _remove; SEL _replaceSEL; Function _replace; SEL _insertManySEL; Function _insertMany; SEL _removeManySEL; Function _removeMany; SEL _replaceManySEL; Function _replaceMany; SEL _objectAtIndexSEL; Function _objectAtIndex; SEL _countSEL; Function _count; SEL _accessSEL; Function _access; SEL _setSEL; Function _set; } + (id)alloc { var array = []; array.isa = self; var ivars = class_copyIvarList(self), count = ivars.length; while (count--) array[ivar_getName(ivars[count])] = nil; return array; } -(id)initWithKey:(id)aKey forProxyObject:(id)anObject { self = [super init]; _key = aKey; _proxyObject = anObject; var capitalizedKey = _key.charAt(0).toUpperCase() + _key.substring(1); _insertSEL = sel_getName(@"insertObject:in"+capitalizedKey+"AtIndex:"); if ([_proxyObject respondsToSelector:_insertSEL]) _insert = [_proxyObject methodForSelector:_insertSEL]; _removeSEL = sel_getName(@"removeObjectFrom"+capitalizedKey+"AtIndex:"); if ([_proxyObject respondsToSelector:_removeSEL]) _remove = [_proxyObject methodForSelector:_removeSEL]; _replaceSEL = sel_getName(@"replaceObjectFrom"+capitalizedKey+"AtIndex:withObject:"); if ([_proxyObject respondsToSelector:_replaceSEL]) _replace = [_proxyObject methodForSelector:_replaceSEL]; _insertManySEL = sel_getName(@"insertObjects:in"+capitalizedKey+"AtIndexes:"); if ([_proxyObject respondsToSelector:_insertManySEL]) _insert = [_proxyObject methodForSelector:_insertManySEL]; _removeManySEL = sel_getName(@"removeObjectsFrom"+capitalizedKey+"AtIndexes:"); if ([_proxyObject respondsToSelector:_removeManySEL]) _remove = [_proxyObject methodForSelector:_removeManySEL]; _replaceManySEL = sel_getName(@"replaceObjectsFrom"+capitalizedKey+"AtIndexes:withObjects:"); if ([_proxyObject respondsToSelector:_replaceManySEL]) _replace = [_proxyObject methodForSelector:_replaceManySEL]; _objectAtIndexSEL = sel_getName(@"objectIn"+capitalizedKey+"AtIndex:"); if ([_proxyObject respondsToSelector:_objectAtIndexSEL]) _objectAtIndex = [_proxyObject methodForSelector:_objectAtIndexSEL]; _countSEL = sel_getName(@"countOf"+capitalizedKey); if ([_proxyObject respondsToSelector:_countSEL]) _count = [_proxyObject methodForSelector:_countSEL]; _accessSEL = sel_getName(_key); if ([_proxyObject respondsToSelector:_accessSEL]) _access = [_proxyObject methodForSelector:_accessSEL]; _setSEL = sel_getName(@"set"+capitalizedKey+":"); if ([_proxyObject respondsToSelector:_setSEL]) _set = [_proxyObject methodForSelector:_setSEL]; return self; } - (id)copy { var theCopy = [], count = [self count]; for (var i=0; i 0) min = item; } return min; } kvoOperators["count"] = function countOperator(self, _cmd, param) { return [self count]; } kvoOperators["sum"] = function sumOperator(self, _cmd, param) { var objects = [self valueForKeyPath:param], index = [objects count], sum = 0.0; while(index--) sum += [objects[index] doubleValue]; return sum; } @implementation CPArray (KeyValueObserving) - (void)addObserver:(id)anObserver toObjectsAtIndexes:(CPIndexSet)indexes forKeyPath:(CPString)aKeyPath options:(unsigned)options context:(id)context { var index = [indexes firstIndex]; while (index >= 0) { [self[index] addObserver:anObserver forKeyPath:aKeyPath options:options context:context]; index = [indexes indexGreaterThanIndex:index]; } } - (void)removeObserver:(id)anObserver fromObjectsAtIndexes:(CPIndexSet)indexes forKeyPath:(CPString)aKeyPath { var index = [indexes firstIndex]; while (index >= 0) { [self[index] removeObserver:anObserver forKeyPath:aKeyPath]; index = [indexes indexGreaterThanIndex:index]; } } -(void)addObserver:(id)observer forKeyPath:(CPString)aKeyPath options:(unsigned)options context:(id)context { if ([isa instanceMethodForSelector:_cmd] === [CPArray instanceMethodForSelector:_cmd]) [CPException raise:CPInvalidArgumentException reason:"Unsupported method on CPArray"]; else [super addObserver:observer forKeyPath:aKeyPath options:options context:context]; } -(void)removeObserver:(id)observer forKeyPath:(CPString)aKeyPath { if ([isa instanceMethodForSelector:_cmd] === [CPArray instanceMethodForSelector:_cmd]) [CPException raise:CPInvalidArgumentException reason:"Unsupported method on CPArray"]; else [super removeObserver:observer forKeyPath:aKeyPath]; } @end