API 0.9.5
Foundation/CPKeyValueObserving.j
Go to the documentation of this file.
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 
 All Classes Files Functions Variables Defines