/Foundation/CPKeyValueObserving.j

http://github.com/cacaodev/cappuccino · Unknown · 1360 lines · 1064 code · 296 blank · 0 comment · 0 complexity · 45183e325cba3acf780edaff0a8d639f MD5 · raw file

  1. /*
  2. * CPKeyValueObserving.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. @import "CPArray.j"
  23. @import "CPDictionary.j"
  24. @import "CPException.j"
  25. @import "CPIndexSet.j"
  26. @import "CPNull.j"
  27. @import "CPObject.j"
  28. @import "CPSet.j"
  29. @implementation CPObject (KeyValueObserving)
  30. - (void)willChangeValueForKey:(CPString)aKey
  31. {
  32. if (!aKey)
  33. return;
  34. if (!self[KVOProxyKey])
  35. {
  36. if (!self._willChangeMessageCounter)
  37. self._willChangeMessageCounter = new Object();
  38. if (!self._willChangeMessageCounter[aKey])
  39. self._willChangeMessageCounter[aKey] = 1;
  40. else
  41. self._willChangeMessageCounter[aKey] += 1;
  42. }
  43. }
  44. - (void)didChangeValueForKey:(CPString)aKey
  45. {
  46. if (!aKey)
  47. return;
  48. if (!self[KVOProxyKey])
  49. {
  50. if (self._willChangeMessageCounter && self._willChangeMessageCounter[aKey])
  51. {
  52. self._willChangeMessageCounter[aKey] -= 1;
  53. if (!self._willChangeMessageCounter[aKey])
  54. delete self._willChangeMessageCounter[aKey];
  55. }
  56. else
  57. [CPException raise:@"CPKeyValueObservingException" reason:@"'didChange...' message called without prior call of 'willChange...'"];
  58. }
  59. }
  60. - (void)willChange:(CPKeyValueChange)aChange valuesAtIndexes:(CPIndexSet)indexes forKey:(CPString)aKey
  61. {
  62. if (!aKey)
  63. return;
  64. if (!self[KVOProxyKey])
  65. {
  66. if (!self._willChangeMessageCounter)
  67. self._willChangeMessageCounter = new Object();
  68. if (!self._willChangeMessageCounter[aKey])
  69. self._willChangeMessageCounter[aKey] = 1;
  70. else
  71. self._willChangeMessageCounter[aKey] += 1;
  72. }
  73. }
  74. - (void)didChange:(CPKeyValueChange)aChange valuesAtIndexes:(CPIndexSet)indexes forKey:(CPString)aKey
  75. {
  76. if (!aKey)
  77. return;
  78. if (!self[KVOProxyKey])
  79. {
  80. if (self._willChangeMessageCounter && self._willChangeMessageCounter[aKey])
  81. {
  82. self._willChangeMessageCounter[aKey] -= 1;
  83. if (!self._willChangeMessageCounter[aKey])
  84. delete self._willChangeMessageCounter[aKey];
  85. }
  86. else
  87. [CPException raise:@"CPKeyValueObservingException" reason:@"'didChange...' message called without prior call of 'willChange...'"];
  88. }
  89. }
  90. - (void)willChangeValueForKey:(CPString)aKey withSetMutation:(CPKeyValueSetMutationKind)aMutationKind usingObjects:(CPSet)objects
  91. {
  92. if (!aKey)
  93. return;
  94. if (!self[KVOProxyKey])
  95. {
  96. if (!self._willChangeMessageCounter)
  97. self._willChangeMessageCounter = new Object();
  98. if (!self._willChangeMessageCounter[aKey])
  99. self._willChangeMessageCounter[aKey] = 1;
  100. else
  101. self._willChangeMessageCounter[aKey] += 1;
  102. }
  103. }
  104. - (void)didChangeValueForKey:(CPString)aKey withSetMutation:(CPKeyValueSetMutationKind)aMutationKind usingObjects:(CPSet)objects
  105. {
  106. if (!self[KVOProxyKey])
  107. {
  108. if (self._willChangeMessageCounter && self._willChangeMessageCounter[aKey])
  109. {
  110. self._willChangeMessageCounter[aKey] -= 1;
  111. if (!self._willChangeMessageCounter[aKey])
  112. delete self._willChangeMessageCounter[aKey];
  113. }
  114. else
  115. [CPException raise:@"CPKeyValueObservingException" reason:@"'didChange...' message called without prior call of 'willChange...'"];
  116. }
  117. }
  118. - (void)addObserver:(id)anObserver forKeyPath:(CPString)aPath options:(CPKeyValueObservingOptions)options context:(id)aContext
  119. {
  120. if (!anObserver || !aPath)
  121. return;
  122. [[_CPKVOProxy proxyForObject:self] _addObserver:anObserver forKeyPath:aPath options:options context:aContext];
  123. }
  124. - (void)removeObserver:(id)anObserver forKeyPath:(CPString)aPath
  125. {
  126. if (!anObserver || !aPath)
  127. return;
  128. [self[KVOProxyKey] _removeObserver:anObserver forKeyPath:aPath];
  129. }
  130. /*!
  131. Whether -willChangeValueForKey/-didChangeValueForKey should automatically be invoked when the
  132. setter of the given key is used. The default is YES. If you override this method to return NO
  133. for some key, you will need to call -willChangeValueForKey/-didChangeValueForKey manually to
  134. be KVO compliant.
  135. The default implementation of this method will check if the receiving class implements
  136. `+ (BOOL)automaticallyNotifiesObserversOf<aKey>` and return the response of that method if it
  137. exists.
  138. */
  139. + (BOOL)automaticallyNotifiesObserversForKey:(CPString)aKey
  140. {
  141. var capitalizedKey = aKey.charAt(0).toUpperCase() + aKey.substring(1),
  142. selector = "automaticallyNotifiesObserversOf" + capitalizedKey;
  143. if ([[self class] respondsToSelector:selector])
  144. return objj_msgSend([self class], selector);
  145. return YES;
  146. }
  147. + (CPSet)keyPathsForValuesAffectingValueForKey:(CPString)aKey
  148. {
  149. var capitalizedKey = aKey.charAt(0).toUpperCase() + aKey.substring(1),
  150. selector = "keyPathsForValuesAffecting" + capitalizedKey;
  151. if ([[self class] respondsToSelector:selector])
  152. return objj_msgSend([self class], selector);
  153. return [CPSet set];
  154. }
  155. - (void)applyChange:(CPDictionary)aChange toKeyPath:(CPString)aKeyPath
  156. {
  157. var changeKind = [aChange objectForKey:CPKeyValueChangeKindKey],
  158. oldValue = [aChange objectForKey:CPKeyValueChangeOldKey],
  159. newValue = [aChange objectForKey:CPKeyValueChangeNewKey];
  160. if (newValue === [CPNull null])
  161. newValue = nil;
  162. if (changeKind === CPKeyValueChangeSetting)
  163. return [self setValue:newValue forKeyPath:aKeyPath];
  164. var indexes = [aChange objectForKey:CPKeyValueChangeIndexesKey];
  165. // If we have an indexes entry, then we have an ordered to-many relationship
  166. if (indexes)
  167. {
  168. if (changeKind === CPKeyValueChangeInsertion)
  169. [[self mutableArrayValueForKeyPath:aKeyPath] insertObjects:newValue atIndexes:indexes];
  170. else if (changeKind === CPKeyValueChangeRemoval)
  171. [[self mutableArrayValueForKeyPath:aKeyPath] removeObjectsAtIndexes:indexes];
  172. else if (changeKind === CPKeyValueChangeReplacement)
  173. [[self mutableArrayValueForKeyPath:aKeyPath] replaceObjectAtIndexes:indexes withObjects:newValue];
  174. }
  175. else
  176. {
  177. if (changeKind === CPKeyValueChangeInsertion)
  178. [[self mutableSetValueForKeyPath:aKeyPath] unionSet:newValue];
  179. else if (changeKind === CPKeyValueChangeRemoval)
  180. [[self mutableSetValueForKeyPath:aKeyPath] minusSet:oldValue];
  181. else if (changeKind === CPKeyValueChangeReplacement)
  182. [[self mutableSetValueForKeyPath:aKeyPath] setSet:newValue];
  183. }
  184. }
  185. @end
  186. @implementation CPDictionary (KeyValueObserving)
  187. - (CPDictionary)inverseChangeDictionary
  188. {
  189. var inverseChangeDictionary = [self mutableCopy],
  190. changeKind = [self objectForKey:CPKeyValueChangeKindKey];
  191. if (changeKind === CPKeyValueChangeSetting || changeKind === CPKeyValueChangeReplacement)
  192. {
  193. [inverseChangeDictionary
  194. setObject:[self objectForKey:CPKeyValueChangeOldKey]
  195. forKey:CPKeyValueChangeNewKey];
  196. [inverseChangeDictionary
  197. setObject:[self objectForKey:CPKeyValueChangeNewKey]
  198. forKey:CPKeyValueChangeOldKey];
  199. }
  200. else if (changeKind === CPKeyValueChangeInsertion)
  201. {
  202. [inverseChangeDictionary
  203. setObject:CPKeyValueChangeRemoval
  204. forKey:CPKeyValueChangeKindKey];
  205. [inverseChangeDictionary
  206. setObject:[self objectForKey:CPKeyValueChangeNewKey]
  207. forKey:CPKeyValueChangeOldKey];
  208. [inverseChangeDictionary removeObjectForKey:CPKeyValueChangeNewKey];
  209. }
  210. else if (changeKind === CPKeyValueChangeRemoval)
  211. {
  212. [inverseChangeDictionary
  213. setObject:CPKeyValueChangeInsertion
  214. forKey:CPKeyValueChangeKindKey];
  215. [inverseChangeDictionary
  216. setObject:[self objectForKey:CPKeyValueChangeOldKey]
  217. forKey:CPKeyValueChangeNewKey];
  218. [inverseChangeDictionary removeObjectForKey:CPKeyValueChangeOldKey];
  219. }
  220. return inverseChangeDictionary;
  221. }
  222. @end
  223. // KVO Options
  224. CPKeyValueObservingOptionNew = 1 << 0;
  225. CPKeyValueObservingOptionOld = 1 << 1;
  226. CPKeyValueObservingOptionInitial = 1 << 2;
  227. CPKeyValueObservingOptionPrior = 1 << 3;
  228. // KVO Change Dictionary Keys
  229. CPKeyValueChangeKindKey = @"CPKeyValueChangeKindKey";
  230. CPKeyValueChangeNewKey = @"CPKeyValueChangeNewKey";
  231. CPKeyValueChangeOldKey = @"CPKeyValueChangeOldKey";
  232. CPKeyValueChangeIndexesKey = @"CPKeyValueChangeIndexesKey";
  233. CPKeyValueChangeNotificationIsPriorKey = @"CPKeyValueChangeNotificationIsPriorKey";
  234. // KVO Change Types
  235. CPKeyValueChangeSetting = 1;
  236. CPKeyValueChangeInsertion = 2;
  237. CPKeyValueChangeRemoval = 3;
  238. CPKeyValueChangeReplacement = 4;
  239. // CPKeyValueSetMutationKind
  240. CPKeyValueUnionSetMutation = 1;
  241. CPKeyValueMinusSetMutation = 2;
  242. CPKeyValueIntersectSetMutation = 3;
  243. CPKeyValueSetSetMutation = 4;
  244. //FIXME: "secret" dict ivar-keys are workaround to support unordered to-many relationships without too many modifications
  245. _CPKeyValueChangeSetMutationObjectsKey = @"_CPKeyValueChangeSetMutationObjectsKey";
  246. _CPKeyValueChangeSetMutationKindKey = @"_CPKeyValueChangeSetMutationKindKey";
  247. _CPKeyValueChangeSetMutationNewValueKey = @"_CPKeyValueChangeSetMutationNewValueKey";
  248. var _changeKindForSetMutationKind = function(mutationKind)
  249. {
  250. switch (mutationKind)
  251. {
  252. case CPKeyValueUnionSetMutation: return CPKeyValueChangeInsertion;
  253. case CPKeyValueMinusSetMutation: return CPKeyValueChangeRemoval;
  254. case CPKeyValueIntersectSetMutation: return CPKeyValueChangeRemoval;
  255. case CPKeyValueSetSetMutation: return CPKeyValueChangeReplacement;
  256. }
  257. };
  258. var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOptionOld,
  259. DependentKeysKey = "$KVODEPENDENT",
  260. KVOProxyKey = "$KVOPROXY";
  261. //rule of thumb: _ methods are called on the real proxy object, others are called on the "fake" proxy object (aka the real object)
  262. /* @ignore */
  263. @implementation _CPKVOProxy : CPObject
  264. {
  265. id _targetObject;
  266. Class _nativeClass;
  267. CPDictionary _changesForKey;
  268. CPDictionary _nestingForKey;
  269. CPDictionary _minOptionsForKey;
  270. Object _observersForKey;
  271. int _observersForKeyLength;
  272. CPSet _replacedKeys;
  273. // TODO: Remove this line when granular notifications are implemented
  274. BOOL _adding @accessors(property=adding);
  275. }
  276. + (id)proxyForObject:(CPObject)anObject
  277. {
  278. var proxy = anObject[KVOProxyKey];
  279. if (proxy)
  280. return proxy;
  281. return [[self alloc] initWithTarget:anObject];
  282. }
  283. - (id)initWithTarget:(id)aTarget
  284. {
  285. if (self = [super init])
  286. {
  287. _targetObject = aTarget;
  288. _nativeClass = [aTarget class];
  289. _observersForKey = {};
  290. _changesForKey = {};
  291. _nestingForKey = {};
  292. _minOptionsForKey = {};
  293. _observersForKeyLength = 0;
  294. [self _replaceClass];
  295. aTarget[KVOProxyKey] = self;
  296. }
  297. return self;
  298. }
  299. - (void)_replaceClass
  300. {
  301. var currentClass = _nativeClass,
  302. kvoClassName = "$KVO_" + class_getName(_nativeClass),
  303. existingKVOClass = objj_lookUpClass(kvoClassName);
  304. if (existingKVOClass)
  305. {
  306. _targetObject.isa = existingKVOClass;
  307. _replacedKeys = existingKVOClass._replacedKeys;
  308. return;
  309. }
  310. var kvoClass = objj_allocateClassPair(currentClass, kvoClassName);
  311. objj_registerClassPair(kvoClass);
  312. _replacedKeys = [CPSet set];
  313. kvoClass._replacedKeys = _replacedKeys;
  314. //copy in the methods from our model subclass
  315. var methods = class_copyMethodList(_CPKVOModelSubclass);
  316. if ([_targetObject isKindOfClass:[CPDictionary class]])
  317. methods = methods.concat(class_copyMethodList(_CPKVOModelDictionarySubclass));
  318. class_addMethods(kvoClass, methods);
  319. _targetObject.isa = kvoClass;
  320. }
  321. - (void)_replaceModifiersForKey:(CPString)aKey
  322. {
  323. if ([_replacedKeys containsObject:aKey] || ![_nativeClass automaticallyNotifiesObserversForKey:aKey])
  324. return;
  325. [_replacedKeys addObject:aKey];
  326. var theClass = _nativeClass,
  327. KVOClass = _targetObject.isa,
  328. capitalizedKey = aKey.charAt(0).toUpperCase() + aKey.substring(1);
  329. // Attribute and To-One Relationships
  330. var setKey_selector = sel_getUid("set" + capitalizedKey + ":"),
  331. setKey_method = class_getInstanceMethod(theClass, setKey_selector);
  332. if (setKey_method)
  333. {
  334. var setKey_method_imp = setKey_method.method_imp;
  335. class_addMethod(KVOClass, setKey_selector, function(self, _cmd, anObject)
  336. {
  337. [self willChangeValueForKey:aKey];
  338. setKey_method_imp(self, _cmd, anObject);
  339. [self didChangeValueForKey:aKey];
  340. }, "");
  341. }
  342. // FIXME: Deprecated.
  343. var _setKey_selector = sel_getUid("_set" + capitalizedKey + ":"),
  344. _setKey_method = class_getInstanceMethod(theClass, _setKey_selector);
  345. if (_setKey_method)
  346. {
  347. var _setKey_method_imp = _setKey_method.method_imp;
  348. class_addMethod(KVOClass, _setKey_selector, function(self, _cmd, anObject)
  349. {
  350. [self willChangeValueForKey:aKey];
  351. _setKey_method_imp(self, _cmd, anObject);
  352. [self didChangeValueForKey:aKey];
  353. }, "");
  354. }
  355. // Ordered To-Many Relationships
  356. var insertObject_inKeyAtIndex_selector = sel_getUid("insertObject:in" + capitalizedKey + "AtIndex:"),
  357. insertObject_inKeyAtIndex_method =
  358. class_getInstanceMethod(theClass, insertObject_inKeyAtIndex_selector),
  359. insertKey_atIndexes_selector = sel_getUid("insert" + capitalizedKey + ":atIndexes:"),
  360. insertKey_atIndexes_method =
  361. class_getInstanceMethod(theClass, insertKey_atIndexes_selector),
  362. removeObjectFromKeyAtIndex_selector = sel_getUid("removeObjectFrom" + capitalizedKey + "AtIndex:"),
  363. removeObjectFromKeyAtIndex_method =
  364. class_getInstanceMethod(theClass, removeObjectFromKeyAtIndex_selector),
  365. removeKeyAtIndexes_selector = sel_getUid("remove" + capitalizedKey + "AtIndexes:"),
  366. removeKeyAtIndexes_method = class_getInstanceMethod(theClass, removeKeyAtIndexes_selector);
  367. if ((insertObject_inKeyAtIndex_method || insertKey_atIndexes_method) &&
  368. (removeObjectFromKeyAtIndex_method || removeKeyAtIndexes_method))
  369. {
  370. if (insertObject_inKeyAtIndex_method)
  371. {
  372. var insertObject_inKeyAtIndex_method_imp = insertObject_inKeyAtIndex_method.method_imp;
  373. class_addMethod(KVOClass, insertObject_inKeyAtIndex_selector, function(self, _cmd, anObject, anIndex)
  374. {
  375. [self willChange:CPKeyValueChangeInsertion
  376. valuesAtIndexes:[CPIndexSet indexSetWithIndex:anIndex]
  377. forKey:aKey];
  378. insertObject_inKeyAtIndex_method_imp(self, _cmd, anObject, anIndex);
  379. [self didChange:CPKeyValueChangeInsertion
  380. valuesAtIndexes:[CPIndexSet indexSetWithIndex:anIndex]
  381. forKey:aKey];
  382. }, "");
  383. }
  384. if (insertKey_atIndexes_method)
  385. {
  386. var insertKey_atIndexes_method_imp = insertKey_atIndexes_method.method_imp;
  387. class_addMethod(KVOClass, insertKey_atIndexes_selector, function(self, _cmd, objects, indexes)
  388. {
  389. [self willChange:CPKeyValueChangeInsertion
  390. valuesAtIndexes:[indexes copy]
  391. forKey:aKey];
  392. insertKey_atIndexes_method_imp(self, _cmd, objects, indexes);
  393. [self didChange:CPKeyValueChangeInsertion
  394. valuesAtIndexes:[indexes copy]
  395. forKey:aKey];
  396. }, "");
  397. }
  398. if (removeObjectFromKeyAtIndex_method)
  399. {
  400. var removeObjectFromKeyAtIndex_method_imp = removeObjectFromKeyAtIndex_method.method_imp;
  401. class_addMethod(KVOClass, removeObjectFromKeyAtIndex_selector, function(self, _cmd, anIndex)
  402. {
  403. [self willChange:CPKeyValueChangeRemoval
  404. valuesAtIndexes:[CPIndexSet indexSetWithIndex:anIndex]
  405. forKey:aKey];
  406. removeObjectFromKeyAtIndex_method_imp(self, _cmd, anIndex);
  407. [self didChange:CPKeyValueChangeRemoval
  408. valuesAtIndexes:[CPIndexSet indexSetWithIndex:anIndex]
  409. forKey:aKey];
  410. }, "");
  411. }
  412. if (removeKeyAtIndexes_method)
  413. {
  414. var removeKeyAtIndexes_method_imp = removeKeyAtIndexes_method.method_imp;
  415. class_addMethod(KVOClass, removeKeyAtIndexes_selector, function(self, _cmd, indexes)
  416. {
  417. [self willChange:CPKeyValueChangeRemoval
  418. valuesAtIndexes:[indexes copy]
  419. forKey:aKey];
  420. removeKeyAtIndexes_method_imp(self, _cmd, indexes);
  421. [self didChange:CPKeyValueChangeRemoval
  422. valuesAtIndexes:[indexes copy]
  423. forKey:aKey];
  424. }, "");
  425. }
  426. // These are optional.
  427. var replaceObjectInKeyAtIndex_withObject_selector =
  428. sel_getUid("replaceObjectIn" + capitalizedKey + "AtIndex:withObject:"),
  429. replaceObjectInKeyAtIndex_withObject_method =
  430. class_getInstanceMethod(theClass, replaceObjectInKeyAtIndex_withObject_selector);
  431. if (replaceObjectInKeyAtIndex_withObject_method)
  432. {
  433. var replaceObjectInKeyAtIndex_withObject_method_imp =
  434. replaceObjectInKeyAtIndex_withObject_method.method_imp;
  435. class_addMethod(KVOClass, replaceObjectInKeyAtIndex_withObject_selector,
  436. function(self, _cmd, anIndex, anObject)
  437. {
  438. [self willChange:CPKeyValueChangeReplacement
  439. valuesAtIndexes:[CPIndexSet indexSetWithIndex:anIndex]
  440. forKey:aKey];
  441. replaceObjectInKeyAtIndex_withObject_method_imp(self, _cmd, anIndex, anObject);
  442. [self didChange:CPKeyValueChangeReplacement
  443. valuesAtIndexes:[CPIndexSet indexSetWithIndex:anIndex]
  444. forKey:aKey];
  445. }, "");
  446. }
  447. var replaceKeyAtIndexes_withKey_selector =
  448. sel_getUid("replace" + capitalizedKey + "AtIndexes:with" + capitalizedKey + ":"),
  449. replaceKeyAtIndexes_withKey_method =
  450. class_getInstanceMethod(theClass, replaceKeyAtIndexes_withKey_selector);
  451. if (replaceKeyAtIndexes_withKey_method)
  452. {
  453. var replaceKeyAtIndexes_withKey_method_imp = replaceKeyAtIndexes_withKey_method.method_imp;
  454. class_addMethod(KVOClass, replaceKeyAtIndexes_withKey_selector, function(self, _cmd, indexes, objects)
  455. {
  456. [self willChange:CPKeyValueChangeReplacement
  457. valuesAtIndexes:[indexes copy]
  458. forKey:aKey];
  459. replaceObjectInKeyAtIndex_withObject_method_imp(self, _cmd, indexes, objects);
  460. [self didChange:CPKeyValueChangeReplacement
  461. valuesAtIndexes:[indexes copy]
  462. forKey:aKey];
  463. }, "");
  464. }
  465. }
  466. // Unordered To-Many Relationships
  467. var addKeyObject_selector = sel_getUid("add" + capitalizedKey + "Object:"),
  468. addKeyObject_method = class_getInstanceMethod(theClass, addKeyObject_selector),
  469. addKey_selector = sel_getUid("add" + capitalizedKey + ":"),
  470. addKey_method = class_getInstanceMethod(theClass, addKey_selector),
  471. removeKeyObject_selector = sel_getUid("remove" + capitalizedKey + "Object:"),
  472. removeKeyObject_method = class_getInstanceMethod(theClass, removeKeyObject_selector),
  473. removeKey_selector = sel_getUid("remove" + capitalizedKey + ":"),
  474. removeKey_method = class_getInstanceMethod(theClass, removeKey_selector);
  475. if ((addKeyObject_method || addKey_method) && (removeKeyObject_method || removeKey_method))
  476. {
  477. if (addKeyObject_method)
  478. {
  479. var addKeyObject_method_imp = addKeyObject_method.method_imp;
  480. class_addMethod(KVOClass, addKeyObject_selector, function(self, _cmd, anObject)
  481. {
  482. [self willChangeValueForKey:aKey
  483. withSetMutation:CPKeyValueUnionSetMutation
  484. usingObjects:[CPSet setWithObject:anObject]];
  485. addKeyObject_method_imp(self, _cmd, anObject);
  486. [self didChangeValueForKey:aKey
  487. withSetMutation:CPKeyValueUnionSetMutation
  488. usingObjects:[CPSet setWithObject:anObject]];
  489. }, "");
  490. }
  491. if (addKey_method)
  492. {
  493. var addKey_method_imp = addKey_method.method_imp;
  494. class_addMethod(KVOClass, addKey_selector, function(self, _cmd, objects)
  495. {
  496. [self willChangeValueForKey:aKey
  497. withSetMutation:CPKeyValueUnionSetMutation
  498. usingObjects:[objects copy]];
  499. addKey_method_imp(self, _cmd, objects);
  500. [self didChangeValueForKey:aKey
  501. withSetMutation:CPKeyValueUnionSetMutation
  502. usingObjects:[objects copy]];
  503. }, "");
  504. }
  505. if (removeKeyObject_method)
  506. {
  507. var removeKeyObject_method_imp = removeKeyObject_method.method_imp;
  508. class_addMethod(KVOClass, removeKeyObject_selector, function(self, _cmd, anObject)
  509. {
  510. [self willChangeValueForKey:aKey
  511. withSetMutation:CPKeyValueMinusSetMutation
  512. usingObjects:[CPSet setWithObject:anObject]];
  513. removeKeyObject_method_imp(self, _cmd, anObject);
  514. [self didChangeValueForKey:aKey
  515. withSetMutation:CPKeyValueMinusSetMutation
  516. usingObjects:[CPSet setWithObject:anObject]];
  517. }, "");
  518. }
  519. if (removeKey_method)
  520. {
  521. var removeKey_method_imp = removeKey_method.method_imp;
  522. class_addMethod(KVOClass, removeKey_selector, function(self, _cmd, objects)
  523. {
  524. [self willChangeValueForKey:aKey
  525. withSetMutation:CPKeyValueMinusSetMutation
  526. usingObjects:[objects copy]];
  527. removeKey_method_imp(self, _cmd, objects);
  528. [self didChangeValueForKey:aKey
  529. withSetMutation:CPKeyValueMinusSetMutation
  530. usingObjects:[objects copy]];
  531. }, "");
  532. }
  533. // intersect<Key>: is optional.
  534. var intersectKey_selector = sel_getUid("intersect" + capitalizedKey + ":"),
  535. intersectKey_method = class_getInstanceMethod(theClass, intersectKey_selector);
  536. if (intersectKey_method)
  537. {
  538. var intersectKey_method_imp = intersectKey_method.method_imp;
  539. class_addMethod(KVOClass, intersectKey_selector, function(self, _cmd, aSet)
  540. {
  541. [self willChangeValueForKey:aKey
  542. withSetMutation:CPKeyValueIntersectSetMutation
  543. usingObjects:[aSet copy]];
  544. intersectKey_method_imp(self, _cmd, aSet);
  545. [self didChangeValueForKey:aKey
  546. withSetMutation:CPKeyValueIntersectSetMutation
  547. usingObjects:[aSet copy]];
  548. }, "");
  549. }
  550. }
  551. var affectingKeys = [[_nativeClass keyPathsForValuesAffectingValueForKey:aKey] allObjects],
  552. affectingKeysCount = affectingKeys ? affectingKeys.length : 0;
  553. if (!affectingKeysCount)
  554. return;
  555. var dependentKeysForClass = _nativeClass[DependentKeysKey];
  556. if (!dependentKeysForClass)
  557. {
  558. dependentKeysForClass = {};
  559. _nativeClass[DependentKeysKey] = dependentKeysForClass;
  560. }
  561. while (affectingKeysCount--)
  562. {
  563. var affectingKey = affectingKeys[affectingKeysCount],
  564. affectedKeys = dependentKeysForClass[affectingKey];
  565. if (!affectedKeys)
  566. {
  567. affectedKeys = [CPSet new];
  568. dependentKeysForClass[affectingKey] = affectedKeys;
  569. }
  570. [affectedKeys addObject:aKey];
  571. //observe key paths of objects other then ourselves, so we are notified of the changes
  572. //use CPKeyValueObservingOptionPrior to ensure proper wrapping around changes
  573. //so CPKeyValueObservingOptionPrior and CPKeyValueObservingOptionOld can be fulfilled even for dependent keys
  574. if (affectingKey.indexOf(@".") !== -1)
  575. [_targetObject addObserver:self forKeyPath:affectingKey options:CPKeyValueObservingOptionPrior | kvoNewAndOld context:nil];
  576. else
  577. [self _replaceModifiersForKey:affectingKey];
  578. }
  579. }
  580. - (void)observeValueForKeyPath:(CPString)theKeyPath ofObject:(id)theObject change:(CPDictionary)theChanges context:(id)theContext
  581. {
  582. // Fire change events for the dependent keys
  583. var dependentKeysForClass = _nativeClass[DependentKeysKey],
  584. dependantKeys = [dependentKeysForClass[theKeyPath] allObjects],
  585. isBeforeFlag = !![theChanges objectForKey:CPKeyValueChangeNotificationIsPriorKey];
  586. for (var i = 0; i < [dependantKeys count]; i++)
  587. {
  588. var dependantKey = [dependantKeys objectAtIndex:i];
  589. [self _sendNotificationsForKey:dependantKey changeOptions:theChanges isBefore:isBeforeFlag];
  590. }
  591. }
  592. - (void)_addObserver:(id)anObserver forKeyPath:(CPString)aPath options:(CPKeyValueObservingOptions)options context:(id)aContext
  593. {
  594. if (!anObserver)
  595. return;
  596. var forwarder = nil;
  597. if (aPath.indexOf('.') !== CPNotFound && aPath.charAt(0) !== '@')
  598. forwarder = [[_CPKVOForwardingObserver alloc] initWithKeyPath:aPath object:_targetObject observer:anObserver options:options context:aContext];
  599. else
  600. [self _replaceModifiersForKey:aPath];
  601. var observers = _observersForKey[aPath];
  602. if (!observers)
  603. {
  604. observers = @{};
  605. _observersForKey[aPath] = observers;
  606. _observersForKeyLength++;
  607. }
  608. [observers setObject:_CPKVOInfoMake(anObserver, options, aContext, forwarder) forKey:[anObserver UID]];
  609. if (options & CPKeyValueObservingOptionInitial)
  610. {
  611. var changes;
  612. if (options & CPKeyValueObservingOptionNew)
  613. {
  614. var newValue = [_targetObject valueForKeyPath:aPath];
  615. if (newValue == nil)
  616. newValue = [CPNull null];
  617. changes = @{ CPKeyValueChangeKindKey: CPKeyValueChangeSetting, CPKeyValueChangeNewKey: newValue };
  618. } else {
  619. changes = @{ CPKeyValueChangeKindKey: CPKeyValueChangeSetting };
  620. }
  621. [anObserver observeValueForKeyPath:aPath ofObject:_targetObject change:changes context:aContext];
  622. }
  623. }
  624. - (void)_removeObserver:(id)anObserver forKeyPath:(CPString)aPath
  625. {
  626. var observers = _observersForKey[aPath];
  627. if (!observers)
  628. {
  629. // TODO: Remove this line when granular notifications are implemented
  630. if (!_adding)
  631. CPLog.warn(@"Cannot remove an observer %@ for the key path \"%@\" from %@ because it is not registered as an observer.", _targetObject, aPath, anObserver);
  632. return;
  633. }
  634. if (aPath.indexOf('.') != CPNotFound)
  635. {
  636. // During cib instantiation, it is possible for the forwarder to not yet be available,
  637. // so we have to check for nil.
  638. var observer = [observers objectForKey:[anObserver UID]],
  639. forwarder = observer ? observer.forwarder : nil;
  640. [forwarder finalize];
  641. }
  642. [observers removeObjectForKey:[anObserver UID]];
  643. if (![observers count])
  644. {
  645. _observersForKeyLength--;
  646. delete _observersForKey[aPath];
  647. }
  648. if (!_observersForKeyLength)
  649. {
  650. _targetObject.isa = _nativeClass; //restore the original class
  651. delete _targetObject[KVOProxyKey];
  652. }
  653. }
  654. //FIXME: We do not compute and cache if CPKeyValueObservingOptionOld is needed, so we may do unnecessary work
  655. - (void)_sendNotificationsForKey:(CPString)aKey changeOptions:(CPDictionary)changeOptions isBefore:(BOOL)isBefore
  656. {
  657. var changes = _changesForKey[aKey],
  658. observers = [_observersForKey[aKey] allValues],
  659. observersMinimumOptions = 0;
  660. if (isBefore)
  661. {
  662. if (changes)
  663. {
  664. // "willChange:X" nesting.
  665. var level = _nestingForKey[aKey];
  666. if (!level)
  667. [CPException raise:CPInternalInconsistencyException reason:@"_changesForKey without _nestingForKey"];
  668. _nestingForKey[aKey] = level + 1;
  669. // Only notify on the first willChange..., silently note any following nested calls.
  670. return;
  671. }
  672. _nestingForKey[aKey] = 1;
  673. // Get the combined minimum of the ...Old and ...New options for all observers
  674. var count = observers ? observers.length : 0;
  675. while (count--)
  676. {
  677. var observerInfo = observers[count];
  678. observersMinimumOptions |= observerInfo.options & kvoNewAndOld;
  679. }
  680. _minOptionsForKey[aKey] = observersMinimumOptions;
  681. changes = changeOptions;
  682. if (observersMinimumOptions & CPKeyValueObservingOptionOld)
  683. {
  684. var indexes = [changes objectForKey:CPKeyValueChangeIndexesKey],
  685. setMutationKind = changes[_CPKeyValueChangeSetMutationKindKey];
  686. if (setMutationKind)
  687. {
  688. var setMutationObjects = [changes[_CPKeyValueChangeSetMutationObjectsKey] copy],
  689. setExistingObjects = [[_targetObject valueForKey: aKey] copy];
  690. if (setMutationKind == CPKeyValueMinusSetMutation)
  691. {
  692. [setExistingObjects intersectSet: setMutationObjects];
  693. [changes setValue:setExistingObjects forKey:CPKeyValueChangeOldKey];
  694. }
  695. else if (setMutationKind === CPKeyValueIntersectSetMutation || setMutationKind === CPKeyValueSetSetMutation)
  696. {
  697. [setExistingObjects minusSet: setMutationObjects];
  698. [changes setValue:setExistingObjects forKey:CPKeyValueChangeOldKey];
  699. }
  700. //for unordered to-many relationships (CPSet) even new values can only be calculated before!!!
  701. if (setMutationKind === CPKeyValueUnionSetMutation || setMutationKind === CPKeyValueSetSetMutation)
  702. {
  703. [setMutationObjects minusSet: setExistingObjects];
  704. //hide new value (for CPKeyValueObservingOptionPrior messages)
  705. //as long as "didChangeValue..." is not yet called!
  706. changes[_CPKeyValueChangeSetMutationNewValueKey] = setMutationObjects;
  707. }
  708. }
  709. else if (indexes)
  710. {
  711. var type = [changes objectForKey:CPKeyValueChangeKindKey];
  712. // for ordered to-many relationships, oldvalue is only sensible for replace and remove
  713. if (type === CPKeyValueChangeReplacement || type === CPKeyValueChangeRemoval)
  714. {
  715. //FIXME: do we need to go through and replace "" with CPNull?
  716. var newValues = [[_targetObject mutableArrayValueForKeyPath:aKey] objectsAtIndexes:indexes];
  717. [changes setValue:newValues forKey:CPKeyValueChangeOldKey];
  718. }
  719. }
  720. else
  721. {
  722. var oldValue = [_targetObject valueForKey:aKey];
  723. if (oldValue === nil || oldValue === undefined)
  724. oldValue = [CPNull null];
  725. [changes setObject:oldValue forKey:CPKeyValueChangeOldKey];
  726. }
  727. }
  728. [changes setObject:1 forKey:CPKeyValueChangeNotificationIsPriorKey];
  729. _changesForKey[aKey] = changes;
  730. // Clear ...New option as it should never be sent for a ...Prior option
  731. observersMinimumOptions &= ~CPKeyValueObservingOptionNew;
  732. }
  733. else
  734. {
  735. var level = _nestingForKey[aKey];
  736. if (!changes || !level)
  737. {
  738. if (_targetObject._willChangeMessageCounter && _targetObject._willChangeMessageCounter[aKey])
  739. {
  740. // Close unobserved willChange for a given key.
  741. _targetObject._willChangeMessageCounter[aKey] -= 1;
  742. if (!_targetObject._willChangeMessageCounter[aKey])
  743. delete _targetObject._willChangeMessageCounter[aKey];
  744. return;
  745. }
  746. else
  747. [CPException raise:@"CPKeyValueObservingException" reason:@"'didChange...' message called without prior call of 'willChange...'"];
  748. }
  749. _nestingForKey[aKey] = level - 1;
  750. if (level - 1 > 0)
  751. {
  752. // willChange... was called multiple times. Only fire observation notifications when
  753. // didChange... has been called an equal number of times.
  754. return;
  755. }
  756. delete _nestingForKey[aKey];
  757. [changes removeObjectForKey:CPKeyValueChangeNotificationIsPriorKey];
  758. observersMinimumOptions = _minOptionsForKey[aKey];
  759. if (observersMinimumOptions & CPKeyValueObservingOptionNew)
  760. {
  761. var indexes = [changes objectForKey:CPKeyValueChangeIndexesKey],
  762. setMutationKind = changes[_CPKeyValueChangeSetMutationKindKey];
  763. if (setMutationKind)
  764. {
  765. //old and new values for unordered to-many relationships can only be calculated before
  766. //set recalculated hidden new value as soon as "didChangeValue..." is called!
  767. var newValue = changes[_CPKeyValueChangeSetMutationNewValueKey];
  768. [changes setValue:newValue forKey:CPKeyValueChangeNewKey];
  769. //delete hidden values
  770. delete changes[_CPKeyValueChangeSetMutationNewValueKey];
  771. delete changes[_CPKeyValueChangeSetMutationObjectsKey];
  772. delete changes[_CPKeyValueChangeSetMutationKindKey];
  773. }
  774. else if (indexes)
  775. {
  776. var type = [changes objectForKey:CPKeyValueChangeKindKey];
  777. // for ordered to-many relationships, newvalue is only sensible for replace and insert
  778. if (type == CPKeyValueChangeReplacement || type == CPKeyValueChangeInsertion)
  779. {
  780. //FIXME: do we need to go through and replace "" with CPNull?
  781. var newValues = [[_targetObject mutableArrayValueForKeyPath:aKey] objectsAtIndexes:indexes];
  782. [changes setValue:newValues forKey:CPKeyValueChangeNewKey];
  783. }
  784. }
  785. else
  786. {
  787. var newValue = [_targetObject valueForKey:aKey];
  788. if (newValue === nil || newValue === undefined)
  789. newValue = [CPNull null];
  790. [changes setObject:newValue forKey:CPKeyValueChangeNewKey];
  791. }
  792. }
  793. delete _minOptionsForKey[aKey];
  794. delete _changesForKey[aKey];
  795. }
  796. var count = observers ? observers.length : 0,
  797. changesCache = {};
  798. while (count--)
  799. {
  800. var observerInfo = observers[count],
  801. options = observerInfo.options,
  802. onlyNewAndOldOptions = options & kvoNewAndOld,
  803. observerChanges = nil;
  804. if (isBefore)
  805. {
  806. // Only send 'observeValueForKeyPath:' for '...Prior' option when handling 'willChangeValue...'
  807. if (options & CPKeyValueObservingOptionPrior)
  808. {
  809. observerChanges = changes;
  810. // The new values are not yet created in the change dictionary so remove ...New option to get a working cache below
  811. onlyNewAndOldOptions &= ~CPKeyValueObservingOptionNew;
  812. }
  813. }
  814. else
  815. {
  816. observerChanges = changes;
  817. }
  818. if (observerChanges)
  819. {
  820. // Don't change the 'change' dictionary when the observer wants the minimum options.
  821. // The ...New option is remved above for the ...Prior case
  822. if (onlyNewAndOldOptions !== observersMinimumOptions)
  823. {
  824. // Use a subset of the 'change' dictionary. First try to find it in the cache
  825. observerChanges = changesCache[onlyNewAndOldOptions];
  826. if (!observerChanges)
  827. {
  828. // Not in the cache. Build a new dictionary and store it in the cache
  829. changesCache[onlyNewAndOldOptions] = observerChanges = [changes mutableCopy];
  830. if (!(onlyNewAndOldOptions & CPKeyValueObservingOptionOld))
  831. [observerChanges removeObjectForKey:CPKeyValueChangeOldKey];
  832. if (!(onlyNewAndOldOptions & CPKeyValueObservingOptionNew))
  833. [observerChanges removeObjectForKey:CPKeyValueChangeNewKey];
  834. }
  835. }
  836. [observerInfo.observer observeValueForKeyPath:aKey ofObject:_targetObject change:observerChanges context:observerInfo.context];
  837. }
  838. }
  839. var dependentKeysMap = _nativeClass[DependentKeysKey];
  840. if (!dependentKeysMap)
  841. return;
  842. var dependentKeyPaths = [dependentKeysMap[aKey] allObjects];
  843. if (!dependentKeyPaths)
  844. return;
  845. var index = 0,
  846. count = [dependentKeyPaths count];
  847. for (; index < count; ++index)
  848. {
  849. var keyPath = dependentKeyPaths[index];
  850. [self _sendNotificationsForKey:keyPath
  851. changeOptions:isBefore ? [changeOptions copy] : _changesForKey[keyPath]
  852. isBefore:isBefore];
  853. }
  854. }
  855. @end
  856. @implementation _CPKVOModelSubclass : CPObject
  857. {
  858. }
  859. - (void)willChangeValueForKey:(CPString)aKey
  860. {
  861. var superClass = [self class],
  862. methodSelector = @selector(willChangeValueForKey:),
  863. methodImp = class_getMethodImplementation(superClass, methodSelector);
  864. methodImp(self, methodSelector, aKey);
  865. if (!aKey)
  866. return;
  867. var changeOptions = @{ CPKeyValueChangeKindKey: CPKeyValueChangeSetting };
  868. [[_CPKVOProxy proxyForObject:self] _sendNotificationsForKey:aKey changeOptions:changeOptions isBefore:YES];
  869. }
  870. - (void)didChangeValueForKey:(CPString)aKey
  871. {
  872. var superClass = [self class],
  873. methodSelector = @selector(didChangeValueForKey:),
  874. methodImp = class_getMethodImplementation(superClass, methodSelector);
  875. methodImp(self, methodSelector, aKey);
  876. if (!aKey)
  877. return;
  878. [[_CPKVOProxy proxyForObject:self] _sendNotificationsForKey:aKey changeOptions:nil isBefore:NO];
  879. }
  880. - (void)willChange:(CPKeyValueChange)change valuesAtIndexes:(CPIndexSet)indexes forKey:(CPString)aKey
  881. {
  882. var superClass = [self class],
  883. methodSelector = @selector(willChange:valuesAtIndexes:forKey:),
  884. methodImp = class_getMethodImplementation(superClass, methodSelector);
  885. methodImp(self, methodSelector, change, indexes, aKey);
  886. if (!aKey)
  887. return;
  888. var changeOptions = @{ CPKeyValueChangeKindKey: change, CPKeyValueChangeIndexesKey: indexes };
  889. [[_CPKVOProxy proxyForObject:self] _sendNotificationsForKey:aKey changeOptions:changeOptions isBefore:YES];
  890. }
  891. - (void)didChange:(CPKeyValueChange)change valuesAtIndexes:(CPIndexSet)indexes forKey:(CPString)aKey
  892. {
  893. var superClass = [self class],
  894. methodSelector = @selector(didChange:valuesAtIndexes:forKey:),
  895. methodImp = class_getMethodImplementation(superClass, methodSelector);
  896. methodImp(self, methodSelector, change, indexes, aKey);
  897. if (!aKey)
  898. return;
  899. [[_CPKVOProxy proxyForObject:self] _sendNotificationsForKey:aKey changeOptions:nil isBefore:NO];
  900. }
  901. - (void)willChangeValueForKey:(CPString)aKey withSetMutation:(CPKeyValueSetMutationKind)mutationKind usingObjects:(CPSet)objects
  902. {
  903. var superClass = [self class],
  904. methodSelector = @selector(willChangeValueForKey:withSetMutation:usingObjects:),
  905. methodImp = class_getMethodImplementation(superClass, methodSelector);
  906. methodImp(self, methodSelector, aKey, mutationKind, objects);
  907. if (!aKey)
  908. return;
  909. var changeKind = _changeKindForSetMutationKind(mutationKind),
  910. changeOptions = @{ CPKeyValueChangeKindKey: changeKind };
  911. //set hidden change-dict ivars to support unordered to-many relationships
  912. changeOptions[_CPKeyValueChangeSetMutationObjectsKey] = objects;
  913. changeOptions[_CPKeyValueChangeSetMutationKindKey] = mutationKind;
  914. [[_CPKVOProxy proxyForObject:self] _sendNotificationsForKey:aKey changeOptions:changeOptions isBefore:YES];
  915. }
  916. - (void)didChangeValueForKey:(CPString)aKey withSetMutation:(CPKeyValueSetMutationKind)mutationKind usingObjects:(CPSet)objects
  917. {
  918. var superClass = [self class],
  919. methodSelector = @selector(didChangeValueForKey:withSetMutation:usingObjects:),
  920. methodImp = class_getMethodImplementation(superClass, methodSelector);
  921. methodImp(self, methodSelector, aKey, mutationKind, objects);
  922. if (!aKey)
  923. return;
  924. [[_CPKVOProxy proxyForObject:self] _sendNotificationsForKey:aKey changeOptions:nil isBefore:NO];
  925. }
  926. - (Class)class
  927. {
  928. return self[KVOProxyKey]._nativeClass;
  929. }
  930. - (Class)superclass
  931. {
  932. return [[self class] superclass];
  933. }
  934. - (BOOL)isKindOfClass:(Class)aClass
  935. {
  936. return [[self class] isSubclassOfClass:aClass];
  937. }
  938. - (BOOL)isMemberOfClass:(Class)aClass
  939. {
  940. return [self class] == aClass;
  941. }
  942. - (CPString)className
  943. {
  944. return [self class].name;
  945. }
  946. @end
  947. @implementation _CPKVOModelDictionarySubclass : CPObject
  948. {
  949. }
  950. - (void)removeAllObjects
  951. {
  952. var keys = [self allKeys],
  953. count = [keys count],
  954. i = 0;
  955. for (; i < count; i++)
  956. [self willChangeValueForKey:keys[i]];
  957. var superClass = [self class],
  958. methodSelector = @selector(removeAllObjects),
  959. methodImp = class_getMethodImplementation(superClass, methodSelector);
  960. methodImp(self, methodSelector);
  961. for (i = 0; i < count; i++)
  962. [self didChangeValueForKey:keys[i]];
  963. }
  964. - (void)removeObjectForKey:(id)aKey
  965. {
  966. [self willChangeValueForKey:aKey];
  967. var superClass = [self class],
  968. methodSelector = @selector(removeObjectForKey:),
  969. methodImp = class_getMethodImplementation(superClass, methodSelector);
  970. methodImp(self, methodSelector, aKey);
  971. [self didChangeValueForKey:aKey];
  972. }
  973. - (void)setObject:(id)anObject forKey:(id)aKey
  974. {
  975. [self willChangeValueForKey:aKey];
  976. var superClass = [self class],
  977. methodSelector = @selector(setObject:forKey:),
  978. methodImp = class_getMethodImplementation(superClass, methodSelector);
  979. methodImp(self, methodSelector, anObject, aKey);
  980. [self didChangeValueForKey:aKey];
  981. }
  982. @end
  983. @implementation _CPKVOForwardingObserver : CPObject
  984. {
  985. id _object;
  986. id _observer;
  987. id _context;
  988. unsigned _options;
  989. //a.b
  990. CPString _firstPart; //a
  991. CPString _secondPart; //b
  992. id _value;
  993. }
  994. - (id)initWithKeyPath:(CPString)aKeyPath object:(id)anObject observer:(id)anObserver options:(unsigned)options context:(id)aContext
  995. {
  996. self = [super init];
  997. _context = aContext;
  998. _observer = anObserver;
  999. _object = anObject;
  1000. _options = options;
  1001. var dotIndex = aKeyPath.indexOf('.');
  1002. if (dotIndex === CPNotFound)
  1003. [CPException raise:CPInvalidArgumentException reason:"Created _CPKVOForwardingObserver without compound key path: " + aKeyPath];
  1004. _firstPart = aKeyPath.substring(0, dotIndex);
  1005. _secondPart = aKeyPath.substring(dotIndex + 1);
  1006. //become an observer of the first part of our key (a)
  1007. [_object addObserver:self forKeyPath:_firstPart options:_options context:nil];
  1008. //the current value of a (not the value of a.b)
  1009. _value = [_object valueForKey:_firstPart];
  1010. if (_value)
  1011. [_value addObserver:self forKeyPath:_secondPart options:_options context:nil]; //we're observing b on current a
  1012. return self;
  1013. }
  1014. - (void)observeValueForKeyPath:(CPString)aKeyPath ofObject:(id)anObject change:(CPDictionary)changes context:(id)aContext
  1015. {
  1016. if (aKeyPath === _firstPart)
  1017. {
  1018. var pathChanges = [CPMutableDictionary dictionaryWithObject:CPKeyValueChangeSetting forKey:CPKeyValueChangeKindKey];
  1019. if (_options & CPKeyValueObservingOptionOld)
  1020. {
  1021. var oldValue = [_value valueForKeyPath:_secondPart];
  1022. [pathChanges setObject:oldValue != null ? oldValue : [CPNull null] forKey:CPKeyValueChangeOldKey];
  1023. }
  1024. if (_options & CPKeyValueObservingOptionNew)
  1025. {
  1026. var newValue = [_object valueForKeyPath:_firstPart + "." + _secondPart];
  1027. [pathChanges setObject:newValue != null ? newValue : [CPNull null] forKey:CPKeyValueChangeNewKey];
  1028. }
  1029. [_observer observeValueForKeyPath:_firstPart + "." + _secondPart ofObject:_object change:pathChanges context:_context];
  1030. //since a has changed, we should remove ourselves as an observer of the old a, and observe the new one
  1031. if (_value)
  1032. [_value removeObserver:self forKeyPath:_secondPart];
  1033. _value = [_object valueForKey:_firstPart];
  1034. if (_value)
  1035. [_value addObserver:self forKeyPath:_secondPart options:_options context:nil];
  1036. }
  1037. else
  1038. {
  1039. //a is the same, but a.b has changed -- nothing to do but forward this message along
  1040. [_observer observeValueForKeyPath:_firstPart + "." + aKeyPath ofObject:_object change:changes context:_context];
  1041. }
  1042. }
  1043. - (void)finalize
  1044. {
  1045. if (_value)
  1046. [_value removeObserver:self forKeyPath:_secondPart];
  1047. [_object removeObserver:self forKeyPath:_firstPart];
  1048. _object = nil;
  1049. _observer = nil;
  1050. _context = nil;
  1051. _value = nil;
  1052. }
  1053. @end
  1054. var _CPKVOInfoMake = function(anObserver, theOptions, aContext, aForwarder)
  1055. {
  1056. return {
  1057. observer: anObserver,
  1058. options: theOptions,
  1059. context: aContext,
  1060. forwarder: aForwarder
  1061. };
  1062. };
  1063. @import "CPArray+KVO.j"
  1064. @import "CPSet+KVO.j"