GNUstep Core Data  0.1
NSManagedObject.m
1 /* Implementation of the NSManagedObject 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 
33 static inline BOOL
34 IsCollection(id object)
35 {
36  if ([object isKindOfClass: [NSSet class]] ||
37  [object isKindOfClass: [NSArray class]])
38  {
39  return YES;
40  }
41  else
42  {
43  return NO;
44  }
45 }
46 
55 static inline void
56 ConstructComplexError(NSError ** target, NSArray * errors)
57 {
58  unsigned int errorCount = [errors count];
59 
60  if (errorCount == 1)
61  {
62  *target = [errors objectAtIndex: 0];
63  }
64  else if (errorCount > 1)
65  {
66  NSError * newError;
67  NSDictionary * userInfo;
68 
69  userInfo = [NSDictionary
70  dictionaryWithObject: [[errors copy] autorelease]
71  forKey: NSDetailedErrorsKey];
72  newError = [NSError errorWithDomain: NSCoreDataErrorDomain
73  code: NSValidationMultipleErrorsError
74  userInfo: userInfo];
75 
76  *target = newError;
77  }
78 }
79 
85 /*
86 static BOOL
87 ValidateAttributeValue(NSAttributeDescription * attribute,
88  id value,
89  NSError ** error)
90 {
91  Class attrClass = NSClassFromString([attribute attributeValueClassName]);
92 
93  // check the class is correct
94  if ([value isKindOfClass: attrClass] == NO)
95  {
96  NSDictionary * userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
97  self, NSValidationObjectErrorKey,
98  [attr name], NSValidationKeyErrorKey,
99  value, NSValidationValueErrorKey,
100  nil];
101 
102  SetNonNullError(error, [NSError
103  errorWithDomain: NSCoreDataErrorDomain
104  code: NSValidationValueOfIncorrectClassError
105  userInfo: userInfo]);
106 
107  return NO;
108  }
109 
110  return YES;
111 }
112 */
113 
117 /*
118 static BOOL
119 ValidateRelationshipValue(NSRelationshipDescription * relationship,
120  id value,
121  NSError ** error)
122  NSEntityDescription * destEntity = [relationship destinationEntity];
123  Class managedObjectClass = [NSManagedObject class];
124  NSDictionary * errorUserInfo = [NSDictionary dictionaryWithObjectsAndKeys:
125  self, NSValidationObjectErrorKey,
126  [relationship name], NSValidationKeyErrorKey,
127  value, NSValidationValueErrorKey,
128  nil];
129 
130  // if the relationship is a to-many relationship and the passed
131  // object is a collection, check that all contained objects are
132  // NSManagedObject's and have the correct entity set.
133  if ([rel isToMany] && IsCollection(value))
134  {
135  NSEnumerator * e;
136  id obj;
137  int count;
138 
139  // make sure correct cardinality is kept
140  count = [value count];
141  if (count < [rel minCount])
142  {
143  SetNonNullError(error, [NSError
144  errorWithDomain: NSCoreDataErrorDomain
145  code: NSValidationRelationshipLacksMinimumCountError
146  userInfo: errorUserInfo]);
147 
148  return NO;
149  }
150  if (count > [rel maxCount])
151  {
152  SetNonNullError(error, [NSError
153  errorWithDomain: NSCoreDataErrorDomain
154  code: NSValidationRelationshipExceedsMaximumCountError
155  userInfo: errorUserInfo]);
156 
157  return NO;
158  }
159 
160  e = [value objectEnumerator];
161  while ((obj = [e nextObject]) != nil)
162  {
163  if ([obj isKindOfClass: managedObjectClass] == NO)
164  {
165  SetNonNullError(error, [NSError
166  errorWithDomain: NSCoreDataErrorDomain
167  code: NSValidationValueOfIncorrectClassError
168  userInfo: errorUserInfo]);
169 
170  return NO;
171  }
172  if ([[obj entity] _isSubentityOf: destEntity] == NO)
173  {
174  SetNonNullError(error, [NSError
175  errorWithDomain: NSCoreDataErrorDomain
176  code: NSValidationValueHasIncorrectEntityError
177  userInfo: errorUserInfo]);
178 
179  return NO;
180  }
181  }
182  }
183  // otherwise, check the value is an NSManagedObject itself
184  else if ([value isKindOfClass: managedObjectClass])
185  {
186  // make sure correct cardinality is kept
187  if ([rel minCount] > 1)
188  {
189  SetNonNullError(error, [NSError
190  errorWithDomain: NSCoreDataErrorDomain
191  code: NSValidationRelationshipLacksMinimumCountError
192  userInfo: errorUserInfo]);
193 
194  return NO;
195  }
196  if ([rel maxCount] < 1)
197  {
198  SetNonNullError(error, [NSError
199  errorWithDomain: NSCoreDataErrorDomain
200  code: NSValidationRelationshipExceedsMaximumCountError
201  userInfo: errorUserInfo]);
202 
203  return NO;
204  }
205 
206  if ([[value entity] _isSubentityOf: destEntity] == NO)
207  {
208  SetNonNullError(error, [NSError
209  errorWithDomain: NSCoreDataErrorDomain
210  code: NSValidationValueHasIncorrectEntityError
211  userInfo: errorUserInfo]);
212 
213  return NO;
214  }
215  }
216  // otherwise fail - incorrect value type
217  else
218  {
219  SetNonNullError(error, [NSError
220  errorWithDomain: NSCoreDataErrorDomain
221  code: NSValidationValueOfIncorrectClassError
222  userInfo: errorUserInfo]);
223 
224  return NO;
225  }
226 }*/
227 
239 @implementation NSManagedObject
240 
241 + (BOOL) automaticallyNotifiesObserversForKey: (NSString *) aKey
242 {
243  return NO;
244 }
245 
246 - (void) dealloc
247 {
248  TEST_RELEASE(_entity);
249  TEST_RELEASE(_objectID);
250  TEST_RELEASE(_changedValues);
251  TEST_RELEASE(_data);
252 
253  [super dealloc];
254 }
255 
265 - (id) initWithEntity: (NSEntityDescription *) entity
266  insertIntoManagedObjectContext: (NSManagedObjectContext *) ctxt
267 {
268  if ((self = [super init]))
269  {
270  if ([entity isAbstract])
271  {
272  [NSException raise: NSInvalidArgumentException
273  format: _(@"Tried to initialize a managed object "
274  @"from an abstract entity (%@)."),
275  [entity name]];
276  }
277 
278  ASSIGN(_entity, entity);
279 
280  [ctxt insertObject: self];
281 
282  }
283  return self;
284 }
285 
290 - (NSManagedObjectContext *) managedObjectContext
291 {
292  return _context;
293 }
294 
298 - (NSEntityDescription *) entity
299 {
300  return _entity;
301 }
302 
309 {
310  // get a new temporary object ID, if necessary
311  if (_objectID == nil)
312  {
313  _objectID = [[NSManagedObjectID alloc] _initWithEntity: _entity];
314  }
315 
316  return _objectID;
317 }
318 
323 - (BOOL) isInserted
324 {
325  return [[_context insertedObjects] containsObject: self];
326 }
327 
333 - (BOOL) isUpdated
334 {
335  return [[_context updatedObjects] containsObject: self];
336 }
337 
343 - (BOOL) isDeleted
344 {
345  return _isDeleted;
346 }
347 
352 - (BOOL) isFault
353 {
354  return _isFault;
355 }
356 
362 - (void) awakeFromFetch
363 {}
364 
369 - (void) awakeFromInsert
370 {}
371 
372 - (NSDictionary *) changedValues
373 {
374  return [[_changedValues copy] autorelease];
375 }
376 
377 - (void) willSave
378 {}
379 
380 - (void) didSave
381 {}
382 
383 - (void) didTurnIntoFault
384 {}
385 
389 - (id) valueForKey: (NSString *) key
390 {
391  id value;
392 
393  // just makes sure the key is valid
394  [self _validatedPropertyForKey: key];
395 
396  [self willAccessValueForKey: key];
397  value = [self _primitiveValueForKey: key doValidation: NO];
398  [self didAccessValueForKey: key];
399 
400  return value;
401 }
402 
407 - (void) setValue: (id) value
408  forKey: (NSString *) key
409 {
410  NSPropertyDescription * property;
411 
412  property = [self _validatedPropertyForKey: key];
413  if ([self _validateValue: &value
414  forKey: key
415  error: NULL
416  property: property] == NO)
417  {
418  [NSException raise: NSInvalidArgumentException
419  format: _(@"Invalid value for key %@ specified."), key];
420  }
421 
422  [self willChangeValueForKey: key];
423  [self _setPrimitiveValue: value forKey: key doValidation: NO];
424  [self didChangeValueForKey: key];
425 }
426 
430 - (id) primitiveValueForKey: (NSString *) key
431 {
432  return [self _primitiveValueForKey: key doValidation: YES];
433 }
434 
438 - (void) setPrimitiveValue: (id) value
439  forKey: (NSString *) key
440 {
441  // Validate the value - internal methods invoke the internal method
442  // explicitly, so this method is invoked only by external code from
443  // which proper validation can't be expected.
444  [self _setPrimitiveValue: value forKey: key doValidation: YES];
445 }
446 
447 // Validation
448 
449 - (BOOL) validateValue: (id *) value
450  forKey: (NSString *) key
451  error: (NSError **) error
452 {
453  return [self _validateValue: value
454  forKey: key
455  error: error
456  property: [self _validatedPropertyForKey: key]];
457 }
458 
465 - (BOOL) validateForDelete: (NSError **) error
466 {
467  NSEnumerator * e;
468  NSRelationshipDescription * rel;
469  NSMutableArray * errors = [NSMutableArray array];
470 
471  e = [[self _allPropertiesOfSubclass: [NSRelationshipDescription class]]
472  objectEnumerator];
473  while ((rel = [e nextObject]) != nil)
474  {
475  NSString * key = [rel name];
476  id value = [self _primitiveValueForKey: key doValidation: NO];
477 
478  if ([rel deleteRule] == NSDenyDeleteRule &&
479  (([rel isToMany] && value != nil && [value count] != 0) ||
480  (value != nil)))
481  {
482  if (error == NULL)
483  {
484  return NO;
485  }
486  else
487  {
488  NSError * localError;
489  NSDictionary * userInfo;
490 
491  userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
492  value, NSValidationValueErrorKey,
493  key, NSValidationKeyErrorKey,
494  nil];
495  localError = [NSError
496  errorWithDomain: NSCoreDataErrorDomain
497  code: NSValidationRelationshipDeniedDeleteError
498  userInfo: userInfo];
499 
500  [errors addObject: localError];
501  }
502  }
503  }
504 
505  if ([errors count] > 0)
506  {
507  ConstructComplexError(error, errors);
508 
509  return NO;
510  }
511  else
512  {
513  return YES;
514  }
515 }
516 
517 - (BOOL) validateForInsert: (NSError **) error
518 {
519  // NB. What is this method actually supposed to do??
520 
521  return YES;
522 }
523 
524 - (BOOL) validateForUpdate: (NSError **) error
525 {
526  NSEnumerator * e;
527  NSPropertyDescription * property;
528  NSMutableArray * errors = [NSMutableArray array];
529 
530  e = [[self _allPropertiesOfSubclass: [NSPropertyDescription class]]
531  objectEnumerator];
532  while ((property = [e nextObject]) != nil)
533  {
534  NSString * key = [property name];
535  id value = [self _primitiveValueForKey: key
536  doValidation: NO];
537  NSError * localError;
538 
539  if ([self _validateValue: &value
540  forKey: key
541  error: &localError
542  property: property] == NO)
543  {
544  // if no errors are requested, stop at the first one
545  if (error == NULL)
546  {
547  return NO;
548  }
549  else
550  {
551  [errors addObject: localError];
552  }
553  }
554  }
555 
556  if ([errors count] > 0)
557  {
558  ConstructComplexError(error, errors);
559 
560  return NO;
561  }
562  else
563  {
564  return YES;
565  }
566 }
567 
568 // Key-value observing
569 
570 - (void) didAccessValueForKey: (NSString *) key
571 {
572 }
573 
574 - (void) didChangeValueForKey: (NSString *) key
575 {
576  [super didChangeValueForKey: key];
577 }
578 
579 - (void) didChangeValueForKey: (NSString *) key
580  withSetMutation: (NSKeyValueSetMutationKind) mutationKind
581  usingObjects: (NSSet *) objects
582 {
583  [super didChangeValueForKey: key
584  withSetMutation: mutationKind
585  usingObjects: objects];
586 }
587 
588 - (void *) observationInfo
589 {
590  return [super observationInfo];
591 }
592 
593 - (void) setObservationInfo: (void *) info
594 {
595  [super setObservationInfo: info];
596 }
597 
598 - (void) willAccessValueForKey: (NSString *) key
599 {
600 }
601 
602 - (void) willChangeValueForKey: (NSString *) key
603 {
604  [super willChangeValueForKey: key];
605 }
606 
607 - (void) willChangeValueForKey: (NSString *) key
608  withSetMutation: (NSKeyValueSetMutationKind) mutationKind
609  usingObjects: (NSSet *) objects
610 {
611  [super willChangeValueForKey: key
612  withSetMutation: mutationKind
613  usingObjects: objects];
614 }
615 
616 
623 - (id) _initAsFaultWithEntity: (NSEntityDescription *) entity
624  ownedByContext: (NSManagedObjectContext *) context
625 {
626  if ((self = [super init]))
627  {
628  if ([entity isAbstract])
629  {
630  [NSException raise: NSInvalidArgumentException
631  format: _(@"Tried to initialize a managed object "
632  @"from an abstract entity (%@)."),
633  [entity name]];
634  }
635 
636  ASSIGN(_entity, entity);
637 
638  _context = context;
639  _isFault = YES;
640  }
641  return self;
642  }
643 
652 - (void) _setObjectID: (NSManagedObjectID *) newID
653 {
654  NSAssert([newID isTemporaryID] == NO, _(@"Tried to assign to a managed "
655  @"object a temporary object ID."));
656 
657  ASSIGN(_objectID, newID);
658 }
659 
664 - (void) _setDeleted: (BOOL) flag
665 {
666  _isDeleted = flag;
667 }
668 
673 - (void) _setFault: (BOOL) flag
674 {
675  _isFault = flag;
676 }
677 
683 - (void) _insertedIntoContext: (NSManagedObjectContext *) ctxt
684 {
685  NSAssert(_context == nil || _context == ctxt, _(@"Tried to re-insert a "
686  @"managed object into different managed object context."));
687 
688  _context = ctxt;
689 }
690 
699 - (void) _removedFromContext
700 {
701  NSAssert(_context != nil, _(@"Attempted to remove from a context an "
702  @"already removed managed object."));
703 
704  _context = nil;
705 }
706 
713 - (NSPropertyDescription *) _validatedPropertyForKey: (NSString *) key
714 {
715  NSPropertyDescription * desc = nil;
716  NSEntityDescription * entity;
717 
718  // Look for the property by name, running upwards through the
719  // entity hierarchy if necessary.
720  for (entity = _entity;
721  desc == nil && entity != nil;
722  entity = [entity superentity])
723  {
724  desc = [[entity propertiesByName] objectForKey: key];
725  }
726 
727  if (desc != nil)
728  {
729  return desc;
730  }
731  else
732  {
733  [NSException raise: NSInvalidArgumentException //NSUnknownKeyException
734  format: _(@"Invalid key specified. The key does not "
735  @"exist in the model.")];
736 
737  return nil;
738  }
739 }
740 
745 - (NSArray *) _allPropertiesOfSubclass: (Class) aClass
746 {
747  NSMutableArray * properties;
748  NSEntityDescription * entity;
749 
750  NSAssert(aClass != nil, _(@"Nil class argument."));
751 
752  properties = [NSMutableArray array];
753 
754  for (entity = _entity; entity != nil; entity = [entity superentity])
755  {
756  NSEnumerator * e;
757  NSPropertyDescription * property;
758 
759  e = [[entity properties] objectEnumerator];
760  while ((property = [e nextObject]) != nil)
761  {
762  if ([property isKindOfClass: aClass])
763  {
764  [properties addObject: property];
765  }
766  }
767  }
768 
769  return [[properties copy] autorelease];
770 }
771 
782 // TODO - finish this method. Validation is partially broken until
783 // we have predicate support in Foundation.
784 - (BOOL) _validateValue: (id *) val
785  forKey: (NSString *) key
786  error: (NSError **) error
787  property: (NSPropertyDescription *) property
788 {
789  id value = *val;
790  SEL customValidationSel;
791 
792  // TODO - use predicates to validate the value
793 
794  if (value != nil)
795  {
796 /* if ([desc isKindOfClass: [NSAttributeDescription class]])
797  {
798  if (ValidateAttributeValue((NSAttributeDescription *) desc,
799  value, error) == NO)
800  {
801  return NO;
802  }
803  }
804  else if ([desc isKindOfClass: [NSRelationshipDescription class]])
805  {
806  if (ValidateRelationshipValue((NSRelationshipDescription *) desc,
807  value, error) == NO)
808  {
809  return NO;
810  }
811  else
812  {
813  [NSException raise: NSInternalInconsistencyException
814  format: _(@"Passed non-attribute, non-relationship "
815  @"property description to internal validation "
816  @"method.")];
817  }*/
818  }
819  // if `nil' is specified, the property must be optional
820  else
821  {
822  if ([property isOptional] == NO)
823  {
824  SetNonNullError(error, [NSError
825  errorWithDomain: NSCoreDataErrorDomain
826  code: NSValidationMissingMandatoryPropertyError
827  userInfo: [NSDictionary dictionaryWithObjectsAndKeys:
828  self, NSValidationObjectErrorKey,
829  key, NSValidationKeyErrorKey,
830  nil]]);
831 
832  return NO;
833  }
834  }
835 
836  // now do the customizable "validate<Key>:error:" validation
837  customValidationSel = NSSelectorFromString([NSString stringWithFormat:
838  @"validate%@:error:", key]);
839  if ([self respondsToSelector: customValidationSel])
840  {
841  BOOL retval;
842  NSInvocation * invocation = [[NSInvocation new] autorelease];
843 
844  [invocation setTarget: self];
845  [invocation setSelector: customValidationSel];
846  [invocation setArgument: &value
847  atIndex: 2];
848  [invocation setArgument: &error
849  atIndex: 3];
850  [invocation invoke];
851  [invocation getReturnValue: &retval];
852 
853  return retval;
854  }
855  else
856  {
857  return YES;
858  }
859 }
860 
867 - (id) _primitiveValueForKey: (NSString *) key doValidation: (BOOL) validate
868 {
869  if (validate == YES)
870  {
871  [self _validatedPropertyForKey: key];
872  }
873  if (_isFault)
874  {
875  [self _fireFault];
876  }
877 
878  return [_data objectForKey: key];
879 }
880 
890 - (void) _setPrimitiveValue: (id) value
891  forKey: (NSString *) key
892  doValidation: (BOOL) validate
893 {
894  NSPropertyDescription * property;
895  NSError * error;
896 
897  property = [self _validatedPropertyForKey: key];
898 
899  // validate the value if requested
900  if (validate)
901  {
902  if ([self _validateValue: &value
903  forKey: key
904  error: &error
905  property: property] != YES)
906  {
907  [NSException raise: NSInvalidArgumentException
908  format: _(@"Invalid value for key \"%@\" specified."),
909  key];
910  }
911  }
912 
913  if (_isFault)
914  {
915  [self _fireFault];
916  }
917 
918  [_data setObject: value forKey: key];
919 
920  if ([property isTransient] == NO)
921  {
922  if (_changedValues == nil)
923  {
924  _changedValues = [NSMutableDictionary new];
925  }
926  [_changedValues setObject: value forKey: key];
927  }
928 }
929 
930 @end
For implementation notes see "Documentation/NSManagedObjectID.txt" in the source distribution of the ...
Validates whether value'' is a valid value forattribute'', returning YES if it is,...
NSManagedObjectContext * managedObjectContext()
Returns the managed object context to which the receiver belongs.
void awakeFromInsert()
Invoked automatically after the receiver has been inserted into a managed object context.
BOOL isUpdated()
Returns YES if the receiver has changes that have not yet been written to a persistent store (the rec...
BOOL isFault()
Returns YES if the receiver is a fault, and NO otherwise.
void awakeFromFetch()
Invoked automatically after the receiver has been fetched from a persistent store.
NSEntityDescription * entity()
Returns the entity of the receiver.
BOOL isInserted()
Returns YES if the receiver is inserted in a managed object context, and NO otherwise.
BOOL isDeleted()
Returns YES if the receiver has been scheduled in it's parent managed object context for deletion fro...
NSManagedObjectID * objectID()
Returns the object ID of the receiver.