![]() |
API 0.9.5
|
00001 /* 00002 * CPKeyValueCoding.j 00003 * Foundation 00004 * 00005 * Created by Ross Boucher. 00006 * Copyright 2008, 280 North, Inc. 00007 * 00008 * This library is free software; you can redistribute it and/or 00009 * modify it under the terms of the GNU Lesser General Public 00010 * License as published by the Free Software Foundation; either 00011 * version 2.1 of the License, or (at your option) any later version. 00012 * 00013 * This library is distributed in the hope that it will be useful, 00014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00016 * Lesser General Public License for more details. 00017 * 00018 * You should have received a copy of the GNU Lesser General Public 00019 * License along with this library; if not, write to the Free Software 00020 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00021 */ 00022 00023 00024 @implementation CPObject (KeyValueObserving) 00025 00026 - (void)willChangeValueForKey:(CPString)aKey 00027 { 00028 } 00029 00030 - (void)didChangeValueForKey:(CPString)aKey 00031 { 00032 } 00033 00034 - (void)willChange:(CPKeyValueChange)aChange valuesAtIndexes:(CPIndexSet)indexes forKey:(CPString)aKey 00035 { 00036 } 00037 00038 - (void)didChange:(CPKeyValueChange)aChange valuesAtIndexes:(CPIndexSet)indexes forKey:(CPString)aKey 00039 { 00040 } 00041 00042 - (void)willChangeValueForKey:(CPString)aKey withSetMutation:(CPKeyValueSetMutationKind)aMutationKind usingObjects:(CPSet)objects 00043 { 00044 } 00045 00046 - (void)didChangeValueForKey:(CPString)aKey withSetMutation:(CPKeyValueSetMutationKind)aMutationKind usingObjects:(CPSet)objects 00047 { 00048 } 00049 00050 - (void)addObserver:(id)anObserver forKeyPath:(CPString)aPath options:(unsigned)options context:(id)aContext 00051 { 00052 if (!anObserver || !aPath) 00053 return; 00054 00055 [[_CPKVOProxy proxyForObject:self] _addObserver:anObserver forKeyPath:aPath options:options context:aContext]; 00056 } 00057 00058 - (void)removeObserver:(id)anObserver forKeyPath:(CPString)aPath 00059 { 00060 if (!anObserver || !aPath) 00061 return; 00062 00063 [self[KVOProxyKey] _removeObserver:anObserver forKeyPath:aPath]; 00064 } 00065 00066 + (BOOL)automaticallyNotifiesObserversForKey:(CPString)aKey 00067 { 00068 return YES; 00069 } 00070 00071 + (CPSet)keyPathsForValuesAffectingValueForKey:(CPString)aKey 00072 { 00073 var capitalizedKey = aKey.charAt(0).toUpperCase() + aKey.substring(1), 00074 selector = "keyPathsForValuesAffecting" + capitalizedKey; 00075 00076 if ([[self class] respondsToSelector:selector]) 00077 return objj_msgSend([self class], selector); 00078 00079 return [CPSet set]; 00080 } 00081 00082 - (void)applyChange:(CPDictionary)aChange toKeyPath:(CPString)aKeyPath 00083 { 00084 var changeKind = [aChange objectForKey:CPKeyValueChangeKindKey], 00085 oldValue = [aChange objectForKey:CPKeyValueChangeOldKey], 00086 newValue = [aChange objectForKey:CPKeyValueChangeNewKey]; 00087 00088 if (newValue === [CPNull null]) 00089 newValue = nil; 00090 00091 if (changeKind === CPKeyValueChangeSetting) 00092 return [self setValue:newValue forKeyPath:aKeyPath]; 00093 00094 var indexes = [aChange objectForKey:CPKeyValueChangeIndexesKey]; 00095 00096 // If we have an indexes entry, then we have an ordered to-many relationship 00097 if (indexes) 00098 { 00099 if (changeKind === CPKeyValueChangeInsertion) 00100 [[self mutableArrayValueForKeyPath:aKeyPath] insertObjects:newValue atIndexes:indexes]; 00101 00102 else if (changeKind === CPKeyValueChangeRemoval) 00103 [[self mutableArrayValueForKeyPath:aKeyPath] removeObjectsAtIndexes:indexes]; 00104 00105 else if (changeKind === CPKeyValueChangeReplacement) 00106 [[self mutableArrayValueForKeyPath:aKeyPath] replaceObjectAtIndexes:indexes withObjects:newValue]; 00107 } 00108 else 00109 { 00110 if (changeKind === CPKeyValueChangeInsertion) 00111 [[self mutableSetValueForKeyPath:aKeyPath] unionSet:newValue]; 00112 00113 else if (changeKind === CPKeyValueChangeRemoval) 00114 [[self mutableSetValueForKeyPath:aKeyPath] minusSet:oldValue]; 00115 00116 else if (changeKind === CPKeyValueChangeReplacement) 00117 [[self mutableSetValueForKeyPath:aKeyPath] setSet:newValue]; 00118 } 00119 } 00120 00121 @end 00122 00123 @implementation CPDictionary (KeyValueObserving) 00124 00125 - (CPDictionary)inverseChangeDictionary 00126 { 00127 var inverseChangeDictionary = [self mutableCopy], 00128 changeKind = [self objectForKey:CPKeyValueChangeKindKey]; 00129 00130 if (changeKind === CPKeyValueChangeSetting || changeKind === CPKeyValueChangeReplacement) 00131 { 00132 [inverseChangeDictionary 00133 setObject:[self objectForKey:CPKeyValueChangeOldKey] 00134 forKey:CPKeyValueChangeNewKey]; 00135 00136 [inverseChangeDictionary 00137 setObject:[self objectForKey:CPKeyValueChangeNewKey] 00138 forKey:CPKeyValueChangeOldKey]; 00139 } 00140 00141 else if (changeKind === CPKeyValueChangeInsertion) 00142 { 00143 [inverseChangeDictionary 00144 setObject:CPKeyValueChangeRemoval 00145 forKey:CPKeyValueChangeKindKey]; 00146 00147 [inverseChangeDictionary 00148 setObject:[self objectForKey:CPKeyValueChangeNewKey] 00149 forKey:CPKeyValueChangeOldKey]; 00150 00151 [inverseChangeDictionary removeObjectForKey:CPKeyValueChangeNewKey]; 00152 } 00153 00154 else if (changeKind === CPKeyValueChangeRemoval) 00155 { 00156 [inverseChangeDictionary 00157 setObject:CPKeyValueChangeInsertion 00158 forKey:CPKeyValueChangeKindKey]; 00159 00160 [inverseChangeDictionary 00161 setObject:[self objectForKey:CPKeyValueChangeOldKey] 00162 forKey:CPKeyValueChangeNewKey]; 00163 00164 [inverseChangeDictionary removeObjectForKey:CPKeyValueChangeOldKey]; 00165 } 00166 00167 return inverseChangeDictionary; 00168 } 00169 00170 @end 00171 00172 // KVO Options 00173 CPKeyValueObservingOptionNew = 1 << 0; 00174 CPKeyValueObservingOptionOld = 1 << 1; 00175 CPKeyValueObservingOptionInitial = 1 << 2; 00176 CPKeyValueObservingOptionPrior = 1 << 3; 00177 00178 // KVO Change Dictionary Keys 00179 CPKeyValueChangeKindKey = @"CPKeyValueChangeKindKey"; 00180 CPKeyValueChangeNewKey = @"CPKeyValueChangeNewKey"; 00181 CPKeyValueChangeOldKey = @"CPKeyValueChangeOldKey"; 00182 CPKeyValueChangeIndexesKey = @"CPKeyValueChangeIndexesKey"; 00183 CPKeyValueChangeNotificationIsPriorKey = @"CPKeyValueChangeNotificationIsPriorKey"; 00184 00185 // KVO Change Types 00186 CPKeyValueChangeSetting = 1; 00187 CPKeyValueChangeInsertion = 2; 00188 CPKeyValueChangeRemoval = 3; 00189 CPKeyValueChangeReplacement = 4; 00190 00191 // CPKeyValueSetMutationKind 00192 CPKeyValueUnionSetMutation = 1; 00193 CPKeyValueMinusSetMutation = 2; 00194 CPKeyValueIntersectSetMutation = 3; 00195 CPKeyValueSetSetMutation = 4; 00196 00197 //FIXME: "secret" dict ivar-keys are workaround to support unordered to-many relationships without too many modifications 00198 _CPKeyValueChangeSetMutationObjectsKey = @"_CPKeyValueChangeSetMutationObjectsKey"; 00199 _CPKeyValueChangeSetMutationKindKey = @"_CPKeyValueChangeSetMutationKindKey"; 00200 _CPKeyValueChangeSetMutationNewValueKey = @"_CPKeyValueChangeSetMutationNewValueKey"; 00201 00202 var _changeKindForSetMutationKind = function(mutationKind) 00203 { 00204 switch (mutationKind) 00205 { 00206 case CPKeyValueUnionSetMutation: return CPKeyValueChangeInsertion; 00207 case CPKeyValueMinusSetMutation: return CPKeyValueChangeRemoval; 00208 case CPKeyValueIntersectSetMutation: return CPKeyValueChangeRemoval; 00209 case CPKeyValueSetSetMutation: return CPKeyValueChangeReplacement; 00210 } 00211 } 00212 00213 var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOptionOld, 00214 DependentKeysKey = "$KVODEPENDENT", 00215 KVOProxyKey = "$KVOPROXY"; 00216 00217 //rule of thumb: _ methods are called on the real proxy object, others are called on the "fake" proxy object (aka the real object) 00218 00219 /* @ignore */ 00220 @implementation _CPKVOProxy : CPObject 00221 { 00222 id _targetObject; 00223 Class _nativeClass; 00224 CPDictionary _changesForKey; 00225 CPDictionary _nestingForKey; 00226 Object _observersForKey; 00227 int _observersForKeyLength; 00228 CPSet _replacedKeys; 00229 } 00230 00231 + (id)proxyForObject:(CPObject)anObject 00232 { 00233 var proxy = anObject[KVOProxyKey]; 00234 00235 if (proxy) 00236 return proxy; 00237 00238 return [[self alloc] initWithTarget:anObject]; 00239 } 00240 00241 - (id)initWithTarget:(id)aTarget 00242 { 00243 if (self = [super init]) 00244 { 00245 _targetObject = aTarget; 00246 _nativeClass = [aTarget class]; 00247 _observersForKey = {}; 00248 _changesForKey = {}; 00249 _nestingForKey = {}; 00250 _observersForKeyLength = 0; 00251 00252 [self _replaceClass]; 00253 aTarget[KVOProxyKey] = self; 00254 } 00255 return self; 00256 } 00257 00258 - (void)_replaceClass 00259 { 00260 var currentClass = _nativeClass, 00261 kvoClassName = "$KVO_" + class_getName(_nativeClass), 00262 existingKVOClass = objj_lookUpClass(kvoClassName); 00263 00264 if (existingKVOClass) 00265 { 00266 _targetObject.isa = existingKVOClass; 00267 _replacedKeys = existingKVOClass._replacedKeys; 00268 return; 00269 } 00270 00271 var kvoClass = objj_allocateClassPair(currentClass, kvoClassName); 00272 00273 objj_registerClassPair(kvoClass); 00274 00275 _replacedKeys = [CPSet set]; 00276 kvoClass._replacedKeys = _replacedKeys; 00277 00278 //copy in the methods from our model subclass 00279 var methods = class_copyMethodList(_CPKVOModelSubclass); 00280 00281 if ([_targetObject isKindOfClass:[CPDictionary class]]) 00282 methods = methods.concat(class_copyMethodList(_CPKVOModelDictionarySubclass)); 00283 00284 class_addMethods(kvoClass, methods); 00285 00286 _targetObject.isa = kvoClass; 00287 } 00288 00289 - (void)_replaceModifiersForKey:(CPString)aKey 00290 { 00291 if ([_replacedKeys containsObject:aKey] || ![_nativeClass automaticallyNotifiesObserversForKey:aKey]) 00292 return; 00293 00294 [_replacedKeys addObject:aKey]; 00295 00296 var theClass = _nativeClass, 00297 KVOClass = _targetObject.isa, 00298 capitalizedKey = aKey.charAt(0).toUpperCase() + aKey.substring(1); 00299 00300 // Attribute and To-One Relationships 00301 var setKey_selector = sel_getUid("set" + capitalizedKey + ":"), 00302 setKey_method = class_getInstanceMethod(theClass, setKey_selector); 00303 00304 if (setKey_method) 00305 { 00306 var setKey_method_imp = setKey_method.method_imp; 00307 00308 class_addMethod(KVOClass, setKey_selector, function(self, _cmd, anObject) 00309 { 00310 [self willChangeValueForKey:aKey]; 00311 00312 setKey_method_imp(self, _cmd, anObject); 00313 00314 [self didChangeValueForKey:aKey]; 00315 }, ""); 00316 } 00317 00318 // FIXME: Deprecated. 00319 var _setKey_selector = sel_getUid("_set" + capitalizedKey + ":"), 00320 _setKey_method = class_getInstanceMethod(theClass, _setKey_selector); 00321 00322 if (_setKey_method) 00323 { 00324 var _setKey_method_imp = _setKey_method.method_imp; 00325 00326 class_addMethod(KVOClass, _setKey_selector, function(self, _cmd, anObject) 00327 { 00328 [self willChangeValueForKey:aKey]; 00329 00330 _setKey_method_imp(self, _cmd, anObject); 00331 00332 [self didChangeValueForKey:aKey]; 00333 }, ""); 00334 } 00335 00336 // Ordered To-Many Relationships 00337 var insertObject_inKeyAtIndex_selector = sel_getUid("insertObject:in" + capitalizedKey + "AtIndex:"), 00338 insertObject_inKeyAtIndex_method = 00339 class_getInstanceMethod(theClass, insertObject_inKeyAtIndex_selector), 00340 00341 insertKey_atIndexes_selector = sel_getUid("insert" + capitalizedKey + ":atIndexes:"), 00342 insertKey_atIndexes_method = 00343 class_getInstanceMethod(theClass, insertKey_atIndexes_selector), 00344 00345 removeObjectFromKeyAtIndex_selector = sel_getUid("removeObjectFrom" + capitalizedKey + "AtIndex:"), 00346 removeObjectFromKeyAtIndex_method = 00347 class_getInstanceMethod(theClass, removeObjectFromKeyAtIndex_selector), 00348 00349 removeKeyAtIndexes_selector = sel_getUid("remove" + capitalizedKey + "AtIndexes:"), 00350 removeKeyAtIndexes_method = class_getInstanceMethod(theClass, removeKeyAtIndexes_selector); 00351 00352 if ((insertObject_inKeyAtIndex_method || insertKey_atIndexes_method) && 00353 (removeObjectFromKeyAtIndex_method || removeKeyAtIndexes_method)) 00354 { 00355 if (insertObject_inKeyAtIndex_method) 00356 { 00357 var insertObject_inKeyAtIndex_method_imp = insertObject_inKeyAtIndex_method.method_imp; 00358 00359 class_addMethod(KVOClass, insertObject_inKeyAtIndex_selector, function(self, _cmd, anObject, anIndex) 00360 { 00361 [self willChange:CPKeyValueChangeInsertion 00362 valuesAtIndexes:[CPIndexSet indexSetWithIndex:anIndex] 00363 forKey:aKey]; 00364 00365 insertObject_inKeyAtIndex_method_imp(self, _cmd, anObject, anIndex); 00366 00367 [self didChange:CPKeyValueChangeInsertion 00368 valuesAtIndexes:[CPIndexSet indexSetWithIndex:anIndex] 00369 forKey:aKey]; 00370 }, ""); 00371 } 00372 00373 if (insertKey_atIndexes_method) 00374 { 00375 var insertKey_atIndexes_method_imp = insertKey_atIndexes_method.method_imp; 00376 00377 class_addMethod(KVOClass, insertKey_atIndexes_selector, function(self, _cmd, objects, indexes) 00378 { 00379 [self willChange:CPKeyValueChangeInsertion 00380 valuesAtIndexes:[indexes copy] 00381 forKey:aKey]; 00382 00383 insertKey_atIndexes_method_imp(self, _cmd, objects, indexes); 00384 00385 [self didChange:CPKeyValueChangeInsertion 00386 valuesAtIndexes:[indexes copy] 00387 forKey:aKey]; 00388 }, ""); 00389 } 00390 00391 if (removeObjectFromKeyAtIndex_method) 00392 { 00393 var removeObjectFromKeyAtIndex_method_imp = removeObjectFromKeyAtIndex_method.method_imp; 00394 00395 class_addMethod(KVOClass, removeObjectFromKeyAtIndex_selector, function(self, _cmd, anIndex) 00396 { 00397 [self willChange:CPKeyValueChangeRemoval 00398 valuesAtIndexes:[CPIndexSet indexSetWithIndex:anIndex] 00399 forKey:aKey]; 00400 00401 removeObjectFromKeyAtIndex_method_imp(self, _cmd, anIndex); 00402 00403 [self didChange:CPKeyValueChangeRemoval 00404 valuesAtIndexes:[CPIndexSet indexSetWithIndex:anIndex] 00405 forKey:aKey]; 00406 }, ""); 00407 } 00408 00409 if (removeKeyAtIndexes_method) 00410 { 00411 var removeKeyAtIndexes_method_imp = removeKeyAtIndexes_method.method_imp; 00412 00413 class_addMethod(KVOClass, removeKeyAtIndexes_selector, function(self, _cmd, indexes) 00414 { 00415 [self willChange:CPKeyValueChangeRemoval 00416 valuesAtIndexes:[indexes copy] 00417 forKey:aKey]; 00418 00419 removeKeyAtIndexes_method_imp(self, _cmd, indexes); 00420 00421 [self didChange:CPKeyValueChangeRemoval 00422 valuesAtIndexes:[indexes copy] 00423 forKey:aKey]; 00424 }, ""); 00425 } 00426 00427 // These are optional. 00428 var replaceObjectInKeyAtIndex_withObject_selector = 00429 sel_getUid("replaceObjectIn" + capitalizedKey + "AtIndex:withObject:"), 00430 replaceObjectInKeyAtIndex_withObject_method = 00431 class_getInstanceMethod(theClass, replaceObjectInKeyAtIndex_withObject_selector); 00432 00433 if (replaceObjectInKeyAtIndex_withObject_method) 00434 { 00435 var replaceObjectInKeyAtIndex_withObject_method_imp = 00436 replaceObjectInKeyAtIndex_withObject_method.method_imp; 00437 00438 class_addMethod(KVOClass, replaceObjectInKeyAtIndex_withObject_selector, 00439 function(self, _cmd, anIndex, anObject) 00440 { 00441 [self willChange:CPKeyValueChangeReplacement 00442 valuesAtIndexes:[CPIndexSet indexSetWithIndex:anIndex] 00443 forKey:aKey]; 00444 00445 replaceObjectInKeyAtIndex_withObject_method_imp(self, _cmd, anIndex, anObject); 00446 00447 [self didChange:CPKeyValueChangeReplacement 00448 valuesAtIndexes:[CPIndexSet indexSetWithIndex:anIndex] 00449 forKey:aKey]; 00450 }, ""); 00451 } 00452 00453 var replaceKeyAtIndexes_withKey_selector = 00454 sel_getUid("replace" + capitalizedKey + "AtIndexes:with" + capitalizedKey + ":"), 00455 replaceKeyAtIndexes_withKey_method = 00456 class_getInstanceMethod(theClass, replaceKeyAtIndexes_withKey_selector); 00457 00458 if (replaceKeyAtIndexes_withKey_method) 00459 { 00460 var replaceKeyAtIndexes_withKey_method_imp = replaceKeyAtIndexes_withKey_method.method_imp; 00461 00462 class_addMethod(KVOClass, replaceKeyAtIndexes_withKey_selector, function(self, _cmd, indexes, objects) 00463 { 00464 [self willChange:CPKeyValueChangeReplacement 00465 valuesAtIndexes:[indexes copy] 00466 forKey:aKey]; 00467 00468 replaceObjectInKeyAtIndex_withObject_method_imp(self, _cmd, indexes, objects); 00469 00470 [self didChange:CPKeyValueChangeReplacement 00471 valuesAtIndexes:[indexes copy] 00472 forKey:aKey]; 00473 }, ""); 00474 } 00475 } 00476 00477 // Unordered To-Many Relationships 00478 var addKeyObject_selector = sel_getUid("add" + capitalizedKey + "Object:"), 00479 addKeyObject_method = class_getInstanceMethod(theClass, addKeyObject_selector), 00480 00481 addKey_selector = sel_getUid("add" + capitalizedKey + ":"), 00482 addKey_method = class_getInstanceMethod(theClass, addKey_selector), 00483 00484 removeKeyObject_selector = sel_getUid("remove" + capitalizedKey + "Object:"), 00485 removeKeyObject_method = class_getInstanceMethod(theClass, removeKeyObject_selector), 00486 00487 removeKey_selector = sel_getUid("remove" + capitalizedKey + ":"), 00488 removeKey_method = class_getInstanceMethod(theClass, removeKey_selector); 00489 00490 if ((addKeyObject_method || addKey_method) && (removeKeyObject_method || removeKey_method)) 00491 { 00492 if (addKeyObject_method) 00493 { 00494 var addKeyObject_method_imp = addKeyObject_method.method_imp; 00495 00496 class_addMethod(KVOClass, addKeyObject_selector, function(self, _cmd, anObject) 00497 { 00498 [self willChangeValueForKey:aKey 00499 withSetMutation:CPKeyValueUnionSetMutation 00500 usingObjects:[CPSet setWithObject:anObject]]; 00501 00502 addKeyObject_method_imp(self, _cmd, anObject); 00503 00504 [self didChangeValueForKey:aKey 00505 withSetMutation:CPKeyValueUnionSetMutation 00506 usingObjects:[CPSet setWithObject:anObject]]; 00507 }, ""); 00508 } 00509 00510 if (addKey_method) 00511 { 00512 var addKey_method_imp = addKey_method.method_imp; 00513 00514 class_addMethod(KVOClass, addKey_selector, function(self, _cmd, objects) 00515 { 00516 [self willChangeValueForKey:aKey 00517 withSetMutation:CPKeyValueUnionSetMutation 00518 usingObjects:[objects copy]]; 00519 00520 addKey_method_imp(self, _cmd, objects); 00521 00522 [self didChangeValueForKey:aKey 00523 withSetMutation:CPKeyValueUnionSetMutation 00524 usingObjects:[objects copy]]; 00525 }, ""); 00526 } 00527 00528 if (removeKeyObject_method) 00529 { 00530 var removeKeyObject_method_imp = removeKeyObject_method.method_imp; 00531 00532 class_addMethod(KVOClass, removeKeyObject_selector, function(self, _cmd, anObject) 00533 { 00534 [self willChangeValueForKey:aKey 00535 withSetMutation:CPKeyValueMinusSetMutation 00536 usingObjects:[CPSet setWithObject:anObject]]; 00537 00538 removeKeyObject_method_imp(self, _cmd, anObject); 00539 00540 [self didChangeValueForKey:aKey 00541 withSetMutation:CPKeyValueMinusSetMutation 00542 usingObjects:[CPSet setWithObject:anObject]]; 00543 }, ""); 00544 } 00545 00546 if (removeKey_method) 00547 { 00548 var removeKey_method_imp = removeKey_method.method_imp; 00549 00550 class_addMethod(KVOClass, removeKey_selector, function(self, _cmd, objects) 00551 { 00552 [self willChangeValueForKey:aKey 00553 withSetMutation:CPKeyValueMinusSetMutation 00554 usingObjects:[objects copy]]; 00555 00556 removeKey_method_imp(self, _cmd, objects); 00557 00558 [self didChangeValueForKey:aKey 00559 withSetMutation:CPKeyValueMinusSetMutation 00560 usingObjects:[objects copy]]; 00561 }, ""); 00562 } 00563 00564 // intersect<Key>: is optional. 00565 var intersectKey_selector = sel_getUid("intersect" + capitalizedKey + ":"), 00566 intersectKey_method = class_getInstanceMethod(theClass, intersectKey_selector); 00567 00568 if (intersectKey_method) 00569 { 00570 var intersectKey_method_imp = intersectKey_method.method_imp; 00571 00572 class_addMethod(KVOClass, intersectKey_selector, function(self, _cmd, aSet) 00573 { 00574 [self willChangeValueForKey:aKey 00575 withSetMutation:CPKeyValueIntersectSetMutation 00576 usingObjects:[aSet copy]]; 00577 00578 intersectKey_method_imp(self, _cmd, aSet); 00579 00580 [self didChangeValueForKey:aKey 00581 withSetMutation:CPKeyValueIntersectSetMutation 00582 usingObjects:[aSet copy]]; 00583 }, ""); 00584 } 00585 } 00586 00587 var affectingKeys = [[_nativeClass keyPathsForValuesAffectingValueForKey:aKey] allObjects], 00588 affectingKeysCount = affectingKeys ? affectingKeys.length : 0; 00589 00590 if (!affectingKeysCount) 00591 return; 00592 00593 var dependentKeysForClass = _nativeClass[DependentKeysKey]; 00594 00595 if (!dependentKeysForClass) 00596 { 00597 dependentKeysForClass = {}; 00598 _nativeClass[DependentKeysKey] = dependentKeysForClass; 00599 } 00600 00601 while (affectingKeysCount--) 00602 { 00603 var affectingKey = affectingKeys[affectingKeysCount], 00604 affectedKeys = dependentKeysForClass[affectingKey]; 00605 00606 if (!affectedKeys) 00607 { 00608 affectedKeys = [CPSet new]; 00609 dependentKeysForClass[affectingKey] = affectedKeys; 00610 } 00611 00612 [affectedKeys addObject:aKey]; 00613 00614 //observe key paths of objects other then ourselves, so we are notified of the changes 00615 //use CPKeyValueObservingOptionPrior to ensure proper wrapping around changes 00616 //so CPKeyValueObservingOptionPrior and CPKeyValueObservingOptionOld can be fulfilled even for dependent keys 00617 if (affectingKey.indexOf(@".") !== -1) 00618 [_targetObject addObserver:self forKeyPath:affectingKey options:CPKeyValueObservingOptionPrior | kvoNewAndOld context:nil]; 00619 else 00620 [self _replaceModifiersForKey:affectingKey]; 00621 } 00622 } 00623 00624 - (void)observeValueForKeyPath:(CPString)theKeyPath ofObject:(id)theObject change:(CPDictionary)theChanges context:(id)theContext 00625 { 00626 // Fire change events for the dependent keys 00627 var dependentKeysForClass = _nativeClass[DependentKeysKey], 00628 dependantKeys = [dependentKeysForClass[theKeyPath] allObjects]; 00629 00630 var isBeforeFlag = !![theChanges objectForKey:CPKeyValueChangeNotificationIsPriorKey]; 00631 for (var i = 0; i < [dependantKeys count]; i++) 00632 { 00633 var dependantKey = [dependantKeys objectAtIndex:i]; 00634 [self _sendNotificationsForKey:dependantKey changeOptions:theChanges isBefore:isBeforeFlag]; 00635 } 00636 } 00637 00638 - (void)_addObserver:(id)anObserver forKeyPath:(CPString)aPath options:(unsigned)options context:(id)aContext 00639 { 00640 if (!anObserver) 00641 return; 00642 00643 var forwarder = nil; 00644 00645 if (aPath.indexOf('.') != CPNotFound) 00646 forwarder = [[_CPKVOForwardingObserver alloc] initWithKeyPath:aPath object:_targetObject observer:anObserver options:options context:aContext]; 00647 else 00648 [self _replaceModifiersForKey:aPath]; 00649 00650 var observers = _observersForKey[aPath]; 00651 00652 if (!observers) 00653 { 00654 observers = [CPDictionary dictionary]; 00655 _observersForKey[aPath] = observers; 00656 _observersForKeyLength++; 00657 } 00658 00659 [observers setObject:_CPKVOInfoMake(anObserver, options, aContext, forwarder) forKey:[anObserver UID]]; 00660 00661 if (options & CPKeyValueObservingOptionInitial) 00662 { 00663 var newValue = [_targetObject valueForKeyPath:aPath]; 00664 00665 if (newValue === nil || newValue === undefined) 00666 newValue = [CPNull null]; 00667 00668 var changes = [CPDictionary dictionaryWithObject:newValue forKey:CPKeyValueChangeNewKey]; 00669 [anObserver observeValueForKeyPath:aPath ofObject:_targetObject change:changes context:aContext]; 00670 } 00671 } 00672 00673 - (void)_removeObserver:(id)anObserver forKeyPath:(CPString)aPath 00674 { 00675 var observers = _observersForKey[aPath]; 00676 00677 if (!observers) 00678 { 00679 CPLog.warn(@"Cannot remove an observer %@ for the key path \"%@\" from %@ because it is not registered as an observer.", 00680 _targetObject, aPath, anObserver); 00681 00682 return; 00683 } 00684 00685 if (aPath.indexOf('.') != CPNotFound) 00686 { 00687 var forwarder = [observers objectForKey:[anObserver UID]].forwarder; 00688 [forwarder finalize]; 00689 } 00690 00691 [observers removeObjectForKey:[anObserver UID]]; 00692 00693 if (![observers count]) 00694 { 00695 _observersForKeyLength--; 00696 delete _observersForKey[aPath]; 00697 } 00698 00699 if (!_observersForKeyLength) 00700 { 00701 _targetObject.isa = _nativeClass; //restore the original class 00702 delete _targetObject[KVOProxyKey]; 00703 } 00704 } 00705 00706 //FIXME: We do not compute and cache if CPKeyValueObservingOptionOld is needed, so we may do unnecessary work 00707 00708 - (void)_sendNotificationsForKey:(CPString)aKey changeOptions:(CPDictionary)changeOptions isBefore:(BOOL)isBefore 00709 { 00710 var changes = _changesForKey[aKey]; 00711 00712 if (isBefore) 00713 { 00714 if (changes) 00715 { 00716 // "willChange:X" nesting. 00717 var level = _nestingForKey[aKey]; 00718 if (!level) 00719 [CPException raise:CPInternalInconsistencyException reason:@"_changesForKey without _nestingForKey"]; 00720 _nestingForKey[aKey] = level + 1; 00721 // Only notify on the first willChange..., silently note any following nested calls. 00722 return; 00723 } 00724 _nestingForKey[aKey] = 1; 00725 00726 changes = changeOptions; 00727 00728 var indexes = [changes objectForKey:CPKeyValueChangeIndexesKey], 00729 setMutationKind = changes[_CPKeyValueChangeSetMutationKindKey]; 00730 00731 if (setMutationKind) 00732 { 00733 var setMutationObjects = [changes[_CPKeyValueChangeSetMutationObjectsKey] copy], 00734 setExistingObjects = [[_targetObject valueForKey: aKey] copy]; 00735 00736 if (setMutationKind == CPKeyValueMinusSetMutation) 00737 { 00738 [setExistingObjects intersectSet: setMutationObjects]; 00739 [changes setValue:setExistingObjects forKey:CPKeyValueChangeOldKey]; 00740 } 00741 else if (setMutationKind === CPKeyValueIntersectSetMutation || setMutationKind === CPKeyValueSetSetMutation) 00742 { 00743 [setExistingObjects minusSet: setMutationObjects]; 00744 [changes setValue:setExistingObjects forKey:CPKeyValueChangeOldKey]; 00745 } 00746 00747 //for unordered to-many relationships (CPSet) even new values can only be calculated before!!! 00748 if (setMutationKind === CPKeyValueUnionSetMutation || setMutationKind === CPKeyValueSetSetMutation) 00749 { 00750 [setMutationObjects minusSet: setExistingObjects]; 00751 //hide new value (for CPKeyValueObservingOptionPrior messages) 00752 //as long as "didChangeValue..." is not yet called! 00753 changes[_CPKeyValueChangeSetMutationNewValueKey] = setMutationObjects; 00754 } 00755 } 00756 else if (indexes) 00757 { 00758 var type = [changes objectForKey:CPKeyValueChangeKindKey]; 00759 // for ordered to-many relationships, oldvalue is only sensible for replace and remove 00760 if (type === CPKeyValueChangeReplacement || type === CPKeyValueChangeRemoval) 00761 { 00762 //FIXME: do we need to go through and replace "" with CPNull? 00763 var newValues = [[_targetObject mutableArrayValueForKeyPath:aKey] objectsAtIndexes:indexes]; 00764 [changes setValue:newValues forKey:CPKeyValueChangeOldKey]; 00765 } 00766 } 00767 else 00768 { 00769 var oldValue = [_targetObject valueForKey:aKey]; 00770 00771 if (oldValue === nil || oldValue === undefined) 00772 oldValue = [CPNull null]; 00773 00774 [changes setObject:oldValue forKey:CPKeyValueChangeOldKey]; 00775 } 00776 00777 [changes setObject:1 forKey:CPKeyValueChangeNotificationIsPriorKey]; 00778 00779 _changesForKey[aKey] = changes; 00780 } 00781 else 00782 { 00783 var level = _nestingForKey[aKey]; 00784 if (!changes || !level) 00785 [CPException raise:@"CPKeyValueObservingException" reason:@"'didChange...' message called without prior call of 'willChange...'"]; 00786 00787 _nestingForKey[aKey] = level - 1; 00788 if (level - 1 > 0) 00789 { 00790 // willChange... was called multiple times. Only fire observation notifications when 00791 // didChange... has been called an equal number of times. 00792 return; 00793 } 00794 delete _nestingForKey[aKey]; 00795 00796 [changes removeObjectForKey:CPKeyValueChangeNotificationIsPriorKey]; 00797 00798 var indexes = [changes objectForKey:CPKeyValueChangeIndexesKey], 00799 setMutationKind = changes[_CPKeyValueChangeSetMutationKindKey]; 00800 00801 if (setMutationKind) 00802 { 00803 //old and new values for unordered to-many relationships can only be calculated before 00804 //set recalculated hidden new value as soon as "didChangeValue..." is called! 00805 var newValue = changes[_CPKeyValueChangeSetMutationNewValueKey]; 00806 [changes setValue:newValue forKey:CPKeyValueChangeNewKey]; 00807 00808 //delete hidden values 00809 delete changes[_CPKeyValueChangeSetMutationNewValueKey]; 00810 delete changes[_CPKeyValueChangeSetMutationObjectsKey]; 00811 delete changes[_CPKeyValueChangeSetMutationKindKey]; 00812 } 00813 else if (indexes) 00814 { 00815 var type = [changes objectForKey:CPKeyValueChangeKindKey]; 00816 // for ordered to-many relationships, newvalue is only sensible for replace and insert 00817 if (type == CPKeyValueChangeReplacement || type == CPKeyValueChangeInsertion) 00818 { 00819 //FIXME: do we need to go through and replace "" with CPNull? 00820 var newValues = [[_targetObject mutableArrayValueForKeyPath:aKey] objectsAtIndexes:indexes]; 00821 [changes setValue:newValues forKey:CPKeyValueChangeNewKey]; 00822 } 00823 } 00824 else 00825 { 00826 var newValue = [_targetObject valueForKey:aKey]; 00827 00828 if (newValue === nil || newValue === undefined) 00829 newValue = [CPNull null]; 00830 00831 [changes setObject:newValue forKey:CPKeyValueChangeNewKey]; 00832 } 00833 00834 delete _changesForKey[aKey]; 00835 } 00836 00837 var observers = [_observersForKey[aKey] allValues], 00838 count = observers ? observers.length : 0; 00839 00840 while (count--) 00841 { 00842 var observerInfo = observers[count]; 00843 00844 if (!isBefore || (observerInfo.options & CPKeyValueObservingOptionPrior)) 00845 [observerInfo.observer observeValueForKeyPath:aKey ofObject:_targetObject change:changes context:observerInfo.context]; 00846 } 00847 00848 var dependentKeysMap = _nativeClass[DependentKeysKey]; 00849 00850 if (!dependentKeysMap) 00851 return; 00852 00853 var dependentKeyPaths = [dependentKeysMap[aKey] allObjects]; 00854 00855 if (!dependentKeyPaths) 00856 return; 00857 00858 var index = 0, 00859 count = [dependentKeyPaths count]; 00860 00861 for (; index < count; ++index) 00862 { 00863 var keyPath = dependentKeyPaths[index]; 00864 00865 [self _sendNotificationsForKey:keyPath 00866 changeOptions:isBefore ? [changeOptions copy] : _changesForKey[keyPath] 00867 isBefore:isBefore]; 00868 } 00869 } 00870 00871 @end 00872 @implementation _CPKVOModelSubclass 00873 { 00874 id __doxygen__; 00875 } 00876 00877 - (void)willChangeValueForKey:(CPString)aKey 00878 { 00879 var superClass = [self class], 00880 methodSelector = @selector(willChangeValueForKey:), 00881 methodImp = class_getMethodImplementation(superClass, methodSelector); 00882 00883 methodImp(self, methodSelector, aKey); 00884 00885 if (!aKey) 00886 return; 00887 00888 var changeOptions = [CPDictionary dictionaryWithObject:CPKeyValueChangeSetting forKey:CPKeyValueChangeKindKey]; 00889 00890 [[_CPKVOProxy proxyForObject:self] _sendNotificationsForKey:aKey changeOptions:changeOptions isBefore:YES]; 00891 } 00892 00893 - (void)didChangeValueForKey:(CPString)aKey 00894 { 00895 var superClass = [self class], 00896 methodSelector = @selector(didChangeValueForKey:), 00897 methodImp = class_getMethodImplementation(superClass, methodSelector); 00898 00899 methodImp(self, methodSelector, aKey); 00900 00901 if (!aKey) 00902 return; 00903 00904 [[_CPKVOProxy proxyForObject:self] _sendNotificationsForKey:aKey changeOptions:nil isBefore:NO]; 00905 } 00906 00907 - (void)willChange:(CPKeyValueChange)change valuesAtIndexes:(CPIndexSet)indexes forKey:(CPString)aKey 00908 { 00909 var superClass = [self class], 00910 methodSelector = @selector(willChange:valuesAtIndexes:forKey:), 00911 methodImp = class_getMethodImplementation(superClass, methodSelector); 00912 00913 methodImp(self, methodSelector, change, indexes, aKey); 00914 00915 if (!aKey) 00916 return; 00917 00918 var changeOptions = [CPDictionary dictionaryWithObjects:[change, indexes] forKeys:[CPKeyValueChangeKindKey, CPKeyValueChangeIndexesKey]]; 00919 00920 [[_CPKVOProxy proxyForObject:self] _sendNotificationsForKey:aKey changeOptions:changeOptions isBefore:YES]; 00921 } 00922 00923 - (void)didChange:(CPKeyValueChange)change valuesAtIndexes:(CPIndexSet)indexes forKey:(CPString)aKey 00924 { 00925 var superClass = [self class], 00926 methodSelector = @selector(didChange:valuesAtIndexes:forKey:), 00927 methodImp = class_getMethodImplementation(superClass, methodSelector); 00928 00929 methodImp(self, methodSelector, change, indexes, aKey); 00930 00931 if (!aKey) 00932 return; 00933 00934 [[_CPKVOProxy proxyForObject:self] _sendNotificationsForKey:aKey changeOptions:nil isBefore:NO]; 00935 } 00936 00937 - (void)willChangeValueForKey:(CPString)aKey withSetMutation:(CPKeyValueSetMutationKind)mutationKind usingObjects:(CPSet)objects 00938 { 00939 var superClass = [self class], 00940 methodSelector = @selector(willChangeValueForKey:withSetMutation:usingObjects:), 00941 methodImp = class_getMethodImplementation(superClass, methodSelector); 00942 00943 methodImp(self, methodSelector, aKey, mutationKind, objects); 00944 00945 if (!aKey) 00946 return; 00947 00948 var changeKind = _changeKindForSetMutationKind(mutationKind), 00949 changeOptions = [CPDictionary dictionaryWithObject:changeKind forKey:CPKeyValueChangeKindKey]; 00950 //set hidden change-dict ivars to support unordered to-many relationships 00951 changeOptions[_CPKeyValueChangeSetMutationObjectsKey] = objects; 00952 changeOptions[_CPKeyValueChangeSetMutationKindKey] = mutationKind; 00953 00954 [[_CPKVOProxy proxyForObject:self] _sendNotificationsForKey:aKey changeOptions:changeOptions isBefore:YES]; 00955 } 00956 00957 - (void)didChangeValueForKey:(CPString)aKey withSetMutation:(CPKeyValueSetMutationKind)mutationKind usingObjects:(CPSet)objects 00958 { 00959 var superClass = [self class], 00960 methodSelector = @selector(didChangeValueForKey:withSetMutation:usingObjects:), 00961 methodImp = class_getMethodImplementation(superClass, methodSelector); 00962 00963 methodImp(self, methodSelector, aKey, mutationKind, objects); 00964 00965 if (!aKey) 00966 return; 00967 00968 [[_CPKVOProxy proxyForObject:self] _sendNotificationsForKey:aKey changeOptions:nil isBefore:NO]; 00969 } 00970 00971 - (Class)class 00972 { 00973 return self[KVOProxyKey]._nativeClass; 00974 } 00975 00976 - (Class)superclass 00977 { 00978 return [[self class] superclass]; 00979 } 00980 00981 - (BOOL)isKindOfClass:(Class)aClass 00982 { 00983 return [[self class] isSubclassOfClass:aClass]; 00984 } 00985 00986 - (BOOL)isMemberOfClass:(Class)aClass 00987 { 00988 return [self class] == aClass; 00989 } 00990 00991 - (CPString)className 00992 { 00993 return [self class].name; 00994 } 00995 00996 @end 00997 @implementation _CPKVOModelDictionarySubclass 00998 { 00999 id __doxygen__; 01000 } 01001 01002 - (void)removeAllObjects 01003 { 01004 var keys = [self allKeys], 01005 count = [keys count], 01006 i = 0; 01007 01008 for (; i < count; i++) 01009 [self willChangeValueForKey:keys[i]]; 01010 01011 var superClass = [self class], 01012 methodSelector = @selector(removeAllObjects), 01013 methodImp = class_getMethodImplementation(superClass, methodSelector); 01014 01015 methodImp(self, methodSelector); 01016 01017 for (i = 0; i < count; i++) 01018 [self didChangeValueForKey:keys[i]]; 01019 } 01020 01021 - (void)removeObjectForKey:(id)aKey 01022 { 01023 [self willChangeValueForKey:aKey]; 01024 01025 var superClass = [self class], 01026 methodSelector = @selector(removeObjectForKey:), 01027 methodImp = class_getMethodImplementation(superClass, methodSelector); 01028 01029 methodImp(self, methodSelector, aKey); 01030 01031 [self didChangeValueForKey:aKey]; 01032 } 01033 01034 - (void)setObject:(id)anObject forKey:(id)aKey 01035 { 01036 [self willChangeValueForKey:aKey]; 01037 01038 var superClass = [self class], 01039 methodSelector = @selector(setObject:forKey:), 01040 methodImp = class_getMethodImplementation(superClass, methodSelector); 01041 01042 methodImp(self, methodSelector, anObject, aKey); 01043 01044 [self didChangeValueForKey:aKey]; 01045 } 01046 01047 @end 01048 01049 @implementation _CPKVOForwardingObserver : CPObject 01050 { 01051 id _object; 01052 id _observer; 01053 id _context; 01054 unsigned _options; 01055 //a.b 01056 CPString _firstPart; //a 01057 CPString _secondPart; //b 01058 01059 id _value; 01060 } 01061 01062 - (id)initWithKeyPath:(CPString)aKeyPath object:(id)anObject observer:(id)anObserver options:(unsigned)options context:(id)aContext 01063 { 01064 self = [super init]; 01065 01066 _context = aContext; 01067 _observer = anObserver; 01068 _object = anObject; 01069 _options = options; 01070 01071 var dotIndex = aKeyPath.indexOf('.'); 01072 01073 if (dotIndex == CPNotFound) 01074 [CPException raise:CPInvalidArgumentException reason:"Created _CPKVOForwardingObserver without compound key path: "+aKeyPath]; 01075 01076 _firstPart = aKeyPath.substring(0, dotIndex); 01077 _secondPart = aKeyPath.substring(dotIndex + 1); 01078 01079 //become an observer of the first part of our key (a) 01080 [_object addObserver:self forKeyPath:_firstPart options:_options context:nil]; 01081 01082 //the current value of a (not the value of a.b) 01083 _value = [_object valueForKey:_firstPart]; 01084 01085 if (_value) 01086 [_value addObserver:self forKeyPath:_secondPart options:_options context:nil]; //we're observing b on current a 01087 01088 return self; 01089 } 01090 01091 - (void)observeValueForKeyPath:(CPString)aKeyPath ofObject:(id)anObject change:(CPDictionary)changes context:(id)aContext 01092 { 01093 if (aKeyPath === _firstPart) 01094 { 01095 [_observer observeValueForKeyPath:_firstPart ofObject:_object change:changes context:_context]; 01096 01097 //since a has changed, we should remove ourselves as an observer of the old a, and observe the new one 01098 if (_value) 01099 [_value removeObserver:self forKeyPath:_secondPart]; 01100 01101 _value = [_object valueForKey:_firstPart]; 01102 01103 if (_value) 01104 [_value addObserver:self forKeyPath:_secondPart options:_options context:nil]; 01105 } 01106 else 01107 { 01108 //a is the same, but a.b has changed -- nothing to do but forward this message along 01109 [_observer observeValueForKeyPath:_firstPart+"."+aKeyPath ofObject:_object change:changes context:_context]; 01110 } 01111 } 01112 01113 - (void)finalize 01114 { 01115 if (_value) 01116 [_value removeObserver:self forKeyPath:_secondPart]; 01117 01118 [_object removeObserver:self forKeyPath:_firstPart]; 01119 01120 _object = nil; 01121 _observer = nil; 01122 _context = nil; 01123 _value = nil; 01124 } 01125 01126 @end 01127 01128 var _CPKVOInfoMake = function _CPKVOInfoMake(anObserver, theOptions, aContext, aForwarder) 01129 { 01130 return { 01131 observer: anObserver, 01132 options: theOptions, 01133 context: aContext, 01134 forwarder: aForwarder 01135 }; 01136 } 01137