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 "CPDictionary.j"
00025 @import "CPException.j"
00026 @import "CPNull.j"
00027 @import "CPObject.j"
00028 @import "CPSet.j"
00029
00030
00031 @implementation CPObject (KeyValueObserving)
00032
00033 - (void)willChangeValueForKey:(CPString)aKey
00034 {
00035 }
00036
00037 - (void)didChangeValueForKey:(CPString)aKey
00038 {
00039 }
00040
00041 - (void)willChange:(CPKeyValueChange)change valuesAtIndexes:(CPIndexSet)indexes forKey:(CPString)key
00042 {
00043 }
00044
00045 - (void)didChange:(CPKeyValueChange)change valuesAtIndexes:(CPIndexSet)indexes forKey:(CPString)key
00046 {
00047 }
00048
00049 - (void)addObserver:(id)anObserver forKeyPath:(CPString)aPath options:(unsigned)options context:(id)aContext
00050 {
00051 if (!anObserver || !aPath)
00052 return;
00053
00054 [[_CPKVOProxy proxyForObject:self] _addObserver:anObserver forKeyPath:aPath options:options context:aContext];
00055 }
00056
00057 - (void)removeObserver:(id)anObserver forKeyPath:(CPString)aPath
00058 {
00059 if (!anObserver || !aPath)
00060 return;
00061
00062 [self[KVOProxyKey] _removeObserver:anObserver forKeyPath:aPath];
00063 }
00064
00065 + (BOOL)automaticallyNotifiesObserversForKey:(CPString)aKey
00066 {
00067 return YES;
00068 }
00069
00070 + (CPSet)keyPathsForValuesAffectingValueForKey:(CPString)aKey
00071 {
00072 var capitalizedKey = aKey.charAt(0).toUpperCase() + aKey.substring(1);
00073 selector = "keyPathsForValuesAffecting" + capitalizedKey;
00074
00075 if ([[self class] respondsToSelector:selector])
00076 return objj_msgSend([self class], selector);
00077
00078 return [CPSet set];
00079 }
00080
00081 - (void)applyChange:(CPDictionary)aChange toKeyPath:(CPString)aKeyPath
00082 {
00083 var changeKind = [aChange objectForKey:CPKeyValueChangeKindKey];
00084
00085 if (changeKind === CPKeyValueChangeSetting)
00086 {
00087 var value = [aChange objectForKey:CPKeyValueChangeNewKey];
00088
00089 [self setValue:value === [CPNull null] ? nil : value forKeyPath:aKeyPath];
00090 }
00091
00092 else if (changeKind === CPKeyValueChangeInsertion)
00093 [[self mutableArrayValueForKeyPath:aKeyPath]
00094 insertObjects:[aChange objectForKey:CPKeyValueChangeNewKey]
00095 atIndexes:[aChange objectForKey:CPKeyValueChangeIndexesKey]];
00096
00097 else if (changeKind === CPKeyValueChangeRemoval)
00098 [[self mutableArrayValueForKeyPath:aKeyPath]
00099 removeObjectsAtIndexes:[aChange objectForKey:CPKeyValueChangeIndexesKey]];
00100
00101 else if (changeKind === CPKeyValueChangeReplacement)
00102 [[self mutableArrayValueForKeyPath:aKeyPath]
00103 replaceObjectAtIndexes:[aChange objectForKey:CPKeyValueChangeIndexesKey]
00104 withObjects:[aChange objectForKey:CPKeyValueChangeNewKey]];
00105 }
00106
00107 @end
00108
00109 @implementation CPDictionary (KeyValueObserving)
00110
00111 - (CPDictionary)inverseChangeDictionary
00112 {
00113 var inverseChangeDictionary = [self mutableCopy],
00114 changeKind = [self objectForKey:CPKeyValueChangeKindKey];
00115
00116 if (changeKind === CPKeyValueChangeSetting || changeKind === CPKeyValueChangeReplacement)
00117 {
00118 [inverseChangeDictionary
00119 setObject:[self objectForKey:CPKeyValueChangeOldKey]
00120 forKey:CPKeyValueChangeNewKey];
00121
00122 [inverseChangeDictionary
00123 setObject:[self objectForKey:CPKeyValueChangeNewKey]
00124 forKey:CPKeyValueChangeOldKey];
00125 }
00126
00127 else if (changeKind === CPKeyValueChangeInsertion)
00128 {
00129 [inverseChangeDictionary
00130 setObject:CPKeyValueChangeRemoval
00131 forKey:CPKeyValueChangeKindKey];
00132
00133 [inverseChangeDictionary
00134 setObject:[self objectForKey:CPKeyValueChangeNewKey]
00135 forKey:CPKeyValueChangeOldKey];
00136
00137 [inverseChangeDictionary removeObjectForKey:CPKeyValueChangeNewKey];
00138 }
00139
00140 else if (changeKind === CPKeyValueChangeRemoval)
00141 {
00142 [inverseChangeDictionary
00143 setObject:CPKeyValueChangeInsertion
00144 forKey:CPKeyValueChangeKindKey];
00145
00146 [inverseChangeDictionary
00147 setObject:[self objectForKey:CPKeyValueChangeOldKey]
00148 forKey:CPKeyValueChangeNewKey];
00149
00150 [inverseChangeDictionary removeObjectForKey:CPKeyValueChangeOldKey];
00151 }
00152
00153 return inverseChangeDictionary;
00154 }
00155
00156 @end
00157
00158
00159 CPKeyValueObservingOptionNew = 1 << 0;
00160 CPKeyValueObservingOptionOld = 1 << 1;
00161 CPKeyValueObservingOptionInitial = 1 << 2;
00162 CPKeyValueObservingOptionPrior = 1 << 3;
00163
00164
00165 CPKeyValueChangeKindKey = @"CPKeyValueChangeKindKey";
00166 CPKeyValueChangeNewKey = @"CPKeyValueChangeNewKey";
00167 CPKeyValueChangeOldKey = @"CPKeyValueChangeOldKey";
00168 CPKeyValueChangeIndexesKey = @"CPKeyValueChangeIndexesKey";
00169 CPKeyValueChangeNotificationIsPriorKey = @"CPKeyValueChangeNotificationIsPriorKey";
00170
00171
00172 CPKeyValueChangeSetting = 1;
00173 CPKeyValueChangeInsertion = 2;
00174 CPKeyValueChangeRemoval = 3;
00175 CPKeyValueChangeReplacement = 4;
00176
00177 var kvoNewAndOld = CPKeyValueObservingOptionNew|CPKeyValueObservingOptionOld,
00178 DependentKeysKey = "$KVODEPENDENT",
00179 KVOProxyKey = "$KVOPROXY";
00180
00181
00182
00183
00184 @implementation _CPKVOProxy : CPObject
00185 {
00186 id _targetObject;
00187 Class _nativeClass;
00188 CPDictionary _changesForKey;
00189 Object _observersForKey;
00190 int _observersForKeyLength;
00191 CPSet _replacedKeys;
00192 }
00193
00194 + (id)proxyForObject:(CPObject)anObject
00195 {
00196 var proxy = anObject[KVOProxyKey];
00197
00198 if (proxy)
00199 return proxy;
00200
00201 proxy = [[self alloc] initWithTarget:anObject];
00202
00203 [proxy _replaceClass];
00204
00205 anObject[KVOProxyKey] = proxy;
00206
00207 return proxy;
00208 }
00209
00210 - (id)initWithTarget:(id)aTarget
00211 {
00212 self = [super init];
00213
00214 _targetObject = aTarget;
00215 _nativeClass = [aTarget class];
00216 _replacedKeys = [CPSet set];
00217 _observersForKey = {};
00218 _changesForKey = {};
00219 _observersForKeyLength = 0;
00220
00221 return self;
00222 }
00223
00224 - (void)_replaceClass
00225 {
00226 var currentClass = _nativeClass,
00227 kvoClassName = "$KVO_"+class_getName(_nativeClass),
00228 existingKVOClass = objj_lookUpClass(kvoClassName);
00229
00230 if (existingKVOClass)
00231 {
00232 _targetObject.isa = existingKVOClass;
00233 return;
00234 }
00235
00236 var kvoClass = objj_allocateClassPair(currentClass, kvoClassName);
00237
00238 objj_registerClassPair(kvoClass);
00239
00240
00241 var methodList = _CPKVOModelSubclass.method_list,
00242 count = methodList.length;
00243
00244 for (var i=0; i<count; i++)
00245 {
00246 var method = methodList[i];
00247 class_addMethod(kvoClass, method_getName(method), method_getImplementation(method), "");
00248 }
00249
00250 _targetObject.isa = kvoClass;
00251 }
00252
00253 - (void)_replaceSetterForKey:(CPString)aKey
00254 {
00255 if ([_replacedKeys containsObject:aKey] || ![_nativeClass automaticallyNotifiesObserversForKey:aKey])
00256 return;
00257
00258 var currentClass = _nativeClass,
00259 capitalizedKey = aKey.charAt(0).toUpperCase() + aKey.substring(1),
00260 found = false,
00261 replacementMethods = [
00262 "set"+capitalizedKey+":", _kvoMethodForMethod,
00263 "_set"+capitalizedKey+":", _kvoMethodForMethod,
00264 "insertObject:in"+capitalizedKey+"AtIndex:", _kvoInsertMethodForMethod,
00265 "replaceObjectIn"+capitalizedKey+"AtIndex:withObject:", _kvoReplaceMethodForMethod,
00266 "removeObjectFrom"+capitalizedKey+"AtIndex:", _kvoRemoveMethodForMethod
00267 ];
00268
00269 for (var i=0, count=replacementMethods.length; i<count; i+=2)
00270 {
00271 var theSelector = sel_getName(replacementMethods[i]),
00272 theReplacementMethod = replacementMethods[i+1];
00273
00274 if ([_nativeClass instancesRespondToSelector:theSelector])
00275 {
00276 var theMethod = class_getInstanceMethod(_nativeClass, theSelector);
00277
00278 class_addMethod(_targetObject.isa, theSelector, theReplacementMethod(aKey, theMethod), "");
00279 }
00280 }
00281
00282 var affectingKeys = [[_nativeClass keyPathsForValuesAffectingValueForKey:aKey] allObjects],
00283 affectingKeysCount = affectingKeys ? affectingKeys.length : 0;
00284
00285 if (!affectingKeysCount)
00286 return;
00287
00288 var dependentKeysForClass = _nativeClass[DependentKeysKey];
00289
00290 if (!dependentKeysForClass)
00291 {
00292 dependentKeysForClass = {};
00293 _nativeClass[DependentKeysKey] = dependentKeysForClass;
00294 }
00295
00296 while (affectingKeysCount--)
00297 {
00298 var affectingKey = affectingKeys[affectingKeysCount],
00299 affectedKeys = dependentKeysForClass[affectingKey];
00300
00301 if (!affectedKeys)
00302 {
00303 affectedKeys = [CPSet new];
00304 dependentKeysForClass[affectingKey] = affectedKeys;
00305 }
00306
00307 [affectedKeys addObject:aKey];
00308 [self _replaceSetterForKey:affectingKey];
00309 }
00310 }
00311
00312 - (void)_addObserver:(id)anObserver forKeyPath:(CPString)aPath options:(unsigned)options context:(id)aContext
00313 {
00314 if (!anObserver)
00315 return;
00316
00317 var forwarder = nil;
00318
00319 if (aPath.indexOf('.') != CPNotFound)
00320 forwarder = [[_CPKVOForwardingObserver alloc] initWithKeyPath:aPath object:_targetObject observer:anObserver options:options context:aContext];
00321 else
00322 [self _replaceSetterForKey:aPath];
00323
00324 var observers = _observersForKey[aPath];
00325
00326 if (!observers)
00327 {
00328 observers = [CPDictionary dictionary];
00329 _observersForKey[aPath] = observers;
00330 _observersForKeyLength++;
00331 }
00332
00333 [observers setObject:_CPKVOInfoMake(anObserver, options, aContext, forwarder) forKey:[anObserver UID]];
00334
00335 if (options & CPKeyValueObservingOptionInitial)
00336 {
00337 var newValue = [_targetObject valueForKeyPath:aPath];
00338
00339 if (newValue === nil || newValue === undefined)
00340 newValue = [CPNull null];
00341
00342 var changes = [CPDictionary dictionaryWithObject:newValue forKey:CPKeyValueChangeNewKey];
00343 [anObserver observeValueForKeyPath:aPath ofObject:self change:changes context:aContext];
00344 }
00345 }
00346
00347 - (void)_removeObserver:(id)anObserver forKeyPath:(CPString)aPath
00348 {
00349 var observers = _observersForKey[aPath];
00350
00351 if (aPath.indexOf('.') != CPNotFound)
00352 {
00353 var forwarder = [observers objectForKey:[anObserver UID]].forwarder;
00354 [forwarder finalize];
00355 }
00356
00357 [observers removeObjectForKey:[anObserver UID]];
00358
00359 if (![observers count])
00360 {
00361 _observersForKeyLength--;
00362 delete _observersForKey[aPath];
00363 }
00364
00365 if (!_observersForKeyLength)
00366 {
00367 _targetObject.isa = _nativeClass;
00368 delete _targetObject[KVOProxyKey];
00369 }
00370 }
00371
00372
00373
00374 - (void)_sendNotificationsForKey:(CPString)aKey changeOptions:(CPDictionary)changeOptions isBefore:(BOOL)isBefore
00375 {
00376 var changes = _changesForKey[aKey];
00377
00378 if (isBefore)
00379 {
00380 changes = changeOptions;
00381
00382 var indexes = [changes objectForKey:CPKeyValueChangeIndexesKey];
00383
00384 if (indexes)
00385 {
00386 var type = [changes objectForKey:CPKeyValueChangeKindKey];
00387
00388
00389 if (type === CPKeyValueChangeReplacement || type === CPKeyValueChangeRemoval)
00390 {
00391
00392 var newValues = [[_targetObject mutableArrayValueForKeyPath:aKey] objectsAtIndexes:indexes];
00393 [changes setValue:newValues forKey:CPKeyValueChangeOldKey];
00394 }
00395 }
00396 else
00397 {
00398 var oldValue = [_targetObject valueForKey:aKey];
00399
00400 if (oldValue === nil || oldValue === undefined)
00401 oldValue = [CPNull null];
00402
00403 [changes setObject:oldValue forKey:CPKeyValueChangeOldKey];
00404 }
00405
00406 [changes setObject:1 forKey:CPKeyValueChangeNotificationIsPriorKey];
00407
00408 _changesForKey[aKey] = changes;
00409 }
00410 else
00411 {
00412 [changes removeObjectForKey:CPKeyValueChangeNotificationIsPriorKey];
00413
00414 var indexes = [changes objectForKey:CPKeyValueChangeIndexesKey];
00415
00416 if (indexes)
00417 {
00418 var type = [changes objectForKey:CPKeyValueChangeKindKey];
00419
00420
00421 if (type == CPKeyValueChangeReplacement || type == CPKeyValueChangeInsertion)
00422 {
00423
00424 var newValues = [[_targetObject mutableArrayValueForKeyPath:aKey] objectsAtIndexes:indexes];
00425 [changes setValue:newValues forKey:CPKeyValueChangeNewKey];
00426 }
00427 }
00428 else
00429 {
00430 var newValue = [_targetObject valueForKey:aKey];
00431
00432 if (newValue === nil || newValue === undefined)
00433 newValue = [CPNull null];
00434
00435 [changes setObject:newValue forKey:CPKeyValueChangeNewKey];
00436 }
00437 }
00438
00439 var observers = [_observersForKey[aKey] allValues],
00440 count = observers ? observers.length : 0;
00441
00442 while (count--)
00443 {
00444 var observerInfo = observers[count];
00445
00446 if (isBefore && (observerInfo.options & CPKeyValueObservingOptionPrior))
00447 [observerInfo.observer observeValueForKeyPath:aKey ofObject:_targetObject change:changes context:observerInfo.context];
00448 else if (!isBefore)
00449 [observerInfo.observer observeValueForKeyPath:aKey ofObject:_targetObject change:changes context:observerInfo.context];
00450 }
00451
00452 var dependentKeysMap = _nativeClass[DependentKeysKey];
00453
00454 if (!dependentKeysMap)
00455 return;
00456
00457 var dependentKeyPaths = [dependentKeysMap[aKey] allObjects];
00458
00459 if (!dependentKeyPaths)
00460 return;
00461
00462 var index = 0,
00463 count = [dependentKeyPaths count];
00464
00465 for (; index < count; ++index)
00466 {
00467 var keyPath = dependentKeyPaths[index];
00468
00469 [self _sendNotificationsForKey:keyPath
00470 changeOptions:isBefore ? [changeOptions copy] : _changesForKey[keyPath]
00471 isBefore:isBefore];
00472 }
00473 }
00474
00475 @end
00476
00477 @implementation _CPKVOModelSubclass
00478 {
00479 }
00480
00481 - (void)willChangeValueForKey:(CPString)aKey
00482 {
00483 if (!aKey)
00484 return;
00485
00486 var changeOptions = [CPDictionary dictionaryWithObject:CPKeyValueChangeSetting forKey:CPKeyValueChangeKindKey];
00487
00488 [[_CPKVOProxy proxyForObject:self] _sendNotificationsForKey:aKey changeOptions:changeOptions isBefore:YES];
00489 }
00490
00491 - (void)didChangeValueForKey:(CPString)aKey
00492 {
00493 if (!aKey)
00494 return;
00495
00496 [[_CPKVOProxy proxyForObject:self] _sendNotificationsForKey:aKey changeOptions:nil isBefore:NO];
00497 }
00498
00499 - (void)willChange:(CPKeyValueChange)change valuesAtIndexes:(CPIndexSet)indexes forKey:(CPString)aKey
00500 {
00501 if (!aKey)
00502 return;
00503
00504 var changeOptions = [CPDictionary dictionaryWithObjects:[change, indexes] forKeys:[CPKeyValueChangeKindKey, CPKeyValueChangeIndexesKey]];
00505
00506 [[_CPKVOProxy proxyForObject:self] _sendNotificationsForKey:aKey changeOptions:changeOptions isBefore:YES];
00507 }
00508
00509 - (void)didChange:(CPKeyValueChange)change valuesAtIndexes:(CPIndexSet)indexes forKey:(CPString)aKey
00510 {
00511 if (!aKey)
00512 return;
00513
00514 [[_CPKVOProxy proxyForObject:self] _sendNotificationsForKey:aKey changeOptions:nil isBefore:NO];
00515 }
00516
00517 - (Class)class
00518 {
00519 return self[KVOProxyKey]._nativeClass;
00520 }
00521
00522 - (Class)superclass
00523 {
00524 return [[self class] superclass];
00525 }
00526
00527 - (BOOL)isKindOfClass:(Class)aClass
00528 {
00529 return [[self class] isSubclassOfClass:aClass];
00530 }
00531
00532 - (BOOL)isMemberOfClass:(Class)aClass
00533 {
00534 return [self class] == aClass;
00535 }
00536
00537 - (CPString)className
00538 {
00539 return [self class].name;
00540 }
00541
00542 @end
00543
00544 @implementation _CPKVOForwardingObserver : CPObject
00545 {
00546 id _object;
00547 id _observer;
00548 id _context;
00549
00550 CPString _firstPart;
00551 CPString _secondPart;
00552
00553 id _value;
00554 }
00555
00556 - (id)initWithKeyPath:(CPString)aKeyPath object:(id)anObject observer:(id)anObserver options:(unsigned)options context:(id)aContext
00557 {
00558 self = [super init];
00559
00560 _context = aContext;
00561 _observer = anObserver;
00562 _object = anObject;
00563
00564
00565
00566 var dotIndex = aKeyPath.indexOf('.');
00567
00568 if (dotIndex == CPNotFound)
00569 [CPException raise:CPInvalidArgumentException reason:"Created _CPKVOForwardingObserver without compound key path: "+aKeyPath];
00570
00571 _firstPart = aKeyPath.substring(0, dotIndex);
00572 _secondPart = aKeyPath.substring(dotIndex+1);
00573
00574
00575 [_object addObserver:self forKeyPath:_firstPart options:kvoNewAndOld context:nil];
00576
00577
00578 _value = [_object valueForKey:_firstPart];
00579
00580 if (_value)
00581 [_value addObserver:self forKeyPath:_secondPart options:kvoNewAndOld context:nil];
00582
00583 return self;
00584 }
00585
00586 - (void)observeValueForKeyPath:(CPString)aKeyPath ofObject:(id)anObject change:(CPDictionary)changes context:(id)aContext
00587 {
00588 if (aKeyPath === _firstPart)
00589 {
00590 [_observer observeValueForKeyPath:_firstPart ofObject:_object change:changes context:_context];
00591
00592
00593 if (_value)
00594 [_value removeObserver:self forKeyPath:_secondPart];
00595
00596 _value = [_object valueForKey:_firstPart];
00597
00598 if (_value)
00599 [_value addObserver:self forKeyPath:_secondPart options:kvoNewAndOld context:nil];
00600 }
00601 else
00602 {
00603
00604 [_observer observeValueForKeyPath:_firstPart+"."+aKeyPath ofObject:_object change:changes context:_context];
00605 }
00606 }
00607
00608 - (void)finalize
00609 {
00610 if (_value)
00611 [_value removeObserver:self forKeyPath:_secondPart];
00612
00613 [_object removeObserver:self forKeyPath:_firstPart];
00614
00615 _object = nil;
00616 _observer = nil;
00617 _context = nil;
00618 _value = nil;
00619 }
00620
00621 @end
00622
00623 var _CPKVOInfoMake = function _CPKVOInfoMake(anObserver, theOptions, aContext, aForwarder)
00624 {
00625 return {
00626 observer: anObserver,
00627 options: theOptions,
00628 context: aContext,
00629 forwarder: aForwarder
00630 };
00631 }
00632
00633 var _kvoMethodForMethod = function _kvoMethodForMethod(theKey, theMethod)
00634 {
00635 return function(self, _cmd, object)
00636 {
00637 [self willChangeValueForKey:theKey];
00638 theMethod.method_imp(self, _cmd, object);
00639 [self didChangeValueForKey:theKey];
00640 }
00641 }
00642
00643 var _kvoInsertMethodForMethod = function _kvoInsertMethodForMethod(theKey, theMethod)
00644 {
00645 return function(self, _cmd, object, index)
00646 {
00647 [self willChange:CPKeyValueChangeInsertion valuesAtIndexes:[CPIndexSet indexSetWithIndex:index] forKey:theKey];
00648 theMethod.method_imp(self, _cmd, object, index);
00649 [self didChange:CPKeyValueChangeInsertion valuesAtIndexes:[CPIndexSet indexSetWithIndex:index] forKey:theKey]
00650 }
00651 }
00652
00653 var _kvoReplaceMethodForMethod = function _kvoReplaceMethodForMethod(theKey, theMethod)
00654 {
00655 return function(self, _cmd, index, object)
00656 {
00657 [self willChange:CPKeyValueChangeReplacement valuesAtIndexes:[CPIndexSet indexSetWithIndex:index] forKey:theKey];
00658 theMethod.method_imp(self, _cmd, index, object);
00659 [self didChange:CPKeyValueChangeReplacement valuesAtIndexes:[CPIndexSet indexSetWithIndex:index] forKey:theKey]
00660 }
00661 }
00662
00663 var _kvoRemoveMethodForMethod = function _kvoRemoveMethodForMethod(theKey, theMethod)
00664 {
00665 return function(self, _cmd, index)
00666 {
00667 [self willChange:CPKeyValueChangeRemoval valuesAtIndexes:[CPIndexSet indexSetWithIndex:index] forKey:theKey];
00668 theMethod.method_imp(self, _cmd, index);
00669 [self didChange:CPKeyValueChangeRemoval valuesAtIndexes:[CPIndexSet indexSetWithIndex:index] forKey:theKey]
00670 }
00671 }
00672
00673 @import "CPArray+KVO.j"