Merge pull request #9 from gunyarakun/fix-typo
[cocotron.git] / CoreData / NSManagedObjectContext.m
blobf85b77108818641e8efb3d9014e16591cd044485
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;
35 @end
37 @implementation NSManagedObjectContext
39 -init {
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];
47    
48    _objectIdToObject=NSCreateMapTable(NSObjectMapKeyCallBacks,NSObjectMapValueCallBacks,0);
49    _requestedProcessPendingChanges = NO;
50    return self;
53 -(void)dealloc {
54    [_storeCoordinator release];
55    [_undoManager release];
56    [_registeredObjects release];
57    [_insertedObjects release];
58    [_objectIdToObject release];
59    [super dealloc];
62 -(NSPersistentStoreCoordinator *)persistentStoreCoordinator {
63    return _storeCoordinator;
66 -(NSUndoManager *)undoManager {
67     return _undoManager;
70 -(BOOL)retainsRegisteredObjects {
71     return _retainsRegisteredObjects;
74 -(BOOL)propagatesDeletesAtEndOfEvent {
75     return _propagatesDeletesAtEndOfEvent;
78 -(NSTimeInterval)stalenessInterval {
79     return _stalenessInterval;
82 -mergePolicy {
83     return _mergePolicy;
86 -(void)persistentStoresDidChange:(NSNotification *)note {
87    NSArray *stores=[[note userInfo] objectForKey:NSRemovedPersistentStoresKey];
88       
89    for(NSPersistentStore *store in stores){
90     NSArray *allObjects=NSAllMapTableValues(_objectIdToObject);
92     for(NSManagedObject *check in allObjects){
93      NSManagedObjectID *objectID=[check objectID];
94         
95      if([objectID persistentStore]==store){
96      
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);
108      }
109     }
110    }
114 -(void)setPersistentStoreCoordinator:(NSPersistentStoreCoordinator *)value {
115    if(_storeCoordinator!=nil)
116     [[NSNotificationCenter defaultCenter] removeObserver:self name:NSPersistentStoreCoordinatorStoresDidChangeNotification object:_storeCoordinator];
117     
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];
129    _undoManager=value;
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];
149    _mergePolicy=value;
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 {
169    _hasChanges=value;
172 -(BOOL)hasChanges {
173     return _hasChanges;
176 -(void)lock {
177    [_lock lock];
180 -(void)unlock {
181    [_lock unlock];
184 -(BOOL)tryLock {
185    return [_lock tryLock];
188 -(void)undo {
189     NSUnimplementedMethod();
193 -(void)redo {
194     NSUnimplementedMethod();
198 -(void)reset {
199     NSUnimplementedMethod();
203 -(void)rollback {
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];
226    }
230 -(NSManagedObject *)objectWithID:(NSManagedObjectID *)objectID {
231    NSManagedObject *result=NSMapGet(_objectIdToObject,objectID);
233    if(result==nil){
234     result=[[NSManagedObject alloc] initWithObjectID:objectID managedObjectContext:self];
235     NSMapInsert(_objectIdToObject,objectID,result);
236     [result release];
237     [self _registerObject:result];
238    }
239    
240    return result;
243 -(NSArray *)executeFetchRequest:(NSFetchRequest *)fetchRequest error:(NSError **)error {
244    NSArray *affectedStores=[fetchRequest affectedStores];
246    if(affectedStores==nil)
247     affectedStores=[_storeCoordinator persistentStores];
248   
249    NSMutableSet *resultSet=[NSMutableSet set];
251    for(NSManagedObject *check in _insertedObjects){
252     NSEntityDescription *entity=[check entity];
253     
254     if([entity _isKindOfEntity:[fetchRequest entity]]){       
255      if(![_deletedObjects containsObject:check]){
256       if([affectedStores containsObject:[[check objectID] persistentStore]]){
257        [resultSet addObject:check];
258        }
259      }
260     }
261    }
262    
263    for(NSAtomicStore *store in affectedStores){
264     NSSet *nodes=[store cacheNodes];
265     
266     for(NSAtomicStoreCacheNode *node in nodes){
267      NSManagedObjectID   *checkID=[node objectID];
268      NSEntityDescription *entity=[checkID entity];
269     
270      if([entity _isKindOfEntity:[fetchRequest entity]]){
271       NSManagedObject *check=[self objectWithID:checkID];
272       
273       if(![_deletedObjects containsObject:check]){
274        [resultSet addObject:check];
275        }
276          }
277     }
278    }
279    
280    NSMutableArray *result=[NSMutableArray arrayWithArray:[resultSet allObjects]];
282    NSPredicate *p=[fetchRequest predicate];
283    
284    if(p!=nil)
285     [result filterUsingPredicate:p];
287    [result sortUsingDescriptors:[fetchRequest sortDescriptors]];
289    return result;
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];
298    
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];
309     
310    for(NSPropertyDescription *property in properties)
311     [object setValue:nil forKey:[property name]];
313    [_deletedObjects addObject:object];
316 -(void)assignObject:object toPersistentStore:(NSPersistentStore *)store {
317    [object retain];
319    NSMapRemove(_objectIdToObject,[object objectID]);
321    [[object objectID] setStoreIdentifier:[store identifier]];
323    [[object objectID] setPersistentStore:store];
325    NSMapInsert(_objectIdToObject,[object objectID],object);
326    [object release];
329 -(void)detectConflictsForObject:(NSManagedObject *)object {
330 // NOT NEEDED
331     NSUnimplementedMethod();
334 -(void)refreshObject:(NSManagedObject *)object mergeChanges:(BOOL)flag {
335 // NEEDED
336     NSUnimplementedMethod();
339 -(void)_requestProcessPendingChanges {
340     if(!_requestedProcessPendingChanges){
342         NSRunLoop *runLoop = [NSRunLoop mainRunLoop];
343         [runLoop performSelector: @selector(_processPendingChangesForRequest)
344                  target: self
345                  argument: nil
346                  order: 0
347                  modes: [NSArray arrayWithObject:NSRunLoopCommonModes]];
348         _requestedProcessPendingChanges = YES;
349    }
352 -(void)_processPendingChanges {
353     _requestedProcessPendingChanges = NO;
356 -(void)processPendingChanges {
357    if(_requestedProcessPendingChanges){
358     [[NSRunLoop mainRunLoop] cancelPerformSelector: @selector(_processPendingChangesForRequest)target: self argument: nil];
359    }
360    
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];
378         
379     if([checkID isTemporaryID]){
380      NSAtomicStore *store=(NSAtomicStore *)[checkID persistentStore];
381      
382      NSMapRemove(_objectIdToObject,checkID);
384 #if 1
385      if(store==nil)
386       NSLog(@"internal inconsistency , object had no store %@",check);
387 #else     
388      if(store==nil){
389       NSString *storeIdentifier=[checkID storeIdentifier];
390       
391       if(storeIdentifier!=nil)
392        store=(NSAtomicStore *)[_storeCoordinator _persistentStoreWithIdentifier:storeIdentifier];
393       else {
394        store=(NSAtomicStore *)[_storeCoordinator _persistentStoreForObject:check];
395        [checkID setStoreIdentifier:[store identifier]];
396       }
397       
398       [checkID setPersistentStore:store];
399      }
400 #endif
401                
402      id referenceObject=[store newReferenceObjectForManagedObject:check];
403      
404      [checkID setReferenceObject:referenceObject];
405      
406      [store _uniqueObjectID:checkID];
407      
408      NSMapInsert(_objectIdToObject,checkID,check);
409     }
410    }
411    
412    return YES;
415 -(BOOL)save:(NSError **)errorp {
416    NSMutableArray *errors=[NSMutableArray array];
417    NSMutableArray *errorStores=[NSMutableArray array];
418    NSError        *idError=nil;
419    NSMutableSet   *affectedStores=[NSMutableSet set];
420    
421    [[NSNotificationCenter defaultCenter] postNotificationName:NSManagedObjectContextWillSaveNotification object:self];
422    
423 // delete cache nodes    
424    for(NSManagedObject *deleted in _deletedObjects){
425     NSAtomicStore          *store=(NSAtomicStore *)[_storeCoordinator _persistentStoreForObject:deleted];
426     NSAtomicStoreCacheNode *node=[store cacheNodeForObjectID:[deleted objectID]];
427     
428     [store willRemoveCacheNodes:[NSSet setWithObject:node]];
430     [affectedStores addObject:store];
431     
432     // Don't process deleted objects
433     NSMapRemove(_objectIdToObject,[deleted objectID]);
435     [_insertedObjects removeObject:deleted];
436     [_updatedObjects removeObject:deleted];
437    }
439    if(![self obtainPermanentIDsForObjects:[_insertedObjects allObjects] error:&idError]){
440     NSMutableDictionary *userInfo=[NSMutableDictionary dictionary];
441    
442     [userInfo setObject:@"obtainPermanentIDsForObjects failed" forKey:NSLocalizedDescriptionKey];
443    
444     if(errorp!=NULL)
445      *errorp=[NSError errorWithDomain:NSCocoaErrorDomain code:NSPersistentStoreIncompleteSaveError userInfo:userInfo];
446      
447     return NO;
448    }
450    for(NSManagedObject *inserted in _insertedObjects){
451     NSAtomicStore          *store=(NSAtomicStore *)[_storeCoordinator _persistentStoreForObject:inserted];
452     NSAtomicStoreCacheNode *node=[store newCacheNodeForManagedObject:inserted];
453     
454     [store addCacheNodes:[NSSet setWithObject:node]];
455     
456     [affectedStores addObject:store];
457    }
459    [_insertedObjects removeAllObjects];
460    
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];
468    }
469    
470    for(NSPersistentStore *store in affectedStores){
471     NSError           *saveError=nil;
472     
473     if([store isKindOfClass:[NSAtomicStore class]]){
474      NSAtomicStore *atomicStore=(NSAtomicStore *)store;
475      
476      if(![atomicStore save:&saveError]){
477       [errorStores addObject:atomicStore];
478       [errors addObject:saveError];
479      }
480     }
481    }
483    [[NSNotificationCenter defaultCenter] postNotificationName:NSManagedObjectContextDidSaveNotification object:self];
484    
485    if([errors count]==0)
486     return YES;
487   
488    NSMutableDictionary *userInfo=[NSMutableDictionary dictionary];
489    
490    [userInfo setObject:@"Unable to save managed object context" forKey:NSLocalizedDescriptionKey];
491    [userInfo setObject:errorStores forKey:NSAffectedStoresErrorKey];
492    [userInfo setObject:errors forKey:NSDetailedErrorsKey];
493    
494    if(errorp!=NULL)
495     *errorp=[NSError errorWithDomain:NSCocoaErrorDomain code:NSPersistentStoreIncompleteSaveError userInfo:userInfo];
496   
497    return NO;
500 -(void)mergeChangesFromContextDidSaveNotification:(NSNotification *)notification {
501     NSUnimplementedMethod();
505 -(BOOL)commitEditing {
506    NSUnimplementedMethod();
507    return NO;
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();
529 @end