API  0.9.6
 All Classes Files Functions Variables Macros Groups Pages
CPKeyValueObserving.j
Go to the documentation of this file.
1 /*
2  * CPKeyValueCoding.j
3  * Foundation
4  *
5  * Created by Ross Boucher.
6  * Copyright 2008, 280 North, Inc.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  */
22 
23 
25 
26 - (void)willChangeValueForKey:(CPString)aKey
27 {
28  if (!aKey)
29  return;
30 
31  if (!self[KVOProxyKey])
32  {
33  if (!self._willChangeMessageCounter)
34  self._willChangeMessageCounter = new Object();
35 
36  if (!self._willChangeMessageCounter[aKey])
37  self._willChangeMessageCounter[aKey] = 1;
38  else
39  self._willChangeMessageCounter[aKey] += 1;
40  }
41 }
42 
43 - (void)didChangeValueForKey:(CPString)aKey
44 {
45  if (!aKey)
46  return;
47 
48  if (!self[KVOProxyKey])
49  {
50  if (self._willChangeMessageCounter && self._willChangeMessageCounter[aKey])
51  {
52  self._willChangeMessageCounter[aKey] -= 1;
53 
54  if (!self._willChangeMessageCounter[aKey])
55  delete self._willChangeMessageCounter[aKey];
56  }
57  else
58  [CPException raise:@"CPKeyValueObservingException" reason:@"'didChange...' message called without prior call of 'willChange...'"];
59  }
60 }
61 
62 - (void)willChange:(CPKeyValueChange)aChange valuesAtIndexes:(CPIndexSet)indexes forKey:(CPString)aKey
63 {
64  if (!aKey)
65  return;
66 
67  if (!self[KVOProxyKey])
68  {
69  if (!self._willChangeMessageCounter)
70  self._willChangeMessageCounter = new Object();
71 
72  if (!self._willChangeMessageCounter[aKey])
73  self._willChangeMessageCounter[aKey] = 1;
74  else
75  self._willChangeMessageCounter[aKey] += 1;
76  }
77 }
78 
79 - (void)didChange:(CPKeyValueChange)aChange valuesAtIndexes:(CPIndexSet)indexes forKey:(CPString)aKey
80 {
81  if (!aKey)
82  return;
83 
84  if (!self[KVOProxyKey])
85  {
86  if (self._willChangeMessageCounter && self._willChangeMessageCounter[aKey])
87  {
88  self._willChangeMessageCounter[aKey] -= 1;
89 
90  if (!self._willChangeMessageCounter[aKey])
91  delete self._willChangeMessageCounter[aKey];
92  }
93  else
94  [CPException raise:@"CPKeyValueObservingException" reason:@"'didChange...' message called without prior call of 'willChange...'"];
95  }
96 }
97 
98 - (void)willChangeValueForKey:(CPString)aKey withSetMutation:(CPKeyValueSetMutationKind)aMutationKind usingObjects:(CPSet)objects
99 {
100  if (!aKey)
101  return;
102 
103  if (!self[KVOProxyKey])
104  {
105  if (!self._willChangeMessageCounter)
106  self._willChangeMessageCounter = new Object();
107 
108  if (!self._willChangeMessageCounter[aKey])
109  self._willChangeMessageCounter[aKey] = 1;
110  else
111  self._willChangeMessageCounter[aKey] += 1;
112  }
113 }
114 
115 - (void)didChangeValueForKey:(CPString)aKey withSetMutation:(CPKeyValueSetMutationKind)aMutationKind usingObjects:(CPSet)objects
116 {
117  if (!self[KVOProxyKey])
118  {
119  if (self._willChangeMessageCounter && self._willChangeMessageCounter[aKey])
120  {
121  self._willChangeMessageCounter[aKey] -= 1;
122 
123  if (!self._willChangeMessageCounter[aKey])
124  delete self._willChangeMessageCounter[aKey];
125  }
126  else
127  [CPException raise:@"CPKeyValueObservingException" reason:@"'didChange...' message called without prior call of 'willChange...'"];
128  }
129 }
130 
131 - (void)addObserver:(id)anObserver forKeyPath:(CPString)aPath options:(unsigned)options context:(id)aContext
132 {
133  if (!anObserver || !aPath)
134  return;
135 
136  [[_CPKVOProxy proxyForObject:self] _addObserver:anObserver forKeyPath:aPath options:options context:aContext];
137 }
138 
139 - (void)removeObserver:(id)anObserver forKeyPath:(CPString)aPath
140 {
141  if (!anObserver || !aPath)
142  return;
143 
144  [self[KVOProxyKey] _removeObserver:anObserver forKeyPath:aPath];
145 }
146 
157 + (BOOL)automaticallyNotifiesObserversForKey:(CPString)aKey
158 {
159  var capitalizedKey = aKey.charAt(0).toUpperCase() + aKey.substring(1),
160  selector = "automaticallyNotifiesObserversOf" + capitalizedKey;
161 
162  if ([[self class] respondsToSelector:selector])
163  return objj_msgSend([self class], selector);
164 
165  return YES;
166 }
167 
168 + (CPSet)keyPathsForValuesAffectingValueForKey:(CPString)aKey
169 {
170  var capitalizedKey = aKey.charAt(0).toUpperCase() + aKey.substring(1),
171  selector = "keyPathsForValuesAffecting" + capitalizedKey;
172 
173  if ([[self class] respondsToSelector:selector])
174  return objj_msgSend([self class], selector);
175 
176  return [CPSet set];
177 }
178 
179 - (void)applyChange:(CPDictionary)aChange toKeyPath:(CPString)aKeyPath
180 {
181  var changeKind = [aChange objectForKey:CPKeyValueChangeKindKey],
182  oldValue = [aChange objectForKey:CPKeyValueChangeOldKey],
183  newValue = [aChange objectForKey:CPKeyValueChangeNewKey];
184 
185  if (newValue === [CPNull null])
186  newValue = nil;
187 
188  if (changeKind === CPKeyValueChangeSetting)
189  return [self setValue:newValue forKeyPath:aKeyPath];
190 
191  var indexes = [aChange objectForKey:CPKeyValueChangeIndexesKey];
192 
193  // If we have an indexes entry, then we have an ordered to-many relationship
194  if (indexes)
195  {
196  if (changeKind === CPKeyValueChangeInsertion)
197  [[self mutableArrayValueForKeyPath:aKeyPath] insertObjects:newValue atIndexes:indexes];
198 
199  else if (changeKind === CPKeyValueChangeRemoval)
200  [[self mutableArrayValueForKeyPath:aKeyPath] removeObjectsAtIndexes:indexes];
201 
202  else if (changeKind === CPKeyValueChangeReplacement)
203  [[self mutableArrayValueForKeyPath:aKeyPath] replaceObjectAtIndexes:indexes withObjects:newValue];
204  }
205  else
206  {
207  if (changeKind === CPKeyValueChangeInsertion)
208  [[self mutableSetValueForKeyPath:aKeyPath] unionSet:newValue];
209 
210  else if (changeKind === CPKeyValueChangeRemoval)
211  [[self mutableSetValueForKeyPath:aKeyPath] minusSet:oldValue];
212 
213  else if (changeKind === CPKeyValueChangeReplacement)
214  [[self mutableSetValueForKeyPath:aKeyPath] setSet:newValue];
215  }
216 }
217 
218 @end
219 
221 
222 - (CPDictionary)inverseChangeDictionary
223 {
224  var inverseChangeDictionary = [self mutableCopy],
225  changeKind = [self objectForKey:CPKeyValueChangeKindKey];
226 
227  if (changeKind === CPKeyValueChangeSetting || changeKind === CPKeyValueChangeReplacement)
228  {
230  setObject:[self objectForKey:CPKeyValueChangeOldKey]
231  forKey:CPKeyValueChangeNewKey];
232 
234  setObject:[self objectForKey:CPKeyValueChangeNewKey]
235  forKey:CPKeyValueChangeOldKey];
236  }
237 
238  else if (changeKind === CPKeyValueChangeInsertion)
239  {
241  setObject:CPKeyValueChangeRemoval
242  forKey:CPKeyValueChangeKindKey];
243 
245  setObject:[self objectForKey:CPKeyValueChangeNewKey]
246  forKey:CPKeyValueChangeOldKey];
247 
248  [inverseChangeDictionary removeObjectForKey:CPKeyValueChangeNewKey];
249  }
250 
251  else if (changeKind === CPKeyValueChangeRemoval)
252  {
254  setObject:CPKeyValueChangeInsertion
255  forKey:CPKeyValueChangeKindKey];
256 
258  setObject:[self objectForKey:CPKeyValueChangeOldKey]
259  forKey:CPKeyValueChangeNewKey];
260 
261  [inverseChangeDictionary removeObjectForKey:CPKeyValueChangeOldKey];
262  }
263 
264  return inverseChangeDictionary;
265 }
266 
267 @end
268 
269 // KVO Options
274 
275 // KVO Change Dictionary Keys
276 CPKeyValueChangeKindKey = @"CPKeyValueChangeKindKey";
277 CPKeyValueChangeNewKey = @"CPKeyValueChangeNewKey";
278 CPKeyValueChangeOldKey = @"CPKeyValueChangeOldKey";
279 CPKeyValueChangeIndexesKey = @"CPKeyValueChangeIndexesKey";
280 CPKeyValueChangeNotificationIsPriorKey = @"CPKeyValueChangeNotificationIsPriorKey";
281 
282 // KVO Change Types
287 
288 // CPKeyValueSetMutationKind
293 
294 //FIXME: "secret" dict ivar-keys are workaround to support unordered to-many relationships without too many modifications
295 _CPKeyValueChangeSetMutationObjectsKey = @"_CPKeyValueChangeSetMutationObjectsKey";
296 _CPKeyValueChangeSetMutationKindKey = @"_CPKeyValueChangeSetMutationKindKey";
297 _CPKeyValueChangeSetMutationNewValueKey = @"_CPKeyValueChangeSetMutationNewValueKey";
298 
299 var _changeKindForSetMutationKind = function(mutationKind)
300 {
301  switch (mutationKind)
302  {
307  }
308 };
309 
311  DependentKeysKey = "$KVODEPENDENT",
312  KVOProxyKey = "$KVOPROXY";
313 
314 //rule of thumb: _ methods are called on the real proxy object, others are called on the "fake" proxy object (aka the real object)
315 
316 /* @ignore */
317 @implementation _CPKVOProxy : CPObject
318 {
319  id _targetObject;
320  Class _nativeClass;
321  CPDictionary _changesForKey;
322  CPDictionary _nestingForKey;
323  Object _observersForKey;
324  int _observersForKeyLength;
325  CPSet _replacedKeys;
326 }
327 
328 + (id)proxyForObject:(CPObject)anObject
329 {
330  var proxy = anObject[KVOProxyKey];
331 
332  if (proxy)
333  return proxy;
334 
335  return [[self alloc] initWithTarget:anObject];
336 }
337 
338 - (id)initWithTarget:(id)aTarget
339 {
340  if (self = [super init])
341  {
342  _targetObject = aTarget;
343  _nativeClass = [aTarget class];
344  _observersForKey = {};
345  _changesForKey = {};
346  _nestingForKey = {};
347  _observersForKeyLength = 0;
348 
349  [self _replaceClass];
350  aTarget[KVOProxyKey] = self;
351  }
352  return self;
353 }
354 
355 - (void)_replaceClass
356 {
357  var currentClass = _nativeClass,
358  kvoClassName = "$KVO_" + class_getName(_nativeClass),
359  existingKVOClass = objj_lookUpClass(kvoClassName);
360 
361  if (existingKVOClass)
362  {
363  _targetObject.isa = existingKVOClass;
364  _replacedKeys = existingKVOClass._replacedKeys;
365  return;
366  }
367 
368  var kvoClass = objj_allocateClassPair(currentClass, kvoClassName);
369 
370  objj_registerClassPair(kvoClass);
371 
372  _replacedKeys = [CPSet set];
373  kvoClass._replacedKeys = _replacedKeys;
374 
375  //copy in the methods from our model subclass
376  var methods = class_copyMethodList(_CPKVOModelSubclass);
377 
378  if ([_targetObject isKindOfClass:[CPDictionary class]])
379  methods = methods.concat(class_copyMethodList(_CPKVOModelDictionarySubclass));
380 
381  class_addMethods(kvoClass, methods);
382 
383  _targetObject.isa = kvoClass;
384 }
385 
386 - (void)_replaceModifiersForKey:(CPString)aKey
387 {
388  if ([_replacedKeys containsObject:aKey] || ![_nativeClass automaticallyNotifiesObserversForKey:aKey])
389  return;
390 
391  [_replacedKeys addObject:aKey];
392 
393  var theClass = _nativeClass,
394  KVOClass = _targetObject.isa,
395  capitalizedKey = aKey.charAt(0).toUpperCase() + aKey.substring(1);
396 
397  // Attribute and To-One Relationships
398  var setKey_selector = sel_getUid("set" + capitalizedKey + ":"),
399  setKey_method = class_getInstanceMethod(theClass, setKey_selector);
400 
401  if (setKey_method)
402  {
403  var setKey_method_imp = setKey_method.method_imp;
404 
405  class_addMethod(KVOClass, setKey_selector, function(self, _cmd, anObject)
406  {
407  [self willChangeValueForKey:aKey];
408 
409  setKey_method_imp(self, _cmd, anObject);
410 
411  [self didChangeValueForKey:aKey];
412  }, "");
413  }
414 
415  // FIXME: Deprecated.
416  var _setKey_selector = sel_getUid("_set" + capitalizedKey + ":"),
417  _setKey_method = class_getInstanceMethod(theClass, _setKey_selector);
418 
419  if (_setKey_method)
420  {
421  var _setKey_method_imp = _setKey_method.method_imp;
422 
423  class_addMethod(KVOClass, _setKey_selector, function(self, _cmd, anObject)
424  {
425  [self willChangeValueForKey:aKey];
426 
427  _setKey_method_imp(self, _cmd, anObject);
428 
429  [self didChangeValueForKey:aKey];
430  }, "");
431  }
432 
433  // Ordered To-Many Relationships
434  var insertObject_inKeyAtIndex_selector = sel_getUid("insertObject:in" + capitalizedKey + "AtIndex:"),
435  insertObject_inKeyAtIndex_method =
436  class_getInstanceMethod(theClass, insertObject_inKeyAtIndex_selector),
437 
438  insertKey_atIndexes_selector = sel_getUid("insert" + capitalizedKey + ":atIndexes:"),
439  insertKey_atIndexes_method =
440  class_getInstanceMethod(theClass, insertKey_atIndexes_selector),
441 
442  removeObjectFromKeyAtIndex_selector = sel_getUid("removeObjectFrom" + capitalizedKey + "AtIndex:"),
443  removeObjectFromKeyAtIndex_method =
444  class_getInstanceMethod(theClass, removeObjectFromKeyAtIndex_selector),
445 
446  removeKeyAtIndexes_selector = sel_getUid("remove" + capitalizedKey + "AtIndexes:"),
447  removeKeyAtIndexes_method = class_getInstanceMethod(theClass, removeKeyAtIndexes_selector);
448 
449  if ((insertObject_inKeyAtIndex_method || insertKey_atIndexes_method) &&
450  (removeObjectFromKeyAtIndex_method || removeKeyAtIndexes_method))
451  {
452  if (insertObject_inKeyAtIndex_method)
453  {
454  var insertObject_inKeyAtIndex_method_imp = insertObject_inKeyAtIndex_method.method_imp;
455 
456  class_addMethod(KVOClass, insertObject_inKeyAtIndex_selector, function(self, _cmd, anObject, anIndex)
457  {
458  [self willChange:CPKeyValueChangeInsertion
459  valuesAtIndexes:[CPIndexSet indexSetWithIndex:anIndex]
460  forKey:aKey];
461 
462  insertObject_inKeyAtIndex_method_imp(self, _cmd, anObject, anIndex);
463 
464  [self didChange:CPKeyValueChangeInsertion
465  valuesAtIndexes:[CPIndexSet indexSetWithIndex:anIndex]
466  forKey:aKey];
467  }, "");
468  }
469 
470  if (insertKey_atIndexes_method)
471  {
472  var insertKey_atIndexes_method_imp = insertKey_atIndexes_method.method_imp;
473 
474  class_addMethod(KVOClass, insertKey_atIndexes_selector, function(self, _cmd, objects, indexes)
475  {
476  [self willChange:CPKeyValueChangeInsertion
477  valuesAtIndexes:[indexes copy]
478  forKey:aKey];
479 
480  insertKey_atIndexes_method_imp(self, _cmd, objects, indexes);
481 
482  [self didChange:CPKeyValueChangeInsertion
483  valuesAtIndexes:[indexes copy]
484  forKey:aKey];
485  }, "");
486  }
487 
488  if (removeObjectFromKeyAtIndex_method)
489  {
490  var removeObjectFromKeyAtIndex_method_imp = removeObjectFromKeyAtIndex_method.method_imp;
491 
492  class_addMethod(KVOClass, removeObjectFromKeyAtIndex_selector, function(self, _cmd, anIndex)
493  {
494  [self willChange:CPKeyValueChangeRemoval
495  valuesAtIndexes:[CPIndexSet indexSetWithIndex:anIndex]
496  forKey:aKey];
497 
498  removeObjectFromKeyAtIndex_method_imp(self, _cmd, anIndex);
499 
500  [self didChange:CPKeyValueChangeRemoval
501  valuesAtIndexes:[CPIndexSet indexSetWithIndex:anIndex]
502  forKey:aKey];
503  }, "");
504  }
505 
506  if (removeKeyAtIndexes_method)
507  {
508  var removeKeyAtIndexes_method_imp = removeKeyAtIndexes_method.method_imp;
509 
510  class_addMethod(KVOClass, removeKeyAtIndexes_selector, function(self, _cmd, indexes)
511  {
512  [self willChange:CPKeyValueChangeRemoval
513  valuesAtIndexes:[indexes copy]
514  forKey:aKey];
515 
516  removeKeyAtIndexes_method_imp(self, _cmd, indexes);
517 
518  [self didChange:CPKeyValueChangeRemoval
519  valuesAtIndexes:[indexes copy]
520  forKey:aKey];
521  }, "");
522  }
523 
524  // These are optional.
525  var replaceObjectInKeyAtIndex_withObject_selector =
526  sel_getUid("replaceObjectIn" + capitalizedKey + "AtIndex:withObject:"),
527  replaceObjectInKeyAtIndex_withObject_method =
528  class_getInstanceMethod(theClass, replaceObjectInKeyAtIndex_withObject_selector);
529 
530  if (replaceObjectInKeyAtIndex_withObject_method)
531  {
532  var replaceObjectInKeyAtIndex_withObject_method_imp =
533  replaceObjectInKeyAtIndex_withObject_method.method_imp;
534 
535  class_addMethod(KVOClass, replaceObjectInKeyAtIndex_withObject_selector,
536  function(self, _cmd, anIndex, anObject)
537  {
538  [self willChange:CPKeyValueChangeReplacement
539  valuesAtIndexes:[CPIndexSet indexSetWithIndex:anIndex]
540  forKey:aKey];
541 
542  replaceObjectInKeyAtIndex_withObject_method_imp(self, _cmd, anIndex, anObject);
543 
544  [self didChange:CPKeyValueChangeReplacement
545  valuesAtIndexes:[CPIndexSet indexSetWithIndex:anIndex]
546  forKey:aKey];
547  }, "");
548  }
549 
550  var replaceKeyAtIndexes_withKey_selector =
551  sel_getUid("replace" + capitalizedKey + "AtIndexes:with" + capitalizedKey + ":"),
552  replaceKeyAtIndexes_withKey_method =
553  class_getInstanceMethod(theClass, replaceKeyAtIndexes_withKey_selector);
554 
555  if (replaceKeyAtIndexes_withKey_method)
556  {
557  var replaceKeyAtIndexes_withKey_method_imp = replaceKeyAtIndexes_withKey_method.method_imp;
558 
559  class_addMethod(KVOClass, replaceKeyAtIndexes_withKey_selector, function(self, _cmd, indexes, objects)
560  {
561  [self willChange:CPKeyValueChangeReplacement
562  valuesAtIndexes:[indexes copy]
563  forKey:aKey];
564 
565  replaceObjectInKeyAtIndex_withObject_method_imp(self, _cmd, indexes, objects);
566 
567  [self didChange:CPKeyValueChangeReplacement
568  valuesAtIndexes:[indexes copy]
569  forKey:aKey];
570  }, "");
571  }
572  }
573 
574  // Unordered To-Many Relationships
575  var addKeyObject_selector = sel_getUid("add" + capitalizedKey + "Object:"),
576  addKeyObject_method = class_getInstanceMethod(theClass, addKeyObject_selector),
577 
578  addKey_selector = sel_getUid("add" + capitalizedKey + ":"),
579  addKey_method = class_getInstanceMethod(theClass, addKey_selector),
580 
581  removeKeyObject_selector = sel_getUid("remove" + capitalizedKey + "Object:"),
582  removeKeyObject_method = class_getInstanceMethod(theClass, removeKeyObject_selector),
583 
584  removeKey_selector = sel_getUid("remove" + capitalizedKey + ":"),
585  removeKey_method = class_getInstanceMethod(theClass, removeKey_selector);
586 
587  if ((addKeyObject_method || addKey_method) && (removeKeyObject_method || removeKey_method))
588  {
589  if (addKeyObject_method)
590  {
591  var addKeyObject_method_imp = addKeyObject_method.method_imp;
592 
593  class_addMethod(KVOClass, addKeyObject_selector, function(self, _cmd, anObject)
594  {
595  [self willChangeValueForKey:aKey
596  withSetMutation:CPKeyValueUnionSetMutation
597  usingObjects:[CPSet setWithObject:anObject]];
598 
599  addKeyObject_method_imp(self, _cmd, anObject);
600 
601  [self didChangeValueForKey:aKey
602  withSetMutation:CPKeyValueUnionSetMutation
603  usingObjects:[CPSet setWithObject:anObject]];
604  }, "");
605  }
606 
607  if (addKey_method)
608  {
609  var addKey_method_imp = addKey_method.method_imp;
610 
611  class_addMethod(KVOClass, addKey_selector, function(self, _cmd, objects)
612  {
613  [self willChangeValueForKey:aKey
614  withSetMutation:CPKeyValueUnionSetMutation
615  usingObjects:[objects copy]];
616 
617  addKey_method_imp(self, _cmd, objects);
618 
619  [self didChangeValueForKey:aKey
620  withSetMutation:CPKeyValueUnionSetMutation
621  usingObjects:[objects copy]];
622  }, "");
623  }
624 
625  if (removeKeyObject_method)
626  {
627  var removeKeyObject_method_imp = removeKeyObject_method.method_imp;
628 
629  class_addMethod(KVOClass, removeKeyObject_selector, function(self, _cmd, anObject)
630  {
631  [self willChangeValueForKey:aKey
632  withSetMutation:CPKeyValueMinusSetMutation
633  usingObjects:[CPSet setWithObject:anObject]];
634 
635  removeKeyObject_method_imp(self, _cmd, anObject);
636 
637  [self didChangeValueForKey:aKey
638  withSetMutation:CPKeyValueMinusSetMutation
639  usingObjects:[CPSet setWithObject:anObject]];
640  }, "");
641  }
642 
643  if (removeKey_method)
644  {
645  var removeKey_method_imp = removeKey_method.method_imp;
646 
647  class_addMethod(KVOClass, removeKey_selector, function(self, _cmd, objects)
648  {
649  [self willChangeValueForKey:aKey
650  withSetMutation:CPKeyValueMinusSetMutation
651  usingObjects:[objects copy]];
652 
653  removeKey_method_imp(self, _cmd, objects);
654 
655  [self didChangeValueForKey:aKey
656  withSetMutation:CPKeyValueMinusSetMutation
657  usingObjects:[objects copy]];
658  }, "");
659  }
660 
661  // intersect<Key>: is optional.
662  var intersectKey_selector = sel_getUid("intersect" + capitalizedKey + ":"),
663  intersectKey_method = class_getInstanceMethod(theClass, intersectKey_selector);
664 
665  if (intersectKey_method)
666  {
667  var intersectKey_method_imp = intersectKey_method.method_imp;
668 
669  class_addMethod(KVOClass, intersectKey_selector, function(self, _cmd, aSet)
670  {
671  [self willChangeValueForKey:aKey
672  withSetMutation:CPKeyValueIntersectSetMutation
673  usingObjects:[aSet copy]];
674 
675  intersectKey_method_imp(self, _cmd, aSet);
676 
677  [self didChangeValueForKey:aKey
678  withSetMutation:CPKeyValueIntersectSetMutation
679  usingObjects:[aSet copy]];
680  }, "");
681  }
682  }
683 
684  var affectingKeys = [[_nativeClass keyPathsForValuesAffectingValueForKey:aKey] allObjects],
685  affectingKeysCount = affectingKeys ? affectingKeys.length : 0;
686 
687  if (!affectingKeysCount)
688  return;
689 
690  var dependentKeysForClass = _nativeClass[DependentKeysKey];
691 
692  if (!dependentKeysForClass)
693  {
694  dependentKeysForClass = {};
695  _nativeClass[DependentKeysKey] = dependentKeysForClass;
696  }
697 
698  while (affectingKeysCount--)
699  {
700  var affectingKey = affectingKeys[affectingKeysCount],
701  affectedKeys = dependentKeysForClass[affectingKey];
702 
703  if (!affectedKeys)
704  {
705  affectedKeys = [CPSet new];
706  dependentKeysForClass[affectingKey] = affectedKeys;
707  }
708 
709  [affectedKeys addObject:aKey];
710 
711  //observe key paths of objects other then ourselves, so we are notified of the changes
712  //use CPKeyValueObservingOptionPrior to ensure proper wrapping around changes
713  //so CPKeyValueObservingOptionPrior and CPKeyValueObservingOptionOld can be fulfilled even for dependent keys
714  if (affectingKey.indexOf(@".") !== -1)
715  [_targetObject addObserver:self forKeyPath:affectingKey options:CPKeyValueObservingOptionPrior | kvoNewAndOld context:nil];
716  else
717  [self _replaceModifiersForKey:affectingKey];
718  }
719 }
720 
721 - (void)observeValueForKeyPath:(CPString)theKeyPath ofObject:(id)theObject change:(CPDictionary)theChanges context:(id)theContext
722 {
723  // Fire change events for the dependent keys
724  var dependentKeysForClass = _nativeClass[DependentKeysKey],
725  dependantKeys = [dependentKeysForClass[theKeyPath] allObjects];
726 
727  var isBeforeFlag = !![theChanges objectForKey:CPKeyValueChangeNotificationIsPriorKey];
728  for (var i = 0; i < [dependantKeys count]; i++)
729  {
730  var dependantKey = [dependantKeys objectAtIndex:i];
731  [self _sendNotificationsForKey:dependantKey changeOptions:theChanges isBefore:isBeforeFlag];
732  }
733 }
734 
735 - (void)_addObserver:(id)anObserver forKeyPath:(CPString)aPath options:(unsigned)options context:(id)aContext
736 {
737  if (!anObserver)
738  return;
739 
740  var forwarder = nil;
741 
742  if (aPath.indexOf('.') !== CPNotFound && aPath.charAt(0) !== '@')
743  forwarder = [[_CPKVOForwardingObserver alloc] initWithKeyPath:aPath object:_targetObject observer:anObserver options:options context:aContext];
744  else
745  [self _replaceModifiersForKey:aPath];
746 
747  var observers = _observersForKey[aPath];
748 
749  if (!observers)
750  {
751  observers = [CPDictionary dictionary];
752  _observersForKey[aPath] = observers;
753  _observersForKeyLength++;
754  }
755 
756  [observers setObject:_CPKVOInfoMake(anObserver, options, aContext, forwarder) forKey:[anObserver UID]];
757 
758  if (options & CPKeyValueObservingOptionInitial)
759  {
760  var newValue = [_targetObject valueForKeyPath:aPath];
761 
762  if (newValue === nil || newValue === undefined)
763  newValue = [CPNull null];
764 
765  var changes = [CPDictionary dictionaryWithObject:newValue forKey:CPKeyValueChangeNewKey];
766  [anObserver observeValueForKeyPath:aPath ofObject:_targetObject change:changes context:aContext];
767  }
768 }
769 
770 - (void)_removeObserver:(id)anObserver forKeyPath:(CPString)aPath
771 {
772  var observers = _observersForKey[aPath];
773 
774  if (!observers)
775  {
776  CPLog.warn(@"Cannot remove an observer %@ for the key path \"%@\" from %@ because it is not registered as an observer.",
777  _targetObject, aPath, anObserver);
778 
779  return;
780  }
781 
782  if (aPath.indexOf('.') != CPNotFound)
783  {
784  var forwarder = [observers objectForKey:[anObserver UID]].forwarder;
785  [forwarder finalize];
786  }
787 
788  [observers removeObjectForKey:[anObserver UID]];
789 
790  if (![observers count])
791  {
792  _observersForKeyLength--;
793  delete _observersForKey[aPath];
794  }
795 
796  if (!_observersForKeyLength)
797  {
798  _targetObject.isa = _nativeClass; //restore the original class
799  delete _targetObject[KVOProxyKey];
800  }
801 }
802 
803 //FIXME: We do not compute and cache if CPKeyValueObservingOptionOld is needed, so we may do unnecessary work
804 
805 - (void)_sendNotificationsForKey:(CPString)aKey changeOptions:(CPDictionary)changeOptions isBefore:(BOOL)isBefore
806 {
807  var changes = _changesForKey[aKey];
808 
809  if (isBefore)
810  {
811  if (changes)
812  {
813  // "willChange:X" nesting.
814  var level = _nestingForKey[aKey];
815  if (!level)
816  [CPException raise:CPInternalInconsistencyException reason:@"_changesForKey without _nestingForKey"];
817  _nestingForKey[aKey] = level + 1;
818  // Only notify on the first willChange..., silently note any following nested calls.
819  return;
820  }
821  _nestingForKey[aKey] = 1;
822 
823  changes = changeOptions;
824 
825  var indexes = [changes objectForKey:CPKeyValueChangeIndexesKey],
826  setMutationKind = changes[_CPKeyValueChangeSetMutationKindKey];
827 
828  if (setMutationKind)
829  {
830  var setMutationObjects = [changes[_CPKeyValueChangeSetMutationObjectsKey] copy],
831  setExistingObjects = [[_targetObject valueForKey: aKey] copy];
832 
833  if (setMutationKind == CPKeyValueMinusSetMutation)
834  {
835  [setExistingObjects intersectSet: setMutationObjects];
836  [changes setValue:setExistingObjects forKey:CPKeyValueChangeOldKey];
837  }
838  else if (setMutationKind === CPKeyValueIntersectSetMutation || setMutationKind === CPKeyValueSetSetMutation)
839  {
840  [setExistingObjects minusSet: setMutationObjects];
841  [changes setValue:setExistingObjects forKey:CPKeyValueChangeOldKey];
842  }
843 
844  //for unordered to-many relationships (CPSet) even new values can only be calculated before!!!
845  if (setMutationKind === CPKeyValueUnionSetMutation || setMutationKind === CPKeyValueSetSetMutation)
846  {
847  [setMutationObjects minusSet: setExistingObjects];
848  //hide new value (for CPKeyValueObservingOptionPrior messages)
849  //as long as "didChangeValue..." is not yet called!
850  changes[_CPKeyValueChangeSetMutationNewValueKey] = setMutationObjects;
851  }
852  }
853  else if (indexes)
854  {
855  var type = [changes objectForKey:CPKeyValueChangeKindKey];
856  // for ordered to-many relationships, oldvalue is only sensible for replace and remove
857  if (type === CPKeyValueChangeReplacement || type === CPKeyValueChangeRemoval)
858  {
859  //FIXME: do we need to go through and replace "" with CPNull?
860  var newValues = [[_targetObject mutableArrayValueForKeyPath:aKey] objectsAtIndexes:indexes];
861  [changes setValue:newValues forKey:CPKeyValueChangeOldKey];
862  }
863  }
864  else
865  {
866  var oldValue = [_targetObject valueForKey:aKey];
867 
868  if (oldValue === nil || oldValue === undefined)
869  oldValue = [CPNull null];
870 
871  [changes setObject:oldValue forKey:CPKeyValueChangeOldKey];
872  }
873 
874  [changes setObject:1 forKey:CPKeyValueChangeNotificationIsPriorKey];
875 
876  _changesForKey[aKey] = changes;
877  }
878  else
879  {
880  var level = _nestingForKey[aKey];
881  if (!changes || !level)
882  {
883  if (_targetObject._willChangeMessageCounter && _targetObject._willChangeMessageCounter[aKey])
884  {
885  // Close unobserved willChange for a given key.
886  _targetObject._willChangeMessageCounter[aKey] -= 1;
887 
888  if (!_targetObject._willChangeMessageCounter[aKey])
889  delete _targetObject._willChangeMessageCounter[aKey];
890 
891  return;
892  }
893  else
894  [CPException raise:@"CPKeyValueObservingException" reason:@"'didChange...' message called without prior call of 'willChange...'"];
895  }
896 
897  _nestingForKey[aKey] = level - 1;
898  if (level - 1 > 0)
899  {
900  // willChange... was called multiple times. Only fire observation notifications when
901  // didChange... has been called an equal number of times.
902  return;
903  }
904  delete _nestingForKey[aKey];
905 
906  [changes removeObjectForKey:CPKeyValueChangeNotificationIsPriorKey];
907 
908  var indexes = [changes objectForKey:CPKeyValueChangeIndexesKey],
909  setMutationKind = changes[_CPKeyValueChangeSetMutationKindKey];
910 
911  if (setMutationKind)
912  {
913  //old and new values for unordered to-many relationships can only be calculated before
914  //set recalculated hidden new value as soon as "didChangeValue..." is called!
915  var newValue = changes[_CPKeyValueChangeSetMutationNewValueKey];
916  [changes setValue:newValue forKey:CPKeyValueChangeNewKey];
917 
918  //delete hidden values
919  delete changes[_CPKeyValueChangeSetMutationNewValueKey];
920  delete changes[_CPKeyValueChangeSetMutationObjectsKey];
921  delete changes[_CPKeyValueChangeSetMutationKindKey];
922  }
923  else if (indexes)
924  {
925  var type = [changes objectForKey:CPKeyValueChangeKindKey];
926  // for ordered to-many relationships, newvalue is only sensible for replace and insert
928  {
929  //FIXME: do we need to go through and replace "" with CPNull?
930  var newValues = [[_targetObject mutableArrayValueForKeyPath:aKey] objectsAtIndexes:indexes];
931  [changes setValue:newValues forKey:CPKeyValueChangeNewKey];
932  }
933  }
934  else
935  {
936  var newValue = [_targetObject valueForKey:aKey];
937 
938  if (newValue === nil || newValue === undefined)
939  newValue = [CPNull null];
940 
941  [changes setObject:newValue forKey:CPKeyValueChangeNewKey];
942  }
943 
944  delete _changesForKey[aKey];
945  }
946 
947  var observers = [_observersForKey[aKey] allValues],
948  count = observers ? observers.length : 0;
949 
950  while (count--)
951  {
952  var observerInfo = observers[count];
953 
954  if (!isBefore || (observerInfo.options & CPKeyValueObservingOptionPrior))
955  [observerInfo.observer observeValueForKeyPath:aKey ofObject:_targetObject change:changes context:observerInfo.context];
956  }
957 
958  var dependentKeysMap = _nativeClass[DependentKeysKey];
959 
960  if (!dependentKeysMap)
961  return;
962 
963  var dependentKeyPaths = [dependentKeysMap[aKey] allObjects];
964 
965  if (!dependentKeyPaths)
966  return;
967 
968  var index = 0,
969  count = [dependentKeyPaths count];
970 
971  for (; index < count; ++index)
972  {
973  var keyPath = dependentKeyPaths[index];
974 
975  [self _sendNotificationsForKey:keyPath
976  changeOptions:isBefore ? [changeOptions copy] : _changesForKey[keyPath]
977  isBefore:isBefore];
978  }
979 }
980 
981 @end
982 @implementation _CPKVOModelSubclass
983 {
984  id __doxygen__;
985 }
986 
987 - (void)willChangeValueForKey:(CPString)aKey
988 {
989  var superClass = [self class],
990  methodSelector = @selector(willChangeValueForKey:),
991  methodImp = class_getMethodImplementation(superClass, methodSelector);
992 
993  methodImp(self, methodSelector, aKey);
994 
995  if (!aKey)
996  return;
997 
998  var changeOptions = [CPDictionary dictionaryWithObject:CPKeyValueChangeSetting forKey:CPKeyValueChangeKindKey];
999 
1000  [[_CPKVOProxy proxyForObject:self] _sendNotificationsForKey:aKey changeOptions:changeOptions isBefore:YES];
1001 }
1002 
1003 - (void)didChangeValueForKey:(CPString)aKey
1004 {
1005  var superClass = [self class],
1006  methodSelector = @selector(didChangeValueForKey:),
1007  methodImp = class_getMethodImplementation(superClass, methodSelector);
1008 
1009  methodImp(self, methodSelector, aKey);
1010 
1011  if (!aKey)
1012  return;
1013 
1014  [[_CPKVOProxy proxyForObject:self] _sendNotificationsForKey:aKey changeOptions:nil isBefore:NO];
1015 }
1016 
1017 - (void)willChange:(CPKeyValueChange)change valuesAtIndexes:(CPIndexSet)indexes forKey:(CPString)aKey
1018 {
1019  var superClass = [self class],
1020  methodSelector = @selector(willChange:valuesAtIndexes:forKey:),
1021  methodImp = class_getMethodImplementation(superClass, methodSelector);
1022 
1023  methodImp(self, methodSelector, change, indexes, aKey);
1024 
1025  if (!aKey)
1026  return;
1027 
1028  var changeOptions = [CPDictionary dictionaryWithObjects:[change, indexes] forKeys:[CPKeyValueChangeKindKey, CPKeyValueChangeIndexesKey]];
1029 
1030  [[_CPKVOProxy proxyForObject:self] _sendNotificationsForKey:aKey changeOptions:changeOptions isBefore:YES];
1031 }
1032 
1033 - (void)didChange:(CPKeyValueChange)change valuesAtIndexes:(CPIndexSet)indexes forKey:(CPString)aKey
1034 {
1035  var superClass = [self class],
1036  methodSelector = @selector(didChange:valuesAtIndexes:forKey:),
1037  methodImp = class_getMethodImplementation(superClass, methodSelector);
1038 
1039  methodImp(self, methodSelector, change, indexes, aKey);
1040 
1041  if (!aKey)
1042  return;
1043 
1044  [[_CPKVOProxy proxyForObject:self] _sendNotificationsForKey:aKey changeOptions:nil isBefore:NO];
1045 }
1046 
1047 - (void)willChangeValueForKey:(CPString)aKey withSetMutation:(CPKeyValueSetMutationKind)mutationKind usingObjects:(CPSet)objects
1048 {
1049  var superClass = [self class],
1050  methodSelector = @selector(willChangeValueForKey:withSetMutation:usingObjects:),
1051  methodImp = class_getMethodImplementation(superClass, methodSelector);
1052 
1053  methodImp(self, methodSelector, aKey, mutationKind, objects);
1054 
1055  if (!aKey)
1056  return;
1057 
1058  var changeKind = _changeKindForSetMutationKind(mutationKind),
1059  changeOptions = [CPDictionary dictionaryWithObject:changeKind forKey:CPKeyValueChangeKindKey];
1060  //set hidden change-dict ivars to support unordered to-many relationships
1061  changeOptions[_CPKeyValueChangeSetMutationObjectsKey] = objects;
1062  changeOptions[_CPKeyValueChangeSetMutationKindKey] = mutationKind;
1063 
1064  [[_CPKVOProxy proxyForObject:self] _sendNotificationsForKey:aKey changeOptions:changeOptions isBefore:YES];
1065 }
1066 
1067 - (void)didChangeValueForKey:(CPString)aKey withSetMutation:(CPKeyValueSetMutationKind)mutationKind usingObjects:(CPSet)objects
1068 {
1069  var superClass = [self class],
1070  methodSelector = @selector(didChangeValueForKey:withSetMutation:usingObjects:),
1071  methodImp = class_getMethodImplementation(superClass, methodSelector);
1072 
1073  methodImp(self, methodSelector, aKey, mutationKind, objects);
1074 
1075  if (!aKey)
1076  return;
1077 
1078  [[_CPKVOProxy proxyForObject:self] _sendNotificationsForKey:aKey changeOptions:nil isBefore:NO];
1079 }
1080 
1081 - (Class)class
1082 {
1083  return self[KVOProxyKey]._nativeClass;
1084 }
1085 
1086 - (Class)superclass
1087 {
1088  return [[self class] superclass];
1089 }
1090 
1091 - (BOOL)isKindOfClass:(Class)aClass
1092 {
1093  return [[self class] isSubclassOfClass:aClass];
1094 }
1095 
1096 - (BOOL)isMemberOfClass:(Class)aClass
1097 {
1098  return [self class] == aClass;
1099 }
1100 
1101 - (CPString)className
1102 {
1103  return [self class].name;
1104 }
1105 
1106 @end
1107 @implementation _CPKVOModelDictionarySubclass
1108 {
1109  id __doxygen__;
1110 }
1111 
1112 - (void)removeAllObjects
1113 {
1114  var keys = [self allKeys],
1115  count = [keys count],
1116  i = 0;
1117 
1118  for (; i < count; i++)
1119  [self willChangeValueForKey:keys[i]];
1120 
1121  var superClass = [self class],
1122  methodSelector = @selector(removeAllObjects),
1123  methodImp = class_getMethodImplementation(superClass, methodSelector);
1124 
1125  methodImp(self, methodSelector);
1126 
1127  for (i = 0; i < count; i++)
1128  [self didChangeValueForKey:keys[i]];
1129 }
1130 
1131 - (void)removeObjectForKey:(id)aKey
1132 {
1133  [self willChangeValueForKey:aKey];
1134 
1135  var superClass = [self class],
1136  methodSelector = @selector(removeObjectForKey:),
1137  methodImp = class_getMethodImplementation(superClass, methodSelector);
1138 
1139  methodImp(self, methodSelector, aKey);
1140 
1141  [self didChangeValueForKey:aKey];
1142 }
1143 
1144 - (void)setObject:(id)anObject forKey:(id)aKey
1145 {
1146  [self willChangeValueForKey:aKey];
1147 
1148  var superClass = [self class],
1149  methodSelector = @selector(setObject:forKey:),
1150  methodImp = class_getMethodImplementation(superClass, methodSelector);
1151 
1152  methodImp(self, methodSelector, anObject, aKey);
1153 
1154  [self didChangeValueForKey:aKey];
1155 }
1156 
1157 @end
1158 
1159 @implementation _CPKVOForwardingObserver : CPObject
1160 {
1161  id _object;
1162  id _observer;
1163  id _context;
1164  unsigned _options;
1165  //a.b
1166  CPString _firstPart; //a
1167  CPString _secondPart; //b
1168 
1169  id _value;
1170 }
1171 
1172 - (id)initWithKeyPath:(CPString)aKeyPath object:(id)anObject observer:(id)anObserver options:(unsigned)options context:(id)aContext
1173 {
1174  self = [super init];
1175 
1176  _context = aContext;
1177  _observer = anObserver;
1178  _object = anObject;
1179  _options = options;
1180 
1181  var dotIndex = aKeyPath.indexOf('.');
1182 
1183  if (dotIndex === CPNotFound)
1184  [CPException raise:CPInvalidArgumentException reason:"Created _CPKVOForwardingObserver without compound key path: " + aKeyPath];
1185 
1186  _firstPart = aKeyPath.substring(0, dotIndex);
1187  _secondPart = aKeyPath.substring(dotIndex + 1);
1188 
1189  //become an observer of the first part of our key (a)
1190  [_object addObserver:self forKeyPath:_firstPart options:_options context:nil];
1191 
1192  //the current value of a (not the value of a.b)
1193  _value = [_object valueForKey:_firstPart];
1194 
1195  if (_value)
1196  [_value addObserver:self forKeyPath:_secondPart options:_options context:nil]; //we're observing b on current a
1197 
1198  return self;
1199 }
1200 
1201 - (void)observeValueForKeyPath:(CPString)aKeyPath ofObject:(id)anObject change:(CPDictionary)changes context:(id)aContext
1202 {
1203  if (aKeyPath === _firstPart)
1204  {
1205  var oldValue = [_value valueForKeyPath:_secondPart],
1206  newValue = [_object valueForKeyPath:_firstPart + "." + _secondPart],
1207  pathChanges = [CPDictionary dictionaryWithObjectsAndKeys:
1208  newValue ? newValue : [CPNull null], CPKeyValueChangeNewKey,
1209  oldValue ? oldValue : [CPNull null], CPKeyValueChangeOldKey,
1210  CPKeyValueChangeSetting, CPKeyValueChangeKindKey];
1211 
1212  [_observer observeValueForKeyPath:_firstPart + "." + _secondPart ofObject:_object change:pathChanges context:_context];
1213 
1214  //since a has changed, we should remove ourselves as an observer of the old a, and observe the new one
1215  if (_value)
1216  [_value removeObserver:self forKeyPath:_secondPart];
1217 
1218  _value = [_object valueForKey:_firstPart];
1219 
1220  if (_value)
1221  [_value addObserver:self forKeyPath:_secondPart options:_options context:nil];
1222  }
1223  else
1224  {
1225  //a is the same, but a.b has changed -- nothing to do but forward this message along
1226  [_observer observeValueForKeyPath:_firstPart + "." + aKeyPath ofObject:_object change:changes context:_context];
1227  }
1228 }
1229 
1230 - (void)finalize
1231 {
1232  if (_value)
1233  [_value removeObserver:self forKeyPath:_secondPart];
1234 
1235  [_object removeObserver:self forKeyPath:_firstPart];
1236 
1237  _object = nil;
1238  _observer = nil;
1239  _context = nil;
1240  _value = nil;
1241 }
1242 
1243 @end
1244 
1245 var _CPKVOInfoMake = function _CPKVOInfoMake(anObserver, theOptions, aContext, aForwarder)
1246 {
1247  return {
1248  observer: anObserver,
1249  options: theOptions,
1250  context: aContext,
1251  forwarder: aForwarder
1252  };
1253 };
1254