GNUstep Core Data 0.1
NSManagedObjectContext.m
1/* Implementation of the NSManagedObjectContext class for the GNUstep
2 Core Data framework.
3 Copyright (C) 2005 Free Software Foundation, Inc.
4
5 Written by: Saso Kiselkov <diablos@manga.sk>
6 Date: August 2005
7
8 This file is part of the GNUstep Core Data framework.
9
10 This library is free software; you can redistribute it and/or
11 modify it under the terms of the GNU Lesser General Public
12 License as published by the Free Software Foundation; either
13 version 2.1 of the License, or (at your option) any later version.
14
15 This library is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 Lesser General Public License for more details.
19
20 You should have received a copy of the GNU Lesser General Public
21 License along with this library; if not, write to the Free
22 Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111 USA.
23 */
24
25#import "CoreDataHeaders.h"
26
27id NSErrorMergePolicy = nil,
28 NSMergeByPropertyStoreTrumpMergePolicy = nil,
29 NSMergeByPropertyObjectTrumpMergePolicy = nil,
30 NSOverwriteMergePolicy = nil,
31 NSRollbackMergePolicy = nil;
32
33
40static void
41RemoveKVOSetupFromObjects(id observer, NSSet * objects)
42{
43 NSEnumerator * e = [objects objectEnumerator];
44 NSManagedObject * object;
45
46 while ((object = [e nextObject]) != nil)
47 {
48 NSEntityDescription * entity;
49
50 // traverse the entity hierarchy correctly.
51 for (entity = [object entity];
52 entity != nil;
53 entity = [entity superentity])
54 {
55 NSEnumerator * propertyEnum = [[entity properties]
56 objectEnumerator];
57 NSPropertyDescription * property;
58
59 while ((property = [propertyEnum nextObject]) != nil)
60 {
61 [object removeObserver: observer
62 forKeyPath: [property name]];
63 }
64 }
65 }
66}
67
68@interface GSMergePolicy : NSObject
69@end
70
71@implementation GSMergePolicy
72@end
73
74@interface NSManagedObjectContext (GSCoreDataInternal)
75
84- (void) _setFetchedPropertyValues: (NSDictionary *) propertyValues
85 ofObject: (NSManagedObject *) object
86 mergeChanges: (BOOL) mergeChanges;
87
95- (void) _setRelationship: (NSRelationshipDescription *) relationship
96 fetchedValue: (id) value
97 ofObject: (NSManagedObject *) object;
98
104- (void) _registerObjects: (NSSet *) objects;
105
109- (void) _registerObject: (NSManagedObject *) object;
110
116- (void) _unregisterObjects: (NSSet *) objects;
117
121- (void) _unregisterObject: (NSManagedObject *) object;
122
123@end
124
125/*
126 * Implementation note:
127 * Has anybody got any idea on why this class is supposed to conform to
128 * NSCoding? Even if you've got one, then how to archive it when all of
129 * it's instance variables don't support coding? Did somebody in Apple
130 * sleep when designing this?
131 */
132@implementation NSManagedObjectContext
133
134+ (void) initialize
135{
136 if (NSErrorMergePolicy == nil)
137 {
138/*
139 NSErrorMergePolicy = [GSErrorMergePolicy new];
140 NSMergeByPropertyStoreTrumpMergePolicy =
141 [GSMergeByPropertyStoreTrumpMergePolicy new];
142 NSMergeByPropertyObjectTrumpMergePolicy =
143 [GSMergeByPropertyObjectTrumpMergePolicy new];
144 NSRollbackMergePolicy = [GSRollbackMergePolicy new];
145 NSOverwriteMergePolicy = [GSOverwriteMergePolicy new];
146*/
147 }
148}
149
150- (void) dealloc
151{
152 TEST_RELEASE(_lock);
153 TEST_RELEASE(_storeCoordinator);
154
155 TEST_RELEASE(_registeredObjects);
156 TEST_RELEASE(_insertedObjects);
157 TEST_RELEASE(_updatedObjects);
158 TEST_RELEASE(_deletedObjects);
159
160 TEST_RELEASE(_undoManager);
161 TEST_RELEASE(_mergePolicy);
162
163 [super dealloc];
164}
165
166- (id) init
167{
168 if ((self = [super init]))
169 {
170 _lock = [NSRecursiveLock new];
171 _undoManager = [NSUndoManager new];
172
173 _registeredObjects = [NSMutableSet new];
174 _insertedObjects = [NSMutableSet new];
175 _updatedObjects = [NSMutableSet new];
176 _deletedObjects = [NSMutableSet new];
177 ASSIGN(_mergePolicy, NSErrorMergePolicy);
178
179 }
180 return self;
181}
182
186- (NSPersistentStoreCoordinator *) persistentStoreCoordinator
187{
188 return _storeCoordinator;
189}
190
198- (void) setPersistentStoreCoordinator: (NSPersistentStoreCoordinator *)
199 coordinator
200{
201 ASSIGN(_storeCoordinator, coordinator);
202}
203
207- (NSUndoManager *) undoManager
208{
209 return _undoManager;
210}
211
217- (void) setUndoManager: (NSUndoManager *) aManager
218{
219 ASSIGN(_undoManager, aManager);
220}
221
225- (void) undo
226{
227 [_undoManager undo];
228}
229
233- (void) redo
234{
235 [_undoManager redo];
236}
237
238// TODO
239- (void) rollback
240{
241 NSEnumerator * e;
242 NSManagedObject * object;
243
244 // clear all actions from the undo manager
245 [_undoManager removeAllActions];
246
247 // remove any inserted or deleted objects
248 RemoveKVOSetupFromObjects(self, _insertedObjects);
249 [_insertedObjects removeAllObjects];
250 RemoveKVOSetupFromObjects(self, _deletedObjects);
251 [_deletedObjects removeAllObjects];
252
253 // and restore the state of all objects to their commited values
254 e = [_registeredObjects objectEnumerator];
255 while ((object = [e nextObject]) != nil)
256 {
257 if ([object isFault] == NO)
258 {
259 NSDictionary * commitedValues = [object commitedValuesForKeys: nil];
260 NSEnumerator * commitedValuesEnumerator = [[commitedValues allKeys]
261 objectEnumerator];
262 NSString * key;
263
264 while ((key = [commitedValuesEnumerator nextObject]) != nil)
265 {
266 [object setPrimitiveValue: [commitedValues objectForKey: key]
267 forKey: key];
268 }
269 }
270 }
271}
272
280- (void) reset
281{
282 /* From what I (Saso) understood, this method works as a shorthand.
283 * To achieve the same effect, we could just as well recreate the
284 * context anew and replace the old one, but this way we won't have
285 * to swap them and aby additional settings (e.g. merge policy,
286 * staleness interval, persistent store coordinator) will remain
287 * untouched. */
288
289 // remove all objects
290 [self _unregisterObjects: _registeredObjects];
291
292 [_insertedObjects removeAllObjects];
293 [_updatedObjects removeAllObjects];
294 [_deletedObjects removeAllObjects];
295
296 // reset the undo manager
297 [_undoManager removeAllActions];
298}
299
312- (BOOL) save: (NSError **) errorPtr
313{
314 /*
315 * FIXME: why does Apple spec say that this method should abort
316 * immediately in case of an error only when NULL is specified as
317 * the `error' argument? (If non-NULL is passed, it should continue
318 * and aggregate further errors inside the error object.) Should
319 * a potentially destructive operation (and saving *is* a destructive
320 * operation - it permanently overwrites data) not be aborted as soon
321 * as a problem is detected? It is no problem extending this method
322 * to just continue in case of an error and then return all errors as
323 * an aggregate error, but that's very likely not something we want.
324 */
325
326 NSEnumerator * e;
327 NSManagedObject * object;
328 NSMutableSet * objectsToSave;
329 NSError * error = nil;
330
331 if (_storeCoordinator == nil)
332 {
333 [NSException raise: NSInternalInconsistencyException
334 format:
335 _(@"-[NSManagedObjectContext save:]: Cannot save a managed "
336 @"object context which isn't connected to a persistent store "
337 @"coordinator.")];
338 }
339
340 // assign unassigned objects to a persistent store
341 e = [_insertedObjects objectEnumerator];
342 while ((object = [e nextObject]) != nil)
343 {
344 if ([[object objectID] isTemporaryID])
345 {
346 GSPersistentStore * store = [_storeCoordinator
347 persistentStoreContainingEntity: [object entity]];
348
349 if (store != nil)
350 {
351 [self assignObject: object toPersistentStore: store];
352 }
353 else
354 // no store contains the specified entity - cannot save object
355 {
356 NSDictionary * userInfo = [NSDictionary
357 dictionaryWithObject: object
358 forKey: NSAffectedObjectsErrorKey];
359 SetNonNullError(errorPtr, [NSError
360 errorWithDomain: NSCoreDataErrorDomain
361 code: NSPersistentStoreIncompatibleSchemaError
362 userInfo: userInfo]);
363
364 return NO;
365 }
366 }
367 }
368
369 // first, put in all changed objects
370 objectsToSave = [[_updatedObjects mutableCopy] autorelease];
371
372 // then all objects which have newly been inserted (which may be
373 // a subset of the previous ones)
374 [objectsToSave unionSet: _insertedObjects];
375
376 // remove any objects from the list which are scheduled for deletion
377 [objectsToSave minusSet: _deletedObjects];
378
379 // first delete the objects scheduled for deletion
380 e = [_deletedObjects objectEnumerator];
381 while ((object = [e nextObject]) != nil)
382 {
383 [_storeCoordinator deleteObjectWithID: [object objectID]];
384 }
385
386 // and finally merge the objects to be saved with the persistent
387 // store coordinator
388 e = [objectsToSave objectEnumerator];
389 while ((object = [e nextObject]) != nil)
390 {
391 if (![_mergePolicy mergeObject: object
392 withStoreCoordinator: _storeCoordinator
393 error: &error])
394 {
395 SetNonNullError(errorPtr, error);
396
397 return NO;
398 }
399 }
400
401 if (![_storeCoordinator commitChangesError: &error])
402 {
403 SetNonNullError(errorPtr, error);
404
405 return NO;
406 }
407
408 // sync our internal state sets
409 [self _unregisterObjects: _deletedObjects];
410
411 [_insertedObjects removeAllObjects];
412 [_updatedObjects removeAllObjects];
413 [_deletedObjects removeAllObjects];
414
415 return YES;
416}
417
424- (BOOL) hasChanges
425{
426 return ([_updatedObjects count] > 0) ||
427 ([_insertedObjects count] > 0) ||
428 ([_deletedObjects count] > 0);
429}
430
436- (NSManagedObject *) objectRegisteredForID: (NSManagedObjectID *) objectID
437{
438 NSEnumerator * e;
439 NSManagedObject * object;
440
441 e = [_registeredObjects objectEnumerator];
442 while ((object = [e nextObject]) != nil)
443 {
444 if ([[object objectID] _isEqualToManagedObjectID: objectID] == YES)
445 {
446 return object;
447 }
448 }
449
450 return nil;
451}
452
461- (NSManagedObject *) objectWithID: (NSManagedObjectID *) objectID
462{
463 NSManagedObject * object;
464
465 object = [self objectRegisteredForID: objectID];
466
467 // create the object as a fault if necessary
468 if (object == nil)
469 {
470 NSManagedObject * object;
471
472 object = [[[NSManagedObject alloc]
473 _initAsFaultWithObjectID: objectID ownedByContext: self]
474 autorelease];
475 [self _registerObject: object];
476 }
477
478 return object;
479}
480
502- (NSArray *) executeFetchRequest: (NSFetchRequest *) request
503 error: (NSError **) error
504{
505 NSEntityDescription * entity = [request entity];
506 NSPredicate * predicate = [request predicate];
507
508 NSMutableArray * fetchedObjects;
509 NSEnumerator * e;
510 NSManagedObject * object;
511
512 NSMutableSet * fetchedObjectsIDs;
513 NSArray * storedObjects;
514
515 fetchedObjects = [NSMutableArray array];
516
517 // fetch all matching objects from the context first
518 e = [_registeredObjects objectEnumerator];
519 while ((object = [e nextObject]) != nil)
520 {
521 // ignore deleted objects
522 if (ObjectMatchedByFetchRequest(object, request) &&
523 [_deletedObjects containsObject: object] == NO)
524 {
525 [fetchedObjects addObject: object];
526 }
527 }
528
529 // record the object IDs of already present objects
530 fetchedObjectsIDs = [NSMutableSet setWithCapacity: [fetchedObjects
531 count]];
532 e = [fetchedObjects objectEnumerator];
533 while ((object = [e nextObject]) != nil)
534 {
535 [fetchedObjectsIDs addObject: [object objectID]];
536 }
537
538 // and tell the store to execute the fetch request, ignoring
539 // already fetched object IDs
540 storedObjects = [_storeCoordinator _executeFetchRequest: request
541 ignoreIDs: fetchedObjectsIDs
542 stalenessInterval: _stalenessInterval
543 error: error];
544 if (storedObjects != nil)
545 {
546 [fetchedObjects addObjectsFromArray: storedObjects];
547 }
548 // store error
549 else
550 {
551 return nil;
552 }
553
554 // now, apply any sorting descriptors
555 [fetchedObjects sortUsingDescriptors: [request sortDescriptors]];
556
557 return [[fetchedObjects copy] autorelease];
558}
559
568- (void) insertObject: (NSManagedObject *) object
569{
570 NSDictionary * userInfo;
571
572 // re-inserting a deleted object brings it in again
573 if ([_deletedObjects containsObject: object] == YES)
574 {
575 [_deletedObjects removeObject: object];
576 }
577 // otherwise if it isn't registered yet schedule it for addition
578 else if ([_registeredObjects containsObject: object] == NO)
579 {
580 [self _registerObject: object];
581
582 [_insertedObjects addObject: object];
583 }
584 else
585 {
586 return;
587 }
588
589 [object _insertedIntoContext: self];
590 [object _setDeleted: NO];
591
592 [_undoManager registerUndoWithTarget: self
593 selector: @selector(deleteObject:)
594 object: object];
595
596 userInfo = [NSDictionary dictionaryWithObject: [NSSet setWithObject: object]
597 forKey: NSInsertedObjectsKey];
598 [[NSNotificationCenter defaultCenter]
599 postNotificationName: NSManagedObjectContextObjectsDidChangeNotification
600 object: self
601 userInfo: userInfo];
602}
603
609- (void) deleteObject: (NSManagedObject *) object
610{
611 if ([_registeredObjects containsObject: object] == YES)
612 {
613 NSDictionary * userInfo;
614
615 // we must do this first to make sure that removing the object
616 // from our internal tables doesn't deallocate it
617 [_undoManager registerUndoWithTarget: self
618 selector: @selector(insertObject:)
619 object: object];
620
621 // an unsaved object is removed immediately
622 if ([_insertedObjects containsObject: object])
623 {
624 [self _unregisterObject: object];
625 [_insertedObjects removeObject: object];
626 }
627 // otherwise it is scheduled for deletion
628 else
629 {
630 [_deletedObjects addObject: object];
631 }
632
633 userInfo = [NSDictionary
634 dictionaryWithObject: [NSSet setWithObject: object]
635 forKey: NSDeletedObjectsKey];
636 [[NSNotificationCenter defaultCenter]
637 postNotificationName: NSManagedObjectContextObjectsDidChangeNotification
638 object: self
639 userInfo: userInfo];
640 }
641}
642
656- (void) assignObject: (id) anObject toPersistentStore: (id) aPersistentStore
657{
658 // Why doesn't this method declare that `obj' must be a managed object??
659 NSManagedObject * object = anObject;
660 GSPersistentStore * store = aPersistentStore;
661 NSManagedObjectID * oldObjectID, * newObjectID;
662
663 if (![object isKindOfClass: [NSManagedObject class]])
664 {
665 [NSException raise: NSInvalidArgumentException
666 format:
667 _(@"-[NSManagedObjectContext assignObject:toPersistentStore:]: "
668 @"Non-managed-object passed.")];
669 }
670
671 if (_storeCoordinator == nil)
672 {
673 [NSException raise: NSInternalInconsistencyException
674 format:
675 _(@"-[NSManagedObjectContext assignObject:toPersistentStore:]: "
676 @"Cannot assign an object to a store in a context that isn't "
677 @"connected to a persistent store coordinator.")];
678 }
679
680 if (![[_storeCoordinator persistentStores] containsObject: store])
681 {
682 [NSException raise: NSInvalidArgumentException
683 format:
684 _(@"-[NSManagedObjectContext assignObject:toPersistentStore:]: "
685 @"Cannot assign an object to a store which isn't in the "
686 @"persistent store with which the context in which the object "
687 @"lives is associated.")];
688 }
689
690 /*
691 * NB. We don't check whether the object has a temporary or a permanent
692 * object ID, only whether it has already been commited to a persistent
693 * store. Thus a newly inserted object can be reassigned several times
694 * to different persistent stores before it is actually saved. The save
695 * will commit it to the latest of the specified stores. After that,
696 * however, one cannot change it's location anymore.
697 */
698 if ([_insertedObjects containsObject: object] == NO)
699 {
700 [NSException raise: NSInvalidArgumentException
701 format:
702 _(@"-[NSManagedObjectContext assignObject:toPersistentStore:]: "
703 @"Cannot assign an object to a persistent store which hasn't "
704 @"been inserted.")];
705 }
706
707 oldObjectID = [object objectID];
708
709 // construct a new object ID in which we will explicitly denote the
710 // persistent store to which the object belongs
711 newObjectID = [[[NSManagedObjectID alloc]
712 _initWithEntity: [oldObjectID entity]
713 persistentStore: store
714 value: [store nextFreeIDValue]]
715 autorelease];
716
717 [object _setObjectID: newObjectID];
718}
719
737- (void) refreshObject: (NSManagedObject *) object
738 mergeChanges: (BOOL) mergeChanges
739{
740 NSManagedObjectID * objectID = [object objectID];
741 NSDictionary * propertyValues;
742
743 propertyValues = [_storeCoordinator fetchObjectWithID: [object objectID]
744 fetchProperties: nil
745 cacheStalenessInterval: _stalenessInterval];
746
747 if (propertyValues == nil)
748 {
749 [NSException raise: NSInvalidArgumentException
750 format:
751 _(@"-[NSManagedObjectContext refreshObject:mergeChanges:]: "
752 @"Cannot refresh object - data for object doesn't exist in "
753 @"the persistent store.")];
754 }
755
756 [self _setFetchedPropertyValues: propertyValues
757 ofObject: object
758 mergeChanges: mergeChanges];
759}
760
765- (NSSet *) insertedObjects
766{
767 return [[_insertedObjects copy] autorelease];
768}
769
774- (NSSet *) updatedObjects
775{
776 return [[_updatedObjects copy] autorelease];
777}
778
783- (NSSet *) deletedObjects
784{
785 return [[_deletedObjects copy] autorelease];
786}
787
791- (NSSet *) registeredObjects
792{
793 return [[_registeredObjects copy] autorelease];
794}
795
802- (void) lock
803{
804 [_lock lock];
805}
806
813- (void) unlock
814{
815 [_lock unlock];
816}
817
828- (BOOL) tryLock
829{
830 return [_lock tryLock];
831}
832
841- (BOOL) retainsRegisteredObjects
842{
843 return _retainsRegisteredObjects;
844}
845
854- (void) setRetainsRegisteredObjects: (BOOL) flag
855{
856 if (_retainsRegisteredObjects != flag)
857 {
858 _retainsRegisteredObjects = flag;
859
860 // retain them
861 if (_retainsRegisteredObjects == YES)
862 {
863 [_registeredObjects makeObjectsPerformSelector: @selector(retain)];
864 }
865 // release them
866 else
867 {
868 [_registeredObjects makeObjectsPerformSelector: @selector(release)];
869 }
870 }
871}
872
878- (NSTimeInterval) stalenessInterval
879{
880 return _stalenessInterval;
881}
882
891- (void) setStalenessInterval: (NSTimeInterval) timeInterval
892{
893 _stalenessInterval = timeInterval;
894}
895
901- (id) mergePolicy
902{
903 return _mergePolicy;
904}
905
924- (void) setMergePolicy: (id) policy
925{
926 if (![policy isKindOfClass: [GSMergePolicy class]])
927 {
928 [NSException raise: NSInvalidArgumentException
929 format: _(@"-[NSManagedObjectContext setMergePolicy:]: "
930 @"Invalid merge policy (%@) specified."), policy];
931 }
932
933 ASSIGN(_mergePolicy, policy);
934}
935
936@end
937
942@implementation NSManagedObjectContext (GSCoreDataPrivate)
943
954- (void) _object: (NSManagedObject *) object
955 changedValueForSingleKey: (NSString *) key
956 oldValue: (id) oldValue
957 newValue: (id) newValue
958{
959 [[_undoManager prepareWithInvocationTarget: object]
960 setValue: oldValue forKey: key];
961}
962
974- (void) _object: (NSManagedObject *) object
975 changedValueForMultiKey: (NSString *) key
976 oldValue: (NSSet *) oldValue
977 setMutation: (NSKeyValueSetMutationKind) mutationKind
978 usingObjects: (NSSet *) objects
979{
980 NSMutableSet * newValue = [object mutableSetValueForKey: key];
981
982 id um = [_undoManager prepareWithInvocationTarget: newValue];
983
984 switch (mutationKind)
985 {
986 case NSKeyValueUnionSetMutation:
987 {
988 // FIXED by hns to make it compile - but not checked!!!
989 NSMutableSet *ms = [[objects mutableCopy] autorelease];
990 [ms minusSet: oldValue];
991 [um minusSet: ms];
992 break;
993 }
994 case NSKeyValueMinusSetMutation:
995 {
996 // FIXED by hns to make it compile - but not checked!!!
997 NSMutableSet *ms = [[objects mutableCopy] autorelease];
998 [ms intersectSet: oldValue];
999 [um unionSet: ms];
1000 break;
1001 }
1002 case NSKeyValueIntersectSetMutation:
1003 [um unionSet: oldValue];
1004 break;
1005 case NSKeyValueSetSetMutation:
1006 [um setSet: oldValue];
1007 break;
1008 }
1009}
1010
1011@end
1012
1017@implementation NSManagedObjectContext (GSCoreDataInternal)
1018
1019- (void) _setFetchedPropertyValues: (NSDictionary *) newPropertyValues
1020 ofObject: (NSManagedObject *) object
1021 mergeChanges: (BOOL) mergeChanges
1022{
1023 Class relationshipClass = [NSRelationshipDescription class];
1024 NSEntityDescription * entity;
1025 NSDictionary * changedValues;
1026 NSEnumerator * e;
1027 NSString * key;
1028
1029 if (mergeChanges == YES)
1030 {
1031 changedValues = [object changedValues];
1032 }
1033
1034 // process all properties, traversing the entity inheritance hierarchy
1035 for (entity = [object entity]; entity != nil; entity = [entity superentity])
1036 {
1037 NSEnumerator * e = [[entity properties] objectEnumerator];
1038 NSPropertyDescription * property;
1039
1040 while ((property = [e nextObject]) != nil)
1041 {
1042 NSString * key = [property name];
1043 id newValue = [newPropertyValues objectForKey: key];
1044
1045 if ([property isTransient])
1046 {
1047 if (mergeChanges == NO)
1048 {
1049 // flush transient values if merging isn't requested
1050 [object setValue: nil forKey: key];
1051 }
1052 }
1053 else
1054 {
1055 if (newValue == nil)
1056 {
1057 [object setValue: nil forKey: key];
1058 }
1059 else if ([property isKindOfClass: relationshipClass])
1060 {
1061 [self _setRelationship: (NSRelationshipDescription*) property
1062 fetchedValue: newValue
1063 ofObject: object];
1064 }
1065 else
1066 {
1067 [object setValue: newValue forKey: key];
1068 }
1069 }
1070 }
1071 }
1072
1073 [object _flushChangedValues];
1074
1075 if (mergeChanges == YES)
1076 {
1077 // now set back all properties which have changed
1078 e = [[changedValues allKeys] objectEnumerator];
1079 while ((key = [e nextObject]) != nil)
1080 {
1081 [object setValue: [changedValues objectForKey: key] forKey: key];
1082 }
1083 }
1084}
1085
1086- (void) _setRelationship: (NSRelationshipDescription *) relationship
1087 fetchedValue: (id) value
1088 ofObject: (NSManagedObject *) object
1089{
1090 NSString * key = [relationship name];
1091 NSManagedObjectID * destinationID;
1092 NSManagedObject * destinationObject;
1093
1094 // If a relationship is a to-many relationship, the value will be a
1095 // collection containing a set of managed object IDs of the destination
1096 // objects. Extract them, get the objects and set them as the value
1097 // of the key.
1098 if ([relationship isToMany])
1099 {
1100 NSMutableSet * newRelationshipValue;
1101 NSEnumerator * e;
1102
1103 NSAssert1([value isKindOfClass: [NSSet class]] ||
1104 [value isKindOfClass: [NSArray class]],
1105 _(@"Encountered non-collection value (%@) from store when setting "
1106 @"a to-many relationship."), value);
1107
1108 newRelationshipValue = [NSMutableSet setWithCapacity: [value count]];
1109
1110 e = [value objectEnumerator];
1111 while ((destinationID = [e nextObject]) != nil)
1112 {
1113 destinationObject = [self objectWithID: destinationID];
1114 [newRelationshipValue addObject: destinationObject];
1115 }
1116
1117 [object setValue: newRelationshipValue forKey: key];
1118 }
1119 // Otherwise the value is a single managed object ID the destination
1120 // object. Get the object and set it.
1121 else
1122 {
1123 NSAssert1([value isKindOfClass: [NSManagedObjectID class]],
1124 _(@"Encountered non-object-ID value (%@) from store when setting "
1125 @"a relationship."), value);
1126
1127 destinationID = value;
1128 destinationObject = [self objectWithID: destinationID];
1129
1130 [object setValue: destinationObject forKey: key];
1131 }
1132}
1133
1134- (void) _registerObjects: (NSSet *) objects
1135{
1136 if (_retainsRegisteredObjects == NO)
1137 {
1138 NSMutableSet * tmp;
1139
1140 tmp = [[objects mutableCopy] autorelease];
1141
1142 // cut out only the objects which aren't registered yet
1143 [tmp minusSet: _registeredObjects];
1144
1145 // first put them in the set, then release
1146 [_registeredObjects unionSet: objects];
1147 [tmp makeObjectsPerformSelector: @selector(release)];
1148 }
1149 else
1150 {
1151 [_registeredObjects unionSet: objects];
1152 }
1153}
1154
1155- (void) _registerObject: (NSManagedObject *) object
1156{
1157 if (_retainsRegisteredObjects == NO)
1158 {
1159 // we need to check whether it's already registered to not
1160 // confuse the retain/release machinery by releasing it more
1161 // times
1162 if ([_registeredObjects containsObject: object] == NO)
1163 {
1164 [_registeredObjects addObject: object];
1165 [object release];
1166 }
1167 }
1168 else
1169 {
1170 [_registeredObjects addObject: object];
1171 }
1172}
1173
1174- (void) _unregisterObjects: (NSSet *) objects
1175{
1176 if (_retainsRegisteredObjects == NO)
1177 {
1178 NSMutableSet * tmp;
1179
1180 tmp = [[objects mutableCopy] autorelease];
1181 // cut out only registered objects
1182 [tmp intersectSet: _registeredObjects];
1183
1184 // first retain, then remove from set
1185 [tmp makeObjectsPerformSelector: @selector(retain)];
1186 [_registeredObjects minusSet: tmp];
1187 }
1188 else
1189 {
1190 [_registeredObjects unionSet: objects];
1191 }
1192}
1193
1194- (void) _unregisterObject: (NSManagedObject *) object
1195{
1196 if (_retainsRegisteredObjects)
1197 {
1198 if ([_registeredObjects containsObject: object] == YES)
1199 {
1200 // first retain, then remove from set
1201 [object retain];
1202 [_registeredObjects removeObject: object];
1203 }
1204 }
1205 else
1206 {
1207 [_registeredObjects removeObject: object];
1208 }
1209}
1210
1211@end
1212
1213NSString * const NSManagedObjectContextObjectsDidChangeNotification =
1214 @"NSManagedObjectContextObjectsDidChangeNotification";
1215NSString * const NSManagedObjectContextDidSaveNotification =
1216 @"NSManagedObjectContextDidSaveNotification";
1217
1218NSString * const NSInsertedObjectsKey = @"NSInsertedObjectsKey";
1219NSString * const NSUpdatedObjectsKey = @"NSUpdatedObjectsKey";
1220NSString * const NSDeletedObjectsKey = @"NSDeletedObjectsKey";
Internal methods methods of GNUstep Core Data for NSManagedObjectContext.
void _setRelationship:fetchedValue:ofObject:(NSRelationshipDescription *relationship,[fetchedValue] id value,[ofObject] NSManagedObject *object)
Sets the relationship described by ‘relationship’ in ‘object’ to ‘value’.
Private methods of GNUstep Core Data for NSManagedObjectContext.
NSEntityDescription * entity()
Returns the entity of the fetch request.
NSArray * sortDescriptors()
Returns the receiver's sort descriptors.
NSPredicate * predicate()
Returns the predicate of the receiver.
For implementation notes see "Documentation/NSManagedObjectID.txt" in the source distribution of the ...
NSEntityDescription * entity()
Returns the receiver's entity (that is, the entity of the object to which this managed object ID belo...
Validates whether value'' is a valid value forattribute'', returning YES if it is,...
void setValue:forKey:(id aValue,[forKey] NSString *aKey)
Sets the value of key ‘aKey’ to ‘aValue’ and invokes corresponding KVO methods.
NSEntityDescription * entity()
Returns the entity of the receiver.
void setPrimitiveValue:forKey:(id aPrimitiveValue,[forKey] NSString *aKey)
Sets the value for key ‘aKey’ without invoking KVO methods.
NSManagedObjectID * objectID()
Returns the object ID of the receiver.