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
33static inline BOOL
34IsCollection(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
55static inline void
56ConstructComplexError(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/*
86static BOOL
87ValidateAttributeValue(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/*
118static BOOL
119ValidateRelationshipValue(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
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
363{}
364
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.