1 /* Copyright(c)2008 Dan Knapp
3 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
5 The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
7 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
8 #import <CoreData/NSManagedObjectContext.h>
9 #import <CoreData/NSManagedObjectModel.h>
10 #import <CoreData/NSFetchRequest.h>
11 #import <CoreData/NSManagedObject.h>
12 #import <CoreData/NSPersistentStore.h>
13 #import "NSManagedObject-Private.h"
14 #import "NSManagedObjectID-Private.h"
15 #import "NSEntityDescription-Private.h"
16 #import <CoreData/NSEntityDescription.h>
17 #import <CoreData/NSAtomicStore.h>
18 #import <CoreData/CoreDataErrors.h>
19 #import "NSPersistentStoreCoordinator-Private.h"
20 #import <Foundation/NSUndoManager.h>
21 #import <Foundation/NSRaise.h>
23 NSString * const NSManagedObjectContextWillSaveNotification=@"NSManagedObjectContextWillSaveNotification";
24 NSString * const NSManagedObjectContextDidSaveNotification=@"NSManagedObjectContextDidSaveNotification";
26 NSString * const NSInsertedObjectsKey=@"NSInsertedObjectsKey";
27 NSString * const NSUpdatedObjectsKey=@"NSUpdatedObjectsKey";
28 NSString * const NSDeletedObjectsKey=@"NSDeletedObjectsKey";
29 NSString * const NSRefreshedObjectsKey=@"NSRefreshedObjectsKey";
30 NSString * const NSInvalidatedObjectsKey=@"NSInvalidatedObjectsKey";
31 NSString * const NSInvalidatedAllObjectsKey=@"NSInvalidatedAllObjectsKey";
33 @interface NSAtomicStore(private)
34 -(void)_uniqueObjectID:(NSManagedObjectID *)objectID;
37 @implementation NSManagedObjectContext
40 _lock=[[NSLock alloc] init];
41 _storeCoordinator=nil;
42 _undoManager=[[NSUndoManager alloc] init];
43 _registeredObjects=[[NSMutableSet alloc] init];
44 _insertedObjects=[[NSMutableSet alloc] init];
45 _updatedObjects=[[NSMutableSet alloc] init];
46 _deletedObjects=[[NSMutableSet alloc] init];
48 _objectIdToObject=NSCreateMapTable(NSObjectMapKeyCallBacks,NSObjectMapValueCallBacks,0);
49 _requestedProcessPendingChanges = NO;
54 [_storeCoordinator release];
55 [_undoManager release];
56 [_registeredObjects release];
57 [_insertedObjects release];
58 [_objectIdToObject release];
62 -(NSPersistentStoreCoordinator *)persistentStoreCoordinator {
63 return _storeCoordinator;
66 -(NSUndoManager *)undoManager {
70 -(BOOL)retainsRegisteredObjects {
71 return _retainsRegisteredObjects;
74 -(BOOL)propagatesDeletesAtEndOfEvent {
75 return _propagatesDeletesAtEndOfEvent;
78 -(NSTimeInterval)stalenessInterval {
79 return _stalenessInterval;
86 -(void)persistentStoresDidChange:(NSNotification *)note {
87 NSArray *stores=[[note userInfo] objectForKey:NSRemovedPersistentStoresKey];
89 for(NSPersistentStore *store in stores){
90 NSArray *allObjects=NSAllMapTableValues(_objectIdToObject);
92 for(NSManagedObject *check in allObjects){
93 NSManagedObjectID *objectID=[check objectID];
95 if([objectID persistentStore]==store){
97 NSEntityDescription *entity=[check entity];
98 NSArray *properties=[[entity propertiesByName] allKeys];
100 for(NSString *key in properties)
101 [check removeObserver:self forKeyPath:key];
103 [_registeredObjects removeObject:check];
104 [_insertedObjects removeObject:check];
105 [_updatedObjects removeObject:check];
106 [_deletedObjects removeObject:check];
107 NSMapRemove(_objectIdToObject,objectID);
114 -(void)setPersistentStoreCoordinator:(NSPersistentStoreCoordinator *)value {
115 if(_storeCoordinator!=nil)
116 [[NSNotificationCenter defaultCenter] removeObserver:self name:NSPersistentStoreCoordinatorStoresDidChangeNotification object:_storeCoordinator];
118 value=[value retain];
119 [_storeCoordinator release];
120 _storeCoordinator=value;
122 if(_storeCoordinator!=nil)
123 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(persistentStoresDidChange:) name:NSPersistentStoreCoordinatorStoresDidChangeNotification object:_storeCoordinator];
126 -(void)setUndoManager:(NSUndoManager *)value {
127 value=[value retain];
128 [_undoManager release];
132 -(void)setRetainsRegisteredObjects:(BOOL)value {
133 _retainsRegisteredObjects=value;
134 // FIXME: actually release/retain if needed
135 NSUnimplementedMethod();
138 -(void)setPropagatesDeletesAtEndOfEvent:(BOOL)value {
139 _propagatesDeletesAtEndOfEvent=value;
142 -(void)setStalenessInterval:(NSTimeInterval)value {
143 _stalenessInterval=value;
146 -(void)setMergePolicy:value {
147 value=[value retain];
148 [_mergePolicy release];
152 -(NSSet *)registeredObjects {
153 return _registeredObjects;
156 -(NSSet *)insertedObjects {
157 return _insertedObjects;
160 -(NSSet *)updatedObjects {
161 return _updatedObjects;
164 -(NSSet *)deletedObjects {
165 return _deletedObjects;
168 -(void)_setHasChanges:(BOOL)value {
185 return [_lock tryLock];
189 NSUnimplementedMethod();
194 NSUnimplementedMethod();
199 NSUnimplementedMethod();
204 NSUnimplementedMethod();
207 -(NSAtomicStoreCacheNode *)_cacheNodeForObjectID:(NSManagedObjectID *)objectID {
208 NSAtomicStore *store=(NSAtomicStore *)[_storeCoordinator _persistentStoreForObjectID:objectID];
210 return [store cacheNodeForObjectID:objectID];
213 -(NSManagedObject *)objectRegisteredForID:(NSManagedObjectID *)objectID {
214 return NSMapGet(_objectIdToObject,objectID);
217 -(void)_registerObject:(NSManagedObject *)object {
218 [_registeredObjects addObject:object];
219 NSMapInsert(_objectIdToObject,[object objectID],object);
221 NSEntityDescription *entity=[object entity];
222 NSArray *properties=[[entity propertiesByName] allKeys];
224 for(NSString *key in properties){
225 [object addObserver:self forKeyPath:key options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
230 -(NSManagedObject *)objectWithID:(NSManagedObjectID *)objectID {
231 NSManagedObject *result=NSMapGet(_objectIdToObject,objectID);
234 result=[[NSManagedObject alloc] initWithObjectID:objectID managedObjectContext:self];
235 NSMapInsert(_objectIdToObject,objectID,result);
237 [self _registerObject:result];
243 -(NSArray *)executeFetchRequest:(NSFetchRequest *)fetchRequest error:(NSError **)error {
244 NSArray *affectedStores=[fetchRequest affectedStores];
246 if(affectedStores==nil)
247 affectedStores=[_storeCoordinator persistentStores];
249 NSMutableSet *resultSet=[NSMutableSet set];
251 for(NSManagedObject *check in _insertedObjects){
252 NSEntityDescription *entity=[check entity];
254 if([entity _isKindOfEntity:[fetchRequest entity]]){
255 if(![_deletedObjects containsObject:check]){
256 if([affectedStores containsObject:[[check objectID] persistentStore]]){
257 [resultSet addObject:check];
263 for(NSAtomicStore *store in affectedStores){
264 NSSet *nodes=[store cacheNodes];
266 for(NSAtomicStoreCacheNode *node in nodes){
267 NSManagedObjectID *checkID=[node objectID];
268 NSEntityDescription *entity=[checkID entity];
270 if([entity _isKindOfEntity:[fetchRequest entity]]){
271 NSManagedObject *check=[self objectWithID:checkID];
273 if(![_deletedObjects containsObject:check]){
274 [resultSet addObject:check];
280 NSMutableArray *result=[NSMutableArray arrayWithArray:[resultSet allObjects]];
282 NSPredicate *p=[fetchRequest predicate];
285 [result filterUsingPredicate:p];
287 [result sortUsingDescriptors:[fetchRequest sortDescriptors]];
292 -(NSUInteger)countForFetchRequest:(NSFetchRequest *)request error:(NSError **)error {
293 return [[self executeFetchRequest:request error:error] count];
296 -(void)insertObject:(NSManagedObject *)object {
297 NSPersistentStore *store=[_storeCoordinator _persistentStoreForObject:object];
299 [[object objectID] setStoreIdentifier:[store identifier]];
300 [[object objectID] setPersistentStore:store];
302 [_insertedObjects addObject:object];
303 [_updatedObjects addObject:object];
304 [self _registerObject:object];
307 -(void)deleteObject:(NSManagedObject *)object {
308 NSArray *properties=[[object entity] properties];
310 for(NSPropertyDescription *property in properties)
311 [object setValue:nil forKey:[property name]];
313 [_deletedObjects addObject:object];
316 -(void)assignObject:object toPersistentStore:(NSPersistentStore *)store {
319 NSMapRemove(_objectIdToObject,[object objectID]);
321 [[object objectID] setStoreIdentifier:[store identifier]];
323 [[object objectID] setPersistentStore:store];
325 NSMapInsert(_objectIdToObject,[object objectID],object);
329 -(void)detectConflictsForObject:(NSManagedObject *)object {
331 NSUnimplementedMethod();
334 -(void)refreshObject:(NSManagedObject *)object mergeChanges:(BOOL)flag {
336 NSUnimplementedMethod();
339 -(void)_requestProcessPendingChanges {
340 if(!_requestedProcessPendingChanges){
342 NSRunLoop *runLoop = [NSRunLoop mainRunLoop];
343 [runLoop performSelector: @selector(_processPendingChangesForRequest)
347 modes: [NSArray arrayWithObject:NSRunLoopCommonModes]];
348 _requestedProcessPendingChanges = YES;
352 -(void)_processPendingChanges {
353 _requestedProcessPendingChanges = NO;
356 -(void)processPendingChanges {
357 if(_requestedProcessPendingChanges){
358 [[NSRunLoop mainRunLoop] cancelPerformSelector: @selector(_processPendingChangesForRequest)target: self argument: nil];
361 [self _processPendingChanges];
365 -(void)_processPendingChangesForRequest {
366 [self _processPendingChanges];
369 -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
370 if(NSMapGet(_objectIdToObject,[object objectID])==object)
371 [_updatedObjects addObject:object];
374 -(BOOL)obtainPermanentIDsForObjects:(NSArray *)objects error:(NSError **)error {
376 for(NSManagedObject *check in objects){
377 NSManagedObjectID *checkID=[check objectID];
379 if([checkID isTemporaryID]){
380 NSAtomicStore *store=(NSAtomicStore *)[checkID persistentStore];
382 NSMapRemove(_objectIdToObject,checkID);
386 NSLog(@"internal inconsistency , object had no store %@",check);
389 NSString *storeIdentifier=[checkID storeIdentifier];
391 if(storeIdentifier!=nil)
392 store=(NSAtomicStore *)[_storeCoordinator _persistentStoreWithIdentifier:storeIdentifier];
394 store=(NSAtomicStore *)[_storeCoordinator _persistentStoreForObject:check];
395 [checkID setStoreIdentifier:[store identifier]];
398 [checkID setPersistentStore:store];
402 id referenceObject=[store newReferenceObjectForManagedObject:check];
404 [checkID setReferenceObject:referenceObject];
406 [store _uniqueObjectID:checkID];
408 NSMapInsert(_objectIdToObject,checkID,check);
415 -(BOOL)save:(NSError **)errorp {
416 NSMutableArray *errors=[NSMutableArray array];
417 NSMutableArray *errorStores=[NSMutableArray array];
418 NSError *idError=nil;
419 NSMutableSet *affectedStores=[NSMutableSet set];
421 [[NSNotificationCenter defaultCenter] postNotificationName:NSManagedObjectContextWillSaveNotification object:self];
423 // delete cache nodes
424 for(NSManagedObject *deleted in _deletedObjects){
425 NSAtomicStore *store=(NSAtomicStore *)[_storeCoordinator _persistentStoreForObject:deleted];
426 NSAtomicStoreCacheNode *node=[store cacheNodeForObjectID:[deleted objectID]];
428 [store willRemoveCacheNodes:[NSSet setWithObject:node]];
430 [affectedStores addObject:store];
432 // Don't process deleted objects
433 NSMapRemove(_objectIdToObject,[deleted objectID]);
435 [_insertedObjects removeObject:deleted];
436 [_updatedObjects removeObject:deleted];
439 if(![self obtainPermanentIDsForObjects:[_insertedObjects allObjects] error:&idError]){
440 NSMutableDictionary *userInfo=[NSMutableDictionary dictionary];
442 [userInfo setObject:@"obtainPermanentIDsForObjects failed" forKey:NSLocalizedDescriptionKey];
445 *errorp=[NSError errorWithDomain:NSCocoaErrorDomain code:NSPersistentStoreIncompleteSaveError userInfo:userInfo];
450 for(NSManagedObject *inserted in _insertedObjects){
451 NSAtomicStore *store=(NSAtomicStore *)[_storeCoordinator _persistentStoreForObject:inserted];
452 NSAtomicStoreCacheNode *node=[store newCacheNodeForManagedObject:inserted];
454 [store addCacheNodes:[NSSet setWithObject:node]];
456 [affectedStores addObject:store];
459 [_insertedObjects removeAllObjects];
461 for(NSManagedObject *updated in _updatedObjects){
462 NSAtomicStore *store=(NSAtomicStore *)[_storeCoordinator _persistentStoreForObject:updated];
463 NSAtomicStoreCacheNode *node=[store cacheNodeForObjectID:[updated objectID]];
465 [store updateCacheNode:node fromManagedObject:updated];
467 [affectedStores addObject:store];
470 for(NSPersistentStore *store in affectedStores){
471 NSError *saveError=nil;
473 if([store isKindOfClass:[NSAtomicStore class]]){
474 NSAtomicStore *atomicStore=(NSAtomicStore *)store;
476 if(![atomicStore save:&saveError]){
477 [errorStores addObject:atomicStore];
478 [errors addObject:saveError];
483 [[NSNotificationCenter defaultCenter] postNotificationName:NSManagedObjectContextDidSaveNotification object:self];
485 if([errors count]==0)
488 NSMutableDictionary *userInfo=[NSMutableDictionary dictionary];
490 [userInfo setObject:@"Unable to save managed object context" forKey:NSLocalizedDescriptionKey];
491 [userInfo setObject:errorStores forKey:NSAffectedStoresErrorKey];
492 [userInfo setObject:errors forKey:NSDetailedErrorsKey];
495 *errorp=[NSError errorWithDomain:NSCocoaErrorDomain code:NSPersistentStoreIncompleteSaveError userInfo:userInfo];
500 -(void)mergeChangesFromContextDidSaveNotification:(NSNotification *)notification {
501 NSUnimplementedMethod();
505 -(BOOL)commitEditing {
506 NSUnimplementedMethod();
510 -(void)commitEditingWithDelegate:(id)delegate didCommitSelector:(SEL)didCommitSelector contextInfo:(void *)contextInfo {
511 NSUnimplementedMethod();
515 -(void)discardEditing {
516 NSUnimplementedMethod();
520 -(void)objectDidBeginEditing:(id)editor {
521 NSUnimplementedMethod();
525 -(void)objectDidEndEditing:(id)editor {
526 NSUnimplementedMethod();