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