GNUstep Core Data  0.1
NSManagedObjectContext.m
00001 /* Implementation of the NSManagedObjectContext class for the GNUstep
00002    Core Data framework.
00003    Copyright (C) 2005 Free Software Foundation, Inc.
00004 
00005    Written by:  Saso Kiselkov <diablos@manga.sk>
00006    Date: August 2005
00007 
00008    This file is part of the GNUstep Core Data framework.
00009 
00010    This library is free software; you can redistribute it and/or
00011    modify it under the terms of the GNU Lesser General Public
00012    License as published by the Free Software Foundation; either
00013    version 2.1 of the License, or (at your option) any later version.
00014 
00015    This library is distributed in the hope that it will be useful,
00016    but WITHOUT ANY WARRANTY; without even the implied warranty of
00017    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00018    Lesser General Public License for more details.
00019 
00020    You should have received a copy of the GNU Lesser General Public
00021    License along with this library; if not, write to the Free
00022    Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111 USA.
00023  */
00024 
00025 #import "CoreDataHeaders.h"
00026 
00027 id NSErrorMergePolicy = nil,
00028    NSMergeByPropertyStoreTrumpMergePolicy = nil,
00029    NSMergeByPropertyObjectTrumpMergePolicy = nil,
00030    NSOverwriteMergePolicy = nil,
00031    NSRollbackMergePolicy = nil;
00032 
00033 
00040 static void
00041 RemoveKVOSetupFromObjects(id observer, NSSet * objects)
00042 {
00043   NSEnumerator * e = [objects objectEnumerator];
00044   NSManagedObject * object;
00045 
00046   while ((object = [e nextObject]) != nil)
00047     {
00048       NSEntityDescription * entity;
00049 
00050       // traverse the entity hierarchy correctly.
00051       for (entity = [object entity];
00052            entity != nil;
00053            entity = [entity superentity])
00054         {
00055           NSEnumerator * propertyEnum = [[entity properties]
00056             objectEnumerator];
00057           NSPropertyDescription * property;
00058 
00059           while ((property = [propertyEnum nextObject]) != nil)
00060             {
00061               [object removeObserver: observer
00062                           forKeyPath: [property name]];
00063             }
00064         }
00065     }
00066 }
00067 
00068 @interface GSMergePolicy : NSObject
00069 @end
00070 
00071 @implementation GSMergePolicy
00072 @end
00073 
00074 @interface NSManagedObjectContext (GSCoreDataInternal)
00075 
00084 - (void) _setFetchedPropertyValues: (NSDictionary *) propertyValues
00085                           ofObject: (NSManagedObject *) object
00086                       mergeChanges: (BOOL) mergeChanges;
00087 
00095 - (void) _setRelationship: (NSRelationshipDescription *) relationship
00096              fetchedValue: (id) value
00097                  ofObject: (NSManagedObject *) object;
00098 
00104 - (void) _registerObjects: (NSSet *) objects;
00105 
00109 - (void) _registerObject: (NSManagedObject *) object;
00110 
00116 - (void) _unregisterObjects: (NSSet *) objects;
00117 
00121 - (void) _unregisterObject: (NSManagedObject *) object;
00122 
00123 @end
00124 
00125 /*
00126  * Implementation note:
00127  * Has anybody got any idea on why this class is supposed to conform to
00128  * NSCoding? Even if you've got one, then how to archive it when all of
00129  * it's instance variables don't support coding? Did somebody in Apple
00130  * sleep when designing this?
00131  */
00132 @implementation NSManagedObjectContext
00133 
00134 + (void) initialize
00135 {
00136   if (NSErrorMergePolicy == nil)
00137     {
00138 /*
00139       NSErrorMergePolicy = [GSErrorMergePolicy new];
00140       NSMergeByPropertyStoreTrumpMergePolicy =
00141         [GSMergeByPropertyStoreTrumpMergePolicy new];
00142       NSMergeByPropertyObjectTrumpMergePolicy =
00143         [GSMergeByPropertyObjectTrumpMergePolicy new];
00144       NSRollbackMergePolicy = [GSRollbackMergePolicy new];
00145       NSOverwriteMergePolicy = [GSOverwriteMergePolicy new];
00146 */
00147     }
00148 }
00149 
00150 - (void) dealloc
00151 {
00152   TEST_RELEASE(_lock);
00153   TEST_RELEASE(_storeCoordinator);
00154 
00155   TEST_RELEASE(_registeredObjects);
00156   TEST_RELEASE(_insertedObjects);
00157   TEST_RELEASE(_updatedObjects);
00158   TEST_RELEASE(_deletedObjects);
00159 
00160   TEST_RELEASE(_undoManager);
00161   TEST_RELEASE(_mergePolicy);
00162 
00163   [super dealloc];
00164 }
00165 
00166 - (id) init
00167 {
00168         if ((self = [super init]))
00169     {
00170       _lock = [NSRecursiveLock new];
00171       _undoManager = [NSUndoManager new];
00172 
00173       _registeredObjects = [NSMutableSet new];
00174       _insertedObjects = [NSMutableSet new];
00175       _updatedObjects = [NSMutableSet new];
00176       _deletedObjects = [NSMutableSet new];
00177       ASSIGN(_mergePolicy, NSErrorMergePolicy);
00178 
00179     }
00180         return self;
00181 }
00182 
00186 - (NSPersistentStoreCoordinator *) persistentStoreCoordinator
00187 {
00188   return _storeCoordinator;
00189 }
00190 
00198 - (void) setPersistentStoreCoordinator: (NSPersistentStoreCoordinator *)
00199   coordinator
00200 {
00201   ASSIGN(_storeCoordinator, coordinator);
00202 }
00203 
00207 - (NSUndoManager *) undoManager
00208 {
00209   return _undoManager;
00210 }
00211 
00217 - (void) setUndoManager: (NSUndoManager *) aManager
00218 {
00219   ASSIGN(_undoManager, aManager);
00220 }
00221 
00225 - (void) undo
00226 {
00227   [_undoManager undo];
00228 }
00229 
00233 - (void) redo
00234 {
00235   [_undoManager redo];
00236 }
00237 
00238 // TODO
00239 - (void) rollback
00240 {
00241   NSEnumerator * e;
00242   NSManagedObject * object;
00243 
00244   // clear all actions from the undo manager
00245   [_undoManager removeAllActions];
00246 
00247   // remove any inserted or deleted objects
00248   RemoveKVOSetupFromObjects(self, _insertedObjects);
00249   [_insertedObjects removeAllObjects];
00250   RemoveKVOSetupFromObjects(self, _deletedObjects);
00251   [_deletedObjects removeAllObjects];
00252 
00253   // and restore the state of all objects to their commited values
00254   e = [_registeredObjects objectEnumerator];
00255   while ((object = [e nextObject]) != nil)
00256     {
00257       if ([object isFault] == NO)
00258         {
00259           NSDictionary * commitedValues = [object commitedValuesForKeys: nil];
00260           NSEnumerator * commitedValuesEnumerator = [[commitedValues allKeys]
00261             objectEnumerator];
00262           NSString * key;
00263 
00264           while ((key = [commitedValuesEnumerator nextObject]) != nil)
00265             {
00266               [object setPrimitiveValue: [commitedValues objectForKey: key]
00267                                  forKey: key];
00268             }
00269         }
00270     }
00271 }
00272 
00280 - (void) reset
00281 {
00282   /* From what I (Saso) understood, this method works as a shorthand.
00283    * To achieve the same effect, we could just as well recreate the
00284    * context anew and replace the old one, but this way we won't have
00285    * to swap them and aby additional settings (e.g. merge policy,
00286    * staleness interval, persistent store coordinator) will remain
00287    * untouched. */
00288 
00289   // remove all objects
00290   [self _unregisterObjects: _registeredObjects];
00291 
00292   [_insertedObjects removeAllObjects];
00293   [_updatedObjects removeAllObjects];
00294   [_deletedObjects removeAllObjects];
00295 
00296   // reset the undo manager
00297   [_undoManager removeAllActions];
00298 }
00299 
00312 - (BOOL) save: (NSError **) errorPtr
00313 {
00314   /*
00315    * FIXME: why does Apple spec say that this method should abort
00316    * immediately in case of an error only when NULL is specified as
00317    * the `error' argument? (If non-NULL is passed, it should continue
00318    * and aggregate further errors inside the error object.) Should
00319    * a potentially destructive operation (and saving *is* a destructive
00320    * operation - it permanently overwrites data) not be aborted as soon
00321    * as a problem is detected? It is no problem extending this method
00322    * to just continue in case of an error and then return all errors as
00323    * an aggregate error, but that's very likely not something we want.
00324    */
00325 
00326   NSEnumerator * e;
00327   NSManagedObject * object;
00328   NSMutableSet * objectsToSave;
00329   NSError * error = nil;
00330 
00331   if (_storeCoordinator == nil)
00332     {
00333       [NSException raise: NSInternalInconsistencyException
00334                   format:
00335         _(@"-[NSManagedObjectContext save:]: Cannot save a managed "
00336           @"object context which isn't connected to a persistent store "
00337           @"coordinator.")];
00338     }
00339 
00340   // assign unassigned objects to a persistent store
00341   e = [_insertedObjects objectEnumerator];
00342   while ((object = [e nextObject]) != nil)
00343     {
00344       if ([[object objectID] isTemporaryID])
00345         {
00346           GSPersistentStore * store = [_storeCoordinator
00347             persistentStoreContainingEntity: [object entity]];
00348 
00349           if (store != nil)
00350             {
00351               [self assignObject: object toPersistentStore: store];
00352             }
00353           else
00354             // no store contains the specified entity - cannot save object
00355             {
00356               NSDictionary * userInfo = [NSDictionary
00357                 dictionaryWithObject: object
00358                               forKey: NSAffectedObjectsErrorKey];
00359               SetNonNullError(errorPtr, [NSError
00360                     errorWithDomain: NSCoreDataErrorDomain
00361                                code: NSPersistentStoreIncompatibleSchemaError
00362                            userInfo: userInfo]);
00363 
00364               return NO;
00365             }
00366         }
00367     }
00368 
00369   // first, put in all changed objects
00370   objectsToSave = [[_updatedObjects mutableCopy] autorelease];
00371 
00372   // then all objects which have newly been inserted (which may be
00373   // a subset of the previous ones)
00374   [objectsToSave unionSet: _insertedObjects];
00375 
00376   // remove any objects from the list which are scheduled for deletion
00377   [objectsToSave minusSet: _deletedObjects];
00378 
00379   // first delete the objects scheduled for deletion
00380   e = [_deletedObjects objectEnumerator];
00381   while ((object = [e nextObject]) != nil)
00382     {
00383       [_storeCoordinator deleteObjectWithID: [object objectID]];
00384     }
00385 
00386   // and finally merge the objects to be saved with the persistent
00387   // store coordinator
00388   e = [objectsToSave objectEnumerator];
00389   while ((object = [e nextObject]) != nil)
00390     {
00391       if (![_mergePolicy mergeObject: object
00392                 withStoreCoordinator: _storeCoordinator
00393                                error: &error])
00394         {
00395           SetNonNullError(errorPtr, error);
00396 
00397           return NO;
00398         }
00399     }
00400 
00401   if (![_storeCoordinator commitChangesError: &error])
00402     {
00403       SetNonNullError(errorPtr, error);
00404 
00405       return NO;
00406     }
00407 
00408   // sync our internal state sets
00409   [self _unregisterObjects: _deletedObjects];
00410 
00411   [_insertedObjects removeAllObjects];
00412   [_updatedObjects removeAllObjects];
00413   [_deletedObjects removeAllObjects];
00414 
00415   return YES;
00416 }
00417 
00424 - (BOOL) hasChanges
00425 {
00426   return ([_updatedObjects count] > 0) ||
00427          ([_insertedObjects count] > 0) ||
00428          ([_deletedObjects count] > 0);
00429 }
00430 
00436 - (NSManagedObject *) objectRegisteredForID: (NSManagedObjectID *) objectID
00437 {
00438   NSEnumerator * e;
00439   NSManagedObject * object;
00440 
00441   e = [_registeredObjects objectEnumerator];
00442   while ((object = [e nextObject]) != nil)
00443     {
00444       if ([[object objectID] _isEqualToManagedObjectID: objectID] == YES)
00445         {
00446           return object;
00447         }
00448     }
00449 
00450   return nil;
00451 }
00452 
00461 - (NSManagedObject *) objectWithID: (NSManagedObjectID *) objectID
00462 {
00463   NSManagedObject * object;
00464 
00465   object = [self objectRegisteredForID: objectID];
00466 
00467   // create the object as a fault if necessary
00468   if (object == nil)
00469     {
00470       NSManagedObject * object;
00471 
00472       object = [[[NSManagedObject alloc]
00473         _initAsFaultWithObjectID: objectID ownedByContext: self]
00474         autorelease];
00475       [self _registerObject: object];
00476     }
00477 
00478   return object;
00479 }
00480 
00502 - (NSArray *) executeFetchRequest: (NSFetchRequest *) request
00503                             error: (NSError **) error
00504 {
00505   NSEntityDescription * entity = [request entity];
00506   NSPredicate * predicate = [request predicate];
00507 
00508   NSMutableArray * fetchedObjects;
00509   NSEnumerator * e;
00510   NSManagedObject * object;
00511 
00512   NSMutableSet * fetchedObjectsIDs;
00513   NSArray * storedObjects;
00514 
00515   fetchedObjects = [NSMutableArray array];
00516 
00517   // fetch all matching objects from the context first
00518   e = [_registeredObjects objectEnumerator];
00519   while ((object = [e nextObject]) != nil)
00520     {
00521       // ignore deleted objects
00522       if (ObjectMatchedByFetchRequest(object, request) &&
00523           [_deletedObjects containsObject: object] == NO)
00524         {
00525           [fetchedObjects addObject: object];
00526         }
00527     }
00528 
00529   // record the object IDs of already present objects
00530   fetchedObjectsIDs = [NSMutableSet setWithCapacity: [fetchedObjects
00531     count]];
00532   e = [fetchedObjects objectEnumerator];
00533   while ((object = [e nextObject]) != nil)
00534     {
00535       [fetchedObjectsIDs addObject: [object objectID]];
00536     }
00537 
00538   // and tell the store to execute the fetch request, ignoring
00539   // already fetched object IDs
00540   storedObjects = [_storeCoordinator _executeFetchRequest: request
00541                                                 ignoreIDs: fetchedObjectsIDs
00542                                         stalenessInterval: _stalenessInterval
00543                                                     error: error];
00544   if (storedObjects != nil)
00545     {
00546       [fetchedObjects addObjectsFromArray: storedObjects];
00547     }
00548   // store error
00549   else
00550     {
00551       return nil;
00552     }
00553 
00554   // now, apply any sorting descriptors
00555   [fetchedObjects sortUsingDescriptors: [request sortDescriptors]];
00556 
00557   return [[fetchedObjects copy] autorelease];
00558 }
00559 
00568 - (void) insertObject: (NSManagedObject *) object
00569 {
00570   NSDictionary * userInfo;
00571 
00572   // re-inserting a deleted object brings it in again
00573   if ([_deletedObjects containsObject: object] == YES)
00574     {
00575       [_deletedObjects removeObject: object];
00576     }
00577   // otherwise if it isn't registered yet schedule it for addition
00578   else if ([_registeredObjects containsObject: object] == NO)
00579     {
00580       [self _registerObject: object];
00581 
00582       [_insertedObjects addObject: object];
00583     }
00584   else
00585     {
00586       return;
00587     }
00588 
00589   [object _insertedIntoContext: self];
00590   [object _setDeleted: NO];
00591 
00592   [_undoManager registerUndoWithTarget: self
00593                               selector: @selector(deleteObject:)
00594                                 object: object];
00595 
00596   userInfo = [NSDictionary dictionaryWithObject: [NSSet setWithObject: object]
00597                                          forKey: NSInsertedObjectsKey];
00598   [[NSNotificationCenter defaultCenter]
00599     postNotificationName: NSManagedObjectContextObjectsDidChangeNotification
00600                   object: self
00601                 userInfo: userInfo];
00602 }
00603 
00609 - (void) deleteObject: (NSManagedObject *) object
00610 {
00611   if ([_registeredObjects containsObject: object] == YES)
00612     {
00613       NSDictionary * userInfo;
00614 
00615       // we must do this first to make sure that removing the object
00616       // from our internal tables doesn't deallocate it
00617       [_undoManager registerUndoWithTarget: self
00618                                   selector: @selector(insertObject:)
00619                                     object: object];
00620 
00621       // an unsaved object is removed immediately
00622       if ([_insertedObjects containsObject: object])
00623         {
00624           [self _unregisterObject: object];
00625           [_insertedObjects removeObject: object];
00626         }
00627       // otherwise it is scheduled for deletion
00628       else
00629         {
00630           [_deletedObjects addObject: object];
00631         }
00632 
00633       userInfo = [NSDictionary
00634         dictionaryWithObject: [NSSet setWithObject: object]
00635                       forKey: NSDeletedObjectsKey];
00636       [[NSNotificationCenter defaultCenter]
00637         postNotificationName: NSManagedObjectContextObjectsDidChangeNotification
00638                       object: self
00639                     userInfo: userInfo];
00640     }
00641 }
00642 
00656 - (void) assignObject: (id) anObject toPersistentStore: (id) aPersistentStore
00657 {
00658   // Why doesn't this method declare that `obj' must be a managed object??
00659   NSManagedObject * object = anObject;
00660   GSPersistentStore * store = aPersistentStore;
00661   NSManagedObjectID * oldObjectID, * newObjectID;
00662 
00663   if (![object isKindOfClass: [NSManagedObject class]])
00664     {
00665       [NSException raise: NSInvalidArgumentException
00666                   format:
00667         _(@"-[NSManagedObjectContext assignObject:toPersistentStore:]: "
00668           @"Non-managed-object passed.")];
00669     }
00670 
00671   if (_storeCoordinator == nil)
00672     {
00673       [NSException raise: NSInternalInconsistencyException
00674                   format:
00675         _(@"-[NSManagedObjectContext assignObject:toPersistentStore:]: "
00676           @"Cannot assign an object to a store in a context that isn't "
00677           @"connected to a persistent store coordinator.")];
00678     }
00679 
00680   if (![[_storeCoordinator persistentStores] containsObject: store])
00681     {
00682       [NSException raise: NSInvalidArgumentException
00683                   format:
00684         _(@"-[NSManagedObjectContext assignObject:toPersistentStore:]: "
00685           @"Cannot assign an object to a store which isn't in the "
00686           @"persistent store with which the context in which the object "
00687           @"lives is associated.")];
00688     }
00689 
00690   /*
00691    * NB. We don't check whether the object has a temporary or a permanent
00692    * object ID, only whether it has already been commited to a persistent
00693    * store. Thus a newly inserted object can be reassigned several times
00694    * to different persistent stores before it is actually saved. The save
00695    * will commit it to the latest of the specified stores. After that,
00696    * however, one cannot change it's location anymore.
00697    */
00698   if ([_insertedObjects containsObject: object] == NO)
00699     {
00700       [NSException raise: NSInvalidArgumentException
00701                   format:
00702         _(@"-[NSManagedObjectContext assignObject:toPersistentStore:]: "
00703           @"Cannot assign an object to a persistent store which hasn't "
00704           @"been inserted.")];
00705     }
00706 
00707   oldObjectID = [object objectID];
00708 
00709   // construct a new object ID in which we will explicitly denote the
00710   // persistent store to which the object belongs
00711   newObjectID = [[[NSManagedObjectID alloc]
00712     _initWithEntity: [oldObjectID entity]
00713     persistentStore: store
00714               value: [store nextFreeIDValue]]
00715     autorelease];
00716 
00717   [object _setObjectID: newObjectID];
00718 }
00719 
00737 - (void) refreshObject: (NSManagedObject *) object
00738           mergeChanges: (BOOL) mergeChanges
00739 {
00740   NSManagedObjectID * objectID = [object objectID];
00741   NSDictionary * propertyValues;
00742 
00743   propertyValues = [_storeCoordinator fetchObjectWithID: [object objectID]
00744                                         fetchProperties: nil
00745                                  cacheStalenessInterval: _stalenessInterval];
00746 
00747   if (propertyValues == nil)
00748     {
00749       [NSException raise: NSInvalidArgumentException
00750                   format:
00751         _(@"-[NSManagedObjectContext refreshObject:mergeChanges:]: "
00752           @"Cannot refresh object - data for object doesn't exist in "
00753           @"the persistent store.")];
00754     }
00755 
00756   [self _setFetchedPropertyValues: propertyValues
00757                          ofObject: object
00758                      mergeChanges: mergeChanges];
00759 }
00760 
00765 - (NSSet *) insertedObjects
00766 {
00767   return [[_insertedObjects copy] autorelease];
00768 }
00769 
00774 - (NSSet *) updatedObjects
00775 {
00776   return [[_updatedObjects copy] autorelease];
00777 }
00778 
00783 - (NSSet *) deletedObjects
00784 {
00785   return [[_deletedObjects copy] autorelease];
00786 }
00787 
00791 - (NSSet *) registeredObjects
00792 {
00793   return [[_registeredObjects copy] autorelease];
00794 }
00795 
00802 - (void) lock
00803 {
00804   [_lock lock];
00805 }
00806 
00813 - (void) unlock
00814 {
00815   [_lock unlock];
00816 }
00817 
00828 - (BOOL) tryLock
00829 {
00830   return [_lock tryLock];
00831 }
00832 
00841 - (BOOL) retainsRegisteredObjects
00842 {
00843   return _retainsRegisteredObjects;
00844 }
00845 
00854 - (void) setRetainsRegisteredObjects: (BOOL) flag
00855 {
00856   if (_retainsRegisteredObjects != flag)
00857     {
00858       _retainsRegisteredObjects = flag;
00859 
00860       // retain them
00861       if (_retainsRegisteredObjects == YES)
00862         {
00863           [_registeredObjects makeObjectsPerformSelector: @selector(retain)];
00864         }
00865       // release them
00866       else
00867         {
00868           [_registeredObjects makeObjectsPerformSelector: @selector(release)];
00869         }
00870     }
00871 }
00872 
00878 - (NSTimeInterval) stalenessInterval
00879 {
00880   return _stalenessInterval;
00881 }
00882 
00891 - (void) setStalenessInterval: (NSTimeInterval) timeInterval
00892 {
00893   _stalenessInterval = timeInterval;
00894 }
00895 
00901 - (id) mergePolicy
00902 {
00903   return _mergePolicy;
00904 }
00905 
00924 - (void) setMergePolicy: (id) policy
00925 {
00926   if (![policy isKindOfClass: [GSMergePolicy class]])
00927     {
00928       [NSException raise: NSInvalidArgumentException
00929                   format: _(@"-[NSManagedObjectContext setMergePolicy:]: "
00930                             @"Invalid merge policy (%@) specified."), policy];
00931     }
00932 
00933   ASSIGN(_mergePolicy, policy);
00934 }
00935 
00936 @end
00937 
00942 @implementation NSManagedObjectContext (GSCoreDataPrivate)
00943 
00954 - (void)          _object: (NSManagedObject *) object
00955  changedValueForSingleKey: (NSString *) key
00956                  oldValue: (id) oldValue
00957                  newValue: (id) newValue
00958 {
00959   [[_undoManager prepareWithInvocationTarget: object]
00960     setValue: oldValue forKey: key];
00961 }
00962 
00974 - (void)         _object: (NSManagedObject *) object
00975  changedValueForMultiKey: (NSString *) key
00976                 oldValue: (NSSet *) oldValue
00977              setMutation: (NSKeyValueSetMutationKind) mutationKind
00978             usingObjects: (NSSet *) objects
00979 {
00980   NSMutableSet * newValue = [object mutableSetValueForKey: key];
00981 
00982   id um = [_undoManager prepareWithInvocationTarget: newValue];
00983 
00984   switch (mutationKind)
00985           {
00986           case NSKeyValueUnionSetMutation:
00987                   {
00988                           // FIXED by hns to make it compile - but not checked!!!
00989                           NSMutableSet *ms = [[objects mutableCopy] autorelease];
00990                           [ms minusSet: oldValue];
00991                           [um minusSet: ms];
00992                           break;
00993                   }
00994           case NSKeyValueMinusSetMutation:
00995                   {
00996                           // FIXED by hns to make it compile - but not checked!!!
00997                           NSMutableSet *ms = [[objects mutableCopy] autorelease];
00998                           [ms intersectSet: oldValue];
00999                           [um unionSet: ms];
01000                           break;
01001                   }
01002           case NSKeyValueIntersectSetMutation:
01003                   [um unionSet: oldValue];
01004                   break;
01005           case NSKeyValueSetSetMutation:
01006                   [um setSet: oldValue];
01007                   break;
01008           }
01009 }
01010 
01011 @end
01012 
01017 @implementation NSManagedObjectContext (GSCoreDataInternal)
01018 
01019 - (void) _setFetchedPropertyValues: (NSDictionary *) newPropertyValues
01020                           ofObject: (NSManagedObject *) object
01021                       mergeChanges: (BOOL) mergeChanges
01022 {
01023   Class relationshipClass = [NSRelationshipDescription class];
01024   NSEntityDescription * entity;
01025   NSDictionary * changedValues;
01026   NSEnumerator * e;
01027   NSString * key;
01028 
01029   if (mergeChanges == YES)
01030     {
01031       changedValues = [object changedValues];
01032     }
01033 
01034   // process all properties, traversing the entity inheritance hierarchy
01035   for (entity = [object entity]; entity != nil; entity = [entity superentity])
01036     {
01037       NSEnumerator * e = [[entity properties] objectEnumerator];
01038       NSPropertyDescription * property;
01039 
01040       while ((property = [e nextObject]) != nil)
01041         {
01042           NSString * key = [property name];
01043           id newValue = [newPropertyValues objectForKey: key];
01044 
01045           if ([property isTransient])
01046             {
01047               if (mergeChanges == NO)
01048                 {
01049                   // flush transient values if merging isn't requested
01050                   [object setValue: nil forKey: key];
01051                 }
01052             }
01053           else
01054             {
01055               if (newValue == nil)
01056                 {
01057                   [object setValue: nil forKey: key];
01058                 }
01059               else if ([property isKindOfClass: relationshipClass])
01060                 {
01061                   [self _setRelationship: (NSRelationshipDescription*) property
01062                             fetchedValue: newValue
01063                                 ofObject: object];
01064                 }
01065               else
01066                 {
01067                   [object setValue: newValue forKey: key];
01068                 }
01069             }
01070         }
01071     }
01072 
01073   [object _flushChangedValues];
01074 
01075   if (mergeChanges == YES)
01076     {
01077       // now set back all properties which have changed
01078       e = [[changedValues allKeys] objectEnumerator];
01079       while ((key = [e nextObject]) != nil)
01080         {
01081           [object setValue: [changedValues objectForKey: key] forKey: key];
01082         }
01083     }
01084 }
01085 
01086 - (void) _setRelationship: (NSRelationshipDescription *) relationship
01087              fetchedValue: (id) value
01088                  ofObject: (NSManagedObject *) object
01089 {
01090   NSString * key = [relationship name];
01091   NSManagedObjectID * destinationID;
01092   NSManagedObject * destinationObject;
01093 
01094   // If a relationship is a to-many relationship, the value will be a
01095   // collection containing a set of managed object IDs of the destination
01096   // objects. Extract them, get the objects and set them as the value
01097   // of the key.
01098   if ([relationship isToMany])
01099     {
01100       NSMutableSet * newRelationshipValue;
01101       NSEnumerator * e;
01102 
01103       NSAssert1([value isKindOfClass: [NSSet class]] ||
01104                 [value isKindOfClass: [NSArray class]],
01105         _(@"Encountered non-collection value (%@) from store when setting "
01106           @"a to-many relationship."), value);
01107 
01108       newRelationshipValue = [NSMutableSet setWithCapacity: [value count]];
01109 
01110       e = [value objectEnumerator];
01111       while ((destinationID = [e nextObject]) != nil)
01112         {
01113           destinationObject = [self objectWithID: destinationID];
01114           [newRelationshipValue addObject: destinationObject];
01115         }
01116 
01117       [object setValue: newRelationshipValue forKey: key];
01118     }
01119   // Otherwise the value is a single managed object ID the destination
01120   // object. Get the object and set it.
01121   else
01122     {
01123       NSAssert1([value isKindOfClass: [NSManagedObjectID class]],
01124         _(@"Encountered non-object-ID value (%@) from store when setting "
01125           @"a relationship."), value);
01126 
01127       destinationID = value;
01128       destinationObject = [self objectWithID: destinationID];
01129 
01130       [object setValue: destinationObject forKey: key];
01131     }
01132 }
01133 
01134 - (void) _registerObjects: (NSSet *) objects
01135 {
01136   if (_retainsRegisteredObjects == NO)
01137     {
01138       NSMutableSet * tmp;
01139 
01140       tmp = [[objects mutableCopy] autorelease];
01141 
01142       // cut out only the objects which aren't registered yet
01143       [tmp minusSet: _registeredObjects];
01144 
01145       // first put them in the set, then release
01146       [_registeredObjects unionSet: objects];
01147       [tmp makeObjectsPerformSelector: @selector(release)];
01148     }
01149   else
01150     {
01151       [_registeredObjects unionSet: objects];
01152     }
01153 }
01154 
01155 - (void) _registerObject: (NSManagedObject *) object
01156 {
01157   if (_retainsRegisteredObjects == NO)
01158     {
01159       // we need to check whether it's already registered to not
01160       // confuse the retain/release machinery by releasing it more
01161       // times
01162       if ([_registeredObjects containsObject: object] == NO)
01163         {
01164           [_registeredObjects addObject: object];
01165           [object release];
01166         }
01167     }
01168   else
01169     {
01170       [_registeredObjects addObject: object];
01171     }
01172 }
01173 
01174 - (void) _unregisterObjects: (NSSet *) objects
01175 {
01176   if (_retainsRegisteredObjects == NO)
01177     {
01178       NSMutableSet * tmp;
01179 
01180       tmp = [[objects mutableCopy] autorelease];
01181       // cut out only registered objects
01182       [tmp intersectSet: _registeredObjects];
01183 
01184       // first retain, then remove from set
01185       [tmp makeObjectsPerformSelector: @selector(retain)];
01186       [_registeredObjects minusSet: tmp];
01187     }
01188   else
01189     {
01190       [_registeredObjects unionSet: objects];
01191     }
01192 }
01193 
01194 - (void) _unregisterObject: (NSManagedObject *) object
01195 {
01196   if (_retainsRegisteredObjects)
01197     {
01198       if ([_registeredObjects containsObject: object] == YES)
01199         {
01200           // first retain, then remove from set
01201           [object retain];
01202           [_registeredObjects removeObject: object];
01203         }
01204     }
01205   else
01206     {
01207       [_registeredObjects removeObject: object];
01208     }
01209 }
01210 
01211 @end
01212 
01213 NSString * const NSManagedObjectContextObjectsDidChangeNotification =
01214   @"NSManagedObjectContextObjectsDidChangeNotification";
01215 NSString * const NSManagedObjectContextDidSaveNotification =
01216   @"NSManagedObjectContextDidSaveNotification";
01217 
01218 NSString * const NSInsertedObjectsKey = @"NSInsertedObjectsKey";
01219 NSString * const NSUpdatedObjectsKey = @"NSUpdatedObjectsKey";
01220 NSString * const NSDeletedObjectsKey = @"NSDeletedObjectsKey";