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 
27 id NSErrorMergePolicy = nil,
28  NSMergeByPropertyStoreTrumpMergePolicy = nil,
29  NSMergeByPropertyObjectTrumpMergePolicy = nil,
30  NSOverwriteMergePolicy = nil,
31  NSRollbackMergePolicy = nil;
32 
33 
40 static void
41 RemoveKVOSetupFromObjects(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 
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 
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 
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 
1213 NSString * const NSManagedObjectContextObjectsDidChangeNotification =
1214  @"NSManagedObjectContextObjectsDidChangeNotification";
1215 NSString * const NSManagedObjectContextDidSaveNotification =
1216  @"NSManagedObjectContextDidSaveNotification";
1217 
1218 NSString * const NSInsertedObjectsKey = @"NSInsertedObjectsKey";
1219 NSString * const NSUpdatedObjectsKey = @"NSUpdatedObjectsKey";
1220 NSString * const NSDeletedObjectsKey = @"NSDeletedObjectsKey";
Internal methods methods of GNUstep Core Data for NSManagedObjectContext.
Private methods of GNUstep Core Data for NSManagedObjectContext.
Nn abstract superclass from which concrete implementations of various persistent store types are subc...
An object for storing details about managed object fetches.
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.