/ CoreData / NSManagedObjectContext.m
NSManagedObjectContext.m
  1  /* Copyright(c)2008 Dan Knapp
  2  
  3  Permission is hereby granted, free of charge, to any person obtaining a copy of
  4  this software and associated documentation files(the "Software"), to deal in the
  5  Software without restriction, including without limitation the rights to use,
  6  copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
  7  Software, and to permit persons to whom the Software is furnished to do so,
  8  subject to the following conditions:
  9  
 10  The above copyright notice and this permission notice shall be included in all
 11  copies or substantial portions of the Software.
 12  
 13  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 14  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 15  FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 16  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 17  IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 18  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
 19  #import "NSEntityDescription-Private.h"
 20  #import "NSManagedObject-Private.h"
 21  #import "NSManagedObjectID-Private.h"
 22  #import "NSPersistentStoreCoordinator-Private.h"
 23  #import <CoreData/CoreDataErrors.h>
 24  #import <CoreData/NSAtomicStore.h>
 25  #import <CoreData/NSEntityDescription.h>
 26  #import <CoreData/NSFetchRequest.h>
 27  #import <CoreData/NSManagedObject.h>
 28  #import <CoreData/NSManagedObjectContext.h>
 29  #import <CoreData/NSManagedObjectModel.h>
 30  #import <CoreData/NSPersistentStore.h>
 31  #import <Foundation/NSRaise.h>
 32  #import <Foundation/NSUndoManager.h>
 33  
 34  NSString *const NSManagedObjectContextWillSaveNotification =
 35          @"NSManagedObjectContextWillSaveNotification";
 36  NSString *const NSManagedObjectContextDidSaveNotification =
 37          @"NSManagedObjectContextDidSaveNotification";
 38  
 39  NSString *const NSInsertedObjectsKey = @"NSInsertedObjectsKey";
 40  NSString *const NSUpdatedObjectsKey = @"NSUpdatedObjectsKey";
 41  NSString *const NSDeletedObjectsKey = @"NSDeletedObjectsKey";
 42  NSString *const NSRefreshedObjectsKey = @"NSRefreshedObjectsKey";
 43  NSString *const NSInvalidatedObjectsKey = @"NSInvalidatedObjectsKey";
 44  NSString *const NSInvalidatedAllObjectsKey = @"NSInvalidatedAllObjectsKey";
 45  NSString *const NSAffectedObjectsErrorKey = @"NSAffectedObjectsErrorKey";
 46  
 47  @interface NSAtomicStore (private)
 48  - (void) _uniqueObjectID: (NSManagedObjectID *) objectID;
 49  @end
 50  
 51  @implementation NSManagedObjectContext
 52  
 53  - init {
 54      _lock = [[NSLock alloc] init];
 55      _storeCoordinator = nil;
 56      _undoManager = [[NSUndoManager alloc] init];
 57      _registeredObjects = [[NSMutableSet alloc] init];
 58      _insertedObjects = [[NSMutableSet alloc] init];
 59      _updatedObjects = [[NSMutableSet alloc] init];
 60      _deletedObjects = [[NSMutableSet alloc] init];
 61  
 62      _objectIdToObject = NSCreateMapTable(NSObjectMapKeyCallBacks,
 63                                           NSObjectMapValueCallBacks, 0);
 64      _requestedProcessPendingChanges = NO;
 65      return self;
 66  }
 67  
 68  - (void) dealloc {
 69      [_storeCoordinator release];
 70      [_undoManager release];
 71      [_registeredObjects release];
 72      [_insertedObjects release];
 73      [_objectIdToObject release];
 74      [super dealloc];
 75  }
 76  
 77  - (NSPersistentStoreCoordinator *) persistentStoreCoordinator {
 78      return _storeCoordinator;
 79  }
 80  
 81  - (NSUndoManager *) undoManager {
 82      return _undoManager;
 83  }
 84  
 85  - (BOOL) retainsRegisteredObjects {
 86      return _retainsRegisteredObjects;
 87  }
 88  
 89  - (BOOL) propagatesDeletesAtEndOfEvent {
 90      return _propagatesDeletesAtEndOfEvent;
 91  }
 92  
 93  - (NSTimeInterval) stalenessInterval {
 94      return _stalenessInterval;
 95  }
 96  
 97  - mergePolicy {
 98      return _mergePolicy;
 99  }
100  
101  - (void) persistentStoresDidChange: (NSNotification *) note {
102      NSArray *stores =
103              [[note userInfo] objectForKey: NSRemovedPersistentStoresKey];
104  
105      for (NSPersistentStore *store in stores) {
106          NSArray *allObjects = NSAllMapTableValues(_objectIdToObject);
107  
108          for (NSManagedObject *check in allObjects) {
109              NSManagedObjectID *objectID = [check objectID];
110  
111              if ([objectID persistentStore] == store) {
112  
113                  NSEntityDescription *entity = [check entity];
114                  NSArray *properties = [[entity propertiesByName] allKeys];
115  
116                  for (NSString *key in properties)
117                      [check removeObserver: self forKeyPath: key];
118  
119                  [_registeredObjects removeObject: check];
120                  [_insertedObjects removeObject: check];
121                  [_updatedObjects removeObject: check];
122                  [_deletedObjects removeObject: check];
123                  NSMapRemove(_objectIdToObject, objectID);
124              }
125          }
126      }
127  }
128  
129  - (void) setPersistentStoreCoordinator: (NSPersistentStoreCoordinator *) value {
130      if (_storeCoordinator != nil)
131          [[NSNotificationCenter defaultCenter]
132                  removeObserver: self
133                            name: NSPersistentStoreCoordinatorStoresDidChangeNotification
134                          object: _storeCoordinator];
135  
136      value = [value retain];
137      [_storeCoordinator release];
138      _storeCoordinator = value;
139  
140      if (_storeCoordinator != nil)
141          [[NSNotificationCenter defaultCenter]
142                  addObserver: self
143                     selector: @selector(persistentStoresDidChange:)
144                         name: NSPersistentStoreCoordinatorStoresDidChangeNotification
145                       object: _storeCoordinator];
146  }
147  
148  - (void) setUndoManager: (NSUndoManager *) value {
149      value = [value retain];
150      [_undoManager release];
151      _undoManager = value;
152  }
153  
154  - (void) setRetainsRegisteredObjects: (BOOL) value {
155      _retainsRegisteredObjects = value;
156      // FIXME: actually release/retain if needed
157      NSUnimplementedMethod();
158  }
159  
160  - (void) setPropagatesDeletesAtEndOfEvent: (BOOL) value {
161      _propagatesDeletesAtEndOfEvent = value;
162  }
163  
164  - (void) setStalenessInterval: (NSTimeInterval) value {
165      _stalenessInterval = value;
166  }
167  
168  - (void) setMergePolicy: value {
169      value = [value retain];
170      [_mergePolicy release];
171      _mergePolicy = value;
172  }
173  
174  - (NSSet *) registeredObjects {
175      return _registeredObjects;
176  }
177  
178  - (NSSet *) insertedObjects {
179      return _insertedObjects;
180  }
181  
182  - (NSSet *) updatedObjects {
183      return _updatedObjects;
184  }
185  
186  - (NSSet *) deletedObjects {
187      return _deletedObjects;
188  }
189  
190  - (void) _setHasChanges: (BOOL) value {
191      _hasChanges = value;
192  }
193  
194  - (BOOL) hasChanges {
195      return _hasChanges;
196  }
197  
198  - (void) lock {
199      [_lock lock];
200  }
201  
202  - (void) unlock {
203      [_lock unlock];
204  }
205  
206  - (BOOL) tryLock {
207      return [_lock tryLock];
208  }
209  
210  - (void) undo {
211      NSUnimplementedMethod();
212  }
213  
214  - (void) redo {
215      NSUnimplementedMethod();
216  }
217  
218  - (void) reset {
219      NSUnimplementedMethod();
220  }
221  
222  - (void) rollback {
223      NSUnimplementedMethod();
224  }
225  
226  - (NSAtomicStoreCacheNode *) _cacheNodeForObjectID:
227          (NSManagedObjectID *) objectID
228  {
229      NSAtomicStore *store = (NSAtomicStore *) [_storeCoordinator
230              _persistentStoreForObjectID: objectID];
231  
232      return [store cacheNodeForObjectID: objectID];
233  }
234  
235  - (NSManagedObject *) objectRegisteredForID: (NSManagedObjectID *) objectID {
236      return NSMapGet(_objectIdToObject, objectID);
237  }
238  
239  - (void) _registerObject: (NSManagedObject *) object {
240      [_registeredObjects addObject: object];
241      NSMapInsert(_objectIdToObject, [object objectID], object);
242  
243      NSEntityDescription *entity = [object entity];
244      NSArray *properties = [[entity propertiesByName] allKeys];
245  
246      for (NSString *key in properties) {
247          [object addObserver: self
248                   forKeyPath: key
249                      options: NSKeyValueObservingOptionNew |
250                               NSKeyValueObservingOptionOld
251                      context: nil];
252      }
253  }
254  
255  - (NSManagedObject *) objectWithID: (NSManagedObjectID *) objectID {
256      NSManagedObject *result = NSMapGet(_objectIdToObject, objectID);
257  
258      if (result == nil) {
259          result = [[NSManagedObject alloc] initWithObjectID: objectID
260                                        managedObjectContext: self];
261          NSMapInsert(_objectIdToObject, objectID, result);
262          [result release];
263          [self _registerObject: result];
264      }
265  
266      return result;
267  }
268  
269  - (NSArray *) executeFetchRequest: (NSFetchRequest *) fetchRequest
270                              error: (NSError **) error
271  {
272      NSArray *affectedStores = [fetchRequest affectedStores];
273  
274      if (affectedStores == nil)
275          affectedStores = [_storeCoordinator persistentStores];
276  
277      NSMutableSet *resultSet = [NSMutableSet set];
278  
279      for (NSManagedObject *check in _insertedObjects) {
280          NSEntityDescription *entity = [check entity];
281  
282          if ([entity _isKindOfEntity: [fetchRequest entity]]) {
283              if (![_deletedObjects containsObject: check]) {
284                  if ([affectedStores containsObject: [[check objectID]
285                                                              persistentStore]]) {
286                      [resultSet addObject: check];
287                  }
288              }
289          }
290      }
291  
292      for (NSAtomicStore *store in affectedStores) {
293          NSSet *nodes = [store cacheNodes];
294  
295          for (NSAtomicStoreCacheNode *node in nodes) {
296              NSManagedObjectID *checkID = [node objectID];
297              NSEntityDescription *entity = [checkID entity];
298  
299              if ([entity _isKindOfEntity: [fetchRequest entity]]) {
300                  NSManagedObject *check = [self objectWithID: checkID];
301  
302                  if (![_deletedObjects containsObject: check]) {
303                      [resultSet addObject: check];
304                  }
305              }
306          }
307      }
308  
309      NSMutableArray *result =
310              [NSMutableArray arrayWithArray: [resultSet allObjects]];
311  
312      NSPredicate *p = [fetchRequest predicate];
313  
314      if (p != nil)
315          [result filterUsingPredicate: p];
316  
317      [result sortUsingDescriptors: [fetchRequest sortDescriptors]];
318  
319      return result;
320  }
321  
322  - (NSUInteger) countForFetchRequest: (NSFetchRequest *) request
323                                error: (NSError **) error
324  {
325      return [[self executeFetchRequest: request error: error] count];
326  }
327  
328  - (void) insertObject: (NSManagedObject *) object {
329      NSPersistentStore *store =
330              [_storeCoordinator _persistentStoreForObject: object];
331  
332      [[object objectID] setStoreIdentifier: [store identifier]];
333      [[object objectID] setPersistentStore: store];
334  
335      [_insertedObjects addObject: object];
336      [_updatedObjects addObject: object];
337      [self _registerObject: object];
338  }
339  
340  - (void) deleteObject: (NSManagedObject *) object {
341      NSArray *properties = [[object entity] properties];
342  
343      for (NSPropertyDescription *property in properties)
344          [object setValue: nil forKey: [property name]];
345  
346      [_deletedObjects addObject: object];
347  }
348  
349  - (void) assignObject: object toPersistentStore: (NSPersistentStore *) store {
350      [object retain];
351  
352      NSMapRemove(_objectIdToObject, [object objectID]);
353  
354      [[object objectID] setStoreIdentifier: [store identifier]];
355  
356      [[object objectID] setPersistentStore: store];
357  
358      NSMapInsert(_objectIdToObject, [object objectID], object);
359      [object release];
360  }
361  
362  - (void) detectConflictsForObject: (NSManagedObject *) object {
363      // NOT NEEDED
364      NSUnimplementedMethod();
365  }
366  
367  - (void) refreshObject: (NSManagedObject *) object mergeChanges: (BOOL) flag {
368      // NEEDED
369      NSUnimplementedMethod();
370  }
371  
372  - (void) _requestProcessPendingChanges {
373      if (!_requestedProcessPendingChanges) {
374  
375          NSRunLoop *runLoop = [NSRunLoop mainRunLoop];
376          [runLoop performSelector: @selector(_processPendingChangesForRequest)
377                            target: self
378                          argument: nil
379                             order: 0
380                             modes: [NSArray arrayWithObject:
381                                                     NSRunLoopCommonModes]];
382          _requestedProcessPendingChanges = YES;
383      }
384  }
385  
386  - (void) _processPendingChanges {
387      _requestedProcessPendingChanges = NO;
388  }
389  
390  - (void) processPendingChanges {
391      if (_requestedProcessPendingChanges) {
392          [[NSRunLoop mainRunLoop] cancelPerformSelector: @selector
393                                   (_processPendingChangesForRequest)
394                                                  target: self
395                                                argument: nil];
396      }
397  
398      [self _processPendingChanges];
399  }
400  
401  - (void) _processPendingChangesForRequest {
402      [self _processPendingChanges];
403  }
404  
405  - (void) observeValueForKeyPath: (NSString *) keyPath
406                         ofObject: (id) object
407                           change: (NSDictionary *) change
408                          context: (void *) context
409  {
410      if (NSMapGet(_objectIdToObject, [object objectID]) == object)
411          [_updatedObjects addObject: object];
412  }
413  
414  - (BOOL) obtainPermanentIDsForObjects: (NSArray *) objects
415                                  error: (NSError **) error
416  {
417  
418      for (NSManagedObject *check in objects) {
419          NSManagedObjectID *checkID = [check objectID];
420  
421          if ([checkID isTemporaryID]) {
422              NSAtomicStore *store = (NSAtomicStore *) [checkID persistentStore];
423  
424              NSMapRemove(_objectIdToObject, checkID);
425  
426  #if 1
427              if (store == nil)
428                  NSLog(@"internal inconsistency , object had no store %@",
429                        check);
430  #else
431              if (store == nil) {
432                  NSString *storeIdentifier = [checkID storeIdentifier];
433  
434                  if (storeIdentifier != nil)
435                      store = (NSAtomicStore *) [_storeCoordinator
436                              _persistentStoreWithIdentifier: storeIdentifier];
437                  else {
438                      store = (NSAtomicStore *) [_storeCoordinator
439                              _persistentStoreForObject: check];
440                      [checkID setStoreIdentifier: [store identifier]];
441                  }
442  
443                  [checkID setPersistentStore: store];
444              }
445  #endif
446  
447              id referenceObject =
448                      [store newReferenceObjectForManagedObject: check];
449  
450              [checkID setReferenceObject: referenceObject];
451  
452              [store _uniqueObjectID: checkID];
453  
454              NSMapInsert(_objectIdToObject, checkID, check);
455          }
456      }
457  
458      return YES;
459  }
460  
461  - (BOOL) save: (NSError **) errorp {
462      NSMutableArray *errors = [NSMutableArray array];
463      NSMutableArray *errorStores = [NSMutableArray array];
464      NSError *idError = nil;
465      NSMutableSet *affectedStores = [NSMutableSet set];
466  
467      [[NSNotificationCenter defaultCenter]
468              postNotificationName: NSManagedObjectContextWillSaveNotification
469                            object: self];
470  
471      // delete cache nodes
472      for (NSManagedObject *deleted in _deletedObjects) {
473          NSAtomicStore *store = (NSAtomicStore *) [_storeCoordinator
474                  _persistentStoreForObject: deleted];
475          NSAtomicStoreCacheNode *node =
476                  [store cacheNodeForObjectID: [deleted objectID]];
477  
478          [store willRemoveCacheNodes: [NSSet setWithObject: node]];
479  
480          [affectedStores addObject: store];
481  
482          // Don't process deleted objects
483          NSMapRemove(_objectIdToObject, [deleted objectID]);
484  
485          [_insertedObjects removeObject: deleted];
486          [_updatedObjects removeObject: deleted];
487      }
488  
489      if (![self obtainPermanentIDsForObjects: [_insertedObjects allObjects]
490                                        error: &idError]) {
491          NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
492  
493          [userInfo setObject: @"obtainPermanentIDsForObjects failed"
494                       forKey: NSLocalizedDescriptionKey];
495  
496          if (errorp != NULL)
497              *errorp = [NSError
498                      errorWithDomain: NSCocoaErrorDomain
499                                 code: NSPersistentStoreIncompleteSaveError
500                             userInfo: userInfo];
501  
502          return NO;
503      }
504  
505      for (NSManagedObject *inserted in _insertedObjects) {
506          NSAtomicStore *store = (NSAtomicStore *) [_storeCoordinator
507                  _persistentStoreForObject: inserted];
508          NSAtomicStoreCacheNode *node =
509                  [store newCacheNodeForManagedObject: inserted];
510  
511          [store addCacheNodes: [NSSet setWithObject: node]];
512  
513          [affectedStores addObject: store];
514      }
515  
516      [_insertedObjects removeAllObjects];
517  
518      for (NSManagedObject *updated in _updatedObjects) {
519          NSAtomicStore *store = (NSAtomicStore *) [_storeCoordinator
520                  _persistentStoreForObject: updated];
521          NSAtomicStoreCacheNode *node =
522                  [store cacheNodeForObjectID: [updated objectID]];
523  
524          [store updateCacheNode: node fromManagedObject: updated];
525  
526          [affectedStores addObject: store];
527      }
528  
529      for (NSPersistentStore *store in affectedStores) {
530          NSError *saveError = nil;
531  
532          if ([store isKindOfClass: [NSAtomicStore class]]) {
533              NSAtomicStore *atomicStore = (NSAtomicStore *) store;
534  
535              if (![atomicStore save: &saveError]) {
536                  [errorStores addObject: atomicStore];
537                  [errors addObject: saveError];
538              }
539          }
540      }
541  
542      [[NSNotificationCenter defaultCenter]
543              postNotificationName: NSManagedObjectContextDidSaveNotification
544                            object: self];
545  
546      if ([errors count] == 0)
547          return YES;
548  
549      NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
550  
551      [userInfo setObject: @"Unable to save managed object context"
552                   forKey: NSLocalizedDescriptionKey];
553      [userInfo setObject: errorStores forKey: NSAffectedStoresErrorKey];
554      [userInfo setObject: errors forKey: NSDetailedErrorsKey];
555  
556      if (errorp != NULL)
557          *errorp = [NSError errorWithDomain: NSCocoaErrorDomain
558                                        code: NSPersistentStoreIncompleteSaveError
559                                    userInfo: userInfo];
560  
561      return NO;
562  }
563  
564  - (void) mergeChangesFromContextDidSaveNotification:
565          (NSNotification *) notification
566  {
567      NSUnimplementedMethod();
568  }
569  
570  - (BOOL) commitEditing {
571      NSUnimplementedMethod();
572      return NO;
573  }
574  
575  - (void) commitEditingWithDelegate: (id) delegate
576                   didCommitSelector: (SEL) didCommitSelector
577                         contextInfo: (void *) contextInfo
578  {
579      NSUnimplementedMethod();
580  }
581  
582  - (void) discardEditing {
583      NSUnimplementedMethod();
584  }
585  
586  - (void) objectDidBeginEditing: (id) editor {
587      NSUnimplementedMethod();
588  }
589  
590  - (void) objectDidEndEditing: (id) editor {
591      NSUnimplementedMethod();
592  }
593  
594  @end