GNUstep Core Data
0.1
|
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";