00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 @import "CPArray.j"
00024
00025 @implementation CPObject (CPArrayKVO)
00026
00027 - (id)mutableArrayValueForKey:(id)aKey
00028 {
00029 return [[_CPKVCArray alloc] initWithKey:aKey forProxyObject:self];
00030 }
00031
00032 - (id)mutableArrayValueForKeyPath:(id)aKeyPath
00033 {
00034 var dotIndex = aKeyPath.indexOf(".");
00035
00036 if (dotIndex < 0)
00037 return [self mutableArrayValueForKey:aKeyPath];
00038
00039 var firstPart = aKeyPath.substring(0, dotIndex),
00040 lastPart = aKeyPath.substring(dotIndex+1);
00041
00042 return [[self valueForKeyPath:firstPart] valueForKeyPath:lastPart];
00043 }
00044
00045 @end
00046
00047 @implementation _CPKVCArray : CPArray
00048 {
00049 id _proxyObject;
00050 id _key;
00051
00052 SEL _insertSEL;
00053 Function _insert;
00054
00055 SEL _removeSEL;
00056 Function _remove;
00057
00058 SEL _replaceSEL;
00059 Function _replace;
00060
00061 SEL _insertManySEL;
00062 Function _insertMany;
00063
00064 SEL _removeManySEL;
00065 Function _removeMany;
00066
00067 SEL _replaceManySEL;
00068 Function _replaceMany;
00069
00070 SEL _objectAtIndexSEL;
00071 Function _objectAtIndex;
00072
00073 SEL _countSEL;
00074 Function _count;
00075
00076 SEL _accessSEL;
00077 Function _access;
00078
00079 SEL _setSEL;
00080 Function _set;
00081 }
00082
00083 + (id)alloc
00084 {
00085 var a = [];
00086 a.isa = self;
00087
00088 var ivars = class_copyIvarList(self),
00089 count = ivars.length;
00090
00091 while (count--)
00092 a[ivar_getName(ivars[count])] = nil;
00093
00094 return a;
00095 }
00096
00097 -(id)initWithKey:(id)aKey forProxyObject:(id)anObject
00098 {
00099 self = [super init];
00100
00101 _key = aKey;
00102 _proxyObject = anObject;
00103
00104 var capitalizedKey = _key.charAt(0).toUpperCase() + _key.substring(1);
00105
00106 _insertSEL = sel_getName(@"insertObject:in"+capitalizedKey+"AtIndex:");
00107 if ([_proxyObject respondsToSelector:_insertSEL])
00108 _insert = [_proxyObject methodForSelector:_insertSEL];
00109
00110 _removeSEL = sel_getName(@"removeObjectFrom"+capitalizedKey+"AtIndex:");
00111 if ([_proxyObject respondsToSelector:_removeSEL])
00112 _remove = [_proxyObject methodForSelector:_removeSEL];
00113
00114 _replaceSEL = sel_getName(@"replaceObjectFrom"+capitalizedKey+"AtIndex:withObject:");
00115 if ([_proxyObject respondsToSelector:_replaceSEL])
00116 _replace = [_proxyObject methodForSelector:_replaceSEL];
00117
00118 _insertManySEL = sel_getName(@"insertObjects:in"+capitalizedKey+"AtIndexes:");
00119 if ([_proxyObject respondsToSelector:_insertManySEL])
00120 _insert = [_proxyObject methodForSelector:_insertManySEL];
00121
00122 _removeManySEL = sel_getName(@"removeObjectsFrom"+capitalizedKey+"AtIndexes:");
00123 if ([_proxyObject respondsToSelector:_removeManySEL])
00124 _remove = [_proxyObject methodForSelector:_removeManySEL];
00125
00126 _replaceManySEL = sel_getName(@"replaceObjectsFrom"+capitalizedKey+"AtIndexes:withObjects:");
00127 if ([_proxyObject respondsToSelector:_replaceManySEL])
00128 _replace = [_proxyObject methodForSelector:_replaceManySEL];
00129
00130 _objectAtIndexSEL = sel_getName(@"objectIn"+capitalizedKey+"AtIndex:");
00131 if ([_proxyObject respondsToSelector:_objectAtIndexSEL])
00132 _objectAtIndex = [_proxyObject methodForSelector:_objectAtIndexSEL];
00133
00134 _countSEL = sel_getName(@"countOf"+capitalizedKey);
00135 if ([_proxyObject respondsToSelector:_countSEL])
00136 _count = [_proxyObject methodForSelector:_countSEL];
00137
00138 _accessSEL = sel_getName(_key);
00139 if ([_proxyObject respondsToSelector:_accessSEL])
00140 _access = [_proxyObject methodForSelector:_accessSEL];
00141
00142 _setSEL = sel_getName(@"set"+capitalizedKey+":");
00143 if ([_proxyObject respondsToSelector:_setSEL])
00144 _set = [_proxyObject methodForSelector:_setSEL];
00145
00146 return self;
00147 }
00148
00149 - (id)copy
00150 {
00151 var theCopy = [],
00152 count = [self count];
00153
00154 for (var i=0; i<count; i++)
00155 [theCopy addObject:[self objectAtIndex:i]];
00156
00157 return theCopy;
00158 }
00159
00160 -(id)_representedObject
00161 {
00162 if (_access)
00163 return _access(_proxyObject, _accessSEL);
00164
00165 return [_proxyObject valueForKey:_key];
00166 }
00167
00168 -(void)_setRepresentedObject:(id)anObject
00169 {
00170 if (_set)
00171 return _set(_proxyObject, _setSEL, anObject);
00172
00173 [_proxyObject setValue:anObject forKey:_key];
00174 }
00175
00176 - (unsigned)count
00177 {
00178 if (_count)
00179 return _count(_proxyObject, _countSEL);
00180
00181 return [[self _representedObject] count];
00182 }
00183
00184 - (id)objectAtIndex:(unsigned)anIndex
00185 {
00186 if(_objectAtIndex)
00187 return _objectAtIndex(_proxyObject, _objectAtIndexSEL, anIndex);
00188
00189 return [[self _representedObject] objectAtIndex:anIndex];
00190 }
00191
00192 - (void)addObject:(id)anObject
00193 {
00194 if (_insert)
00195 return _insert(_proxyObject, _insertSEL, anObject, [self count]);
00196
00197 var target = [[self _representedObject] copy];
00198
00199 [target addObject:anObject];
00200 [self _setRepresentedObject:target];
00201 }
00202
00203 - (void)insertObject:(id)anObject atIndex:(unsigned)anIndex
00204 {
00205 if (_insert)
00206 return _insert(_proxyObject, _insertSEL, anObject, anIndex);
00207
00208 var target = [[self _representedObject] copy];
00209
00210 [target insertObject:anObject atIndex:anIndex];
00211 [self _setRepresentedObject:target];
00212 }
00213
00214 - (void)removeLastObject
00215 {
00216 if(_remove)
00217 return _remove(_proxyObject, _removeSEL, [self count]-1);
00218
00219 var target = [[self _representedObject] copy];
00220
00221 [target removeLastObject];
00222 [self _setRepresentedObject:target];
00223 }
00224
00225 - (void)removeObjectAtIndex:(unsigned)anIndex
00226 {
00227 if(_remove)
00228 return _remove(_proxyObject, _removeSEL, anIndex);
00229
00230 var target = [[self _representedObject] copy];
00231
00232 [target removeObjectAtIndex:anIndex];
00233 [self _setRepresentedObject:target];
00234 }
00235
00236 - (void)replaceObjectAtIndex:(unsigned)anIndex withObject:(id)anObject
00237 {
00238 if(_replace)
00239 return _replace(_proxyObject, _replaceSEL, anIndex, anObject);
00240
00241 var target = [[self _representedObject] copy];
00242
00243 [target replaceObjectAtIndex:anIndex withObject:anObject];
00244 [self _setRepresentedObject:target];
00245 }
00246
00247 - (CPArray)objectsAtIndexes:(CPIndexSet)indexes
00248 {
00249 var index = [indexes firstIndex],
00250 objects = [];
00251
00252 while(index != CPNotFound)
00253 {
00254 [objects addObject:[self objectAtIndex:index]];
00255 index = [indexes indexGreaterThanIndex:index];
00256 }
00257
00258 return objects;
00259 }
00260
00261 @end
00262
00263
00264
00265
00266 @implementation CPArray (KeyValueCoding)
00267
00268 - (id)valueForKey:(CPString)aKey
00269 {
00270
00271 if (aKey.indexOf("@") === 0)
00272 {
00273 if (aKey.indexOf(".") !== -1)
00274 [CPException raise:CPInvalidArgumentException reason:"called valueForKey: on an array with a complex key ("+aKey+"). use valueForKeyPath:"];
00275
00276 if (aKey == "@count")
00277 return length;
00278
00279 return nil;
00280 }
00281 else
00282 {
00283 var newArray = [],
00284 enumerator = [self objectEnumerator],
00285 object;
00286
00287 while ((object = [enumerator nextObject]) !== nil)
00288 {
00289 var value = [object valueForKey:aKey];
00290
00291 if (value === nil || value === undefined)
00292 value = [CPNull null];
00293
00294 newArray.push(value);
00295 }
00296
00297 return newArray;
00298 }
00299 }
00300
00301 - (id)valueForKeyPath:(CPString)aKeyPath
00302 {
00303 if (aKeyPath.indexOf("@") === 0)
00304 {
00305 var dotIndex = aKeyPath.indexOf("."),
00306 operator = aKeyPath.substring(1, dotIndex),
00307 parameter = aKeyPath.substring(dotIndex+1);
00308
00309 if (kvoOperators[operator])
00310 return kvoOperators[operator](self, _cmd, parameter);
00311
00312 return nil;
00313 }
00314 else
00315 {
00316 var newArray = [],
00317 enumerator = [self objectEnumerator],
00318 object;
00319
00320 while ((object = [enumerator nextObject]) !== nil)
00321 {
00322 var value = [object valueForKeyPath:aKeyPath];
00323
00324 if (value === nil || value === undefined)
00325 value = [CPNull null];
00326
00327 newArray.push(value);
00328 }
00329
00330 return newArray;
00331 }
00332 }
00333
00334 - (void)setValue:(id)aValue forKey:(CPString)aKey
00335 {
00336 var enumerator = [self objectEnumerator],
00337 object;
00338
00339 while (object = [enumerator nextObject])
00340 [object setValue:aValue forKey:aKey];
00341 }
00342
00343 - (void)setValue:(id)aValue forKeyPath:(CPString)aKeyPath
00344 {
00345 var enumerator = [self objectEnumerator],
00346 object;
00347
00348 while (object = [enumerator nextObject])
00349 [object setValue:aValue forKeyPath:aKeyPath];
00350 }
00351
00352
00353 @end
00354
00355 var kvoOperators = [];
00356
00357 kvoOperators["avg"] = function avgOperator(self, _cmd, param)
00358 {
00359 var objects = [self valueForKeyPath:param],
00360 length = [objects count],
00361 index = length;
00362 average = 0.0;
00363
00364 if (!length)
00365 return 0;
00366
00367 while(index--)
00368 average += [objects[index] doubleValue];
00369
00370 return average / length;
00371 }
00372
00373 kvoOperators["max"] = function maxOperator(self, _cmd, param)
00374 {
00375 var objects = [self valueForKeyPath:param],
00376 index = [objects count] - 1,
00377 max = [objects lastObject];
00378
00379 while (index--)
00380 {
00381 var item = objects[index];
00382 if ([max compare:item] < 0)
00383 max = item;
00384 }
00385
00386 return max;
00387 }
00388
00389 kvoOperators["min"] = function minOperator(self, _cmd, param)
00390 {
00391 var objects = [self valueForKeyPath:param],
00392 index = [objects count] - 1,
00393 min = [objects lastObject];
00394
00395 while (index--)
00396 {
00397 var item = objects[index];
00398 if ([min compare:item] > 0)
00399 min = item;
00400 }
00401
00402 return min;
00403 }
00404
00405 kvoOperators["count"] = function countOperator(self, _cmd, param)
00406 {
00407 return [self count];
00408 }
00409
00410 kvoOperators["sum"] = function sumOperator(self, _cmd, param)
00411 {
00412 var objects = [self valueForKeyPath:param],
00413 index = [objects count],
00414 sum = 0.0;
00415
00416 while(index--)
00417 sum += [objects[index] doubleValue];
00418
00419 return sum;
00420 }
00421
00422 @implementation CPArray (KeyValueObserving)
00423
00424 - (void)addObserver:(id)anObserver toObjectsAtIndexes:(CPIndexSet)indexes forKeyPath:(CPString)aKeyPath options:(unsigned)options context:(id)context
00425 {
00426 var index = [indexes firstIndex];
00427
00428 while (index >= 0)
00429 {
00430 [self[index] addObserver:anObserver forKeyPath:aKeyPath options:options context:context];
00431
00432 index = [indexes indexGreaterThanIndex:index];
00433 }
00434 }
00435
00436 - (void)removeObserver:(id)anObserver fromObjectsAtIndexes:(CPIndexSet)indexes forKeyPath:(CPString)aKeyPath
00437 {
00438 var index = [indexes firstIndex];
00439
00440 while (index >= 0)
00441 {
00442 [self[index] removeObserver:anObserver forKeyPath:aKeyPath];
00443
00444 index = [indexes indexGreaterThanIndex:index];
00445 }
00446 }
00447
00448 -(void)addObserver:(id)observer forKeyPath:(CPString)aKeyPath options:(unsigned)options context:(id)context
00449 {
00450 if ([isa instanceMethodForSelector:_cmd] === [CPArray instanceMethodForSelector:_cmd])
00451 [CPException raise:CPInvalidArgumentException reason:"Unsupported method on CPArray"];
00452 else
00453 [super addObserver:observer forKeyPath:aKeyPath options:options context:context];
00454 }
00455
00456 -(void)removeObserver:(id)observer forKeyPath:(CPString)aKeyPath
00457 {
00458 if ([isa instanceMethodForSelector:_cmd] === [CPArray instanceMethodForSelector:_cmd])
00459 [CPException raise:CPInvalidArgumentException reason:"Unsupported method on CPArray"];
00460 else
00461 [super removeObserver:observer forKeyPath:aKeyPath];
00462 }
00463
00464 @end