GNUstep Core Data
0.1
|
00001 /* Implementation of the NSManagedObject class for the GNUstep 00002 Core Data framework. 00003 Copyright (C) 2005 Free Software Foundation, Inc. 00004 00005 Written by: Saso Kiselkov <diablos@manga.sk> 00006 Date: August 2005 00007 00008 This file is part of the GNUstep Core Data framework. 00009 00010 This library is free software; you can redistribute it and/or 00011 modify it under the terms of the GNU Lesser General Public 00012 License as published by the Free Software Foundation; either 00013 version 2.1 of the License, or (at your option) any later version. 00014 00015 This library is distributed in the hope that it will be useful, 00016 but WITHOUT ANY WARRANTY; without even the implied warranty of 00017 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00018 Lesser General Public License for more details. 00019 00020 You should have received a copy of the GNU Lesser General Public 00021 License along with this library; if not, write to the Free 00022 Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111 USA. 00023 */ 00024 00025 #import "CoreDataHeaders.h" 00026 00033 static inline BOOL 00034 IsCollection(id object) 00035 { 00036 if ([object isKindOfClass: [NSSet class]] || 00037 [object isKindOfClass: [NSArray class]]) 00038 { 00039 return YES; 00040 } 00041 else 00042 { 00043 return NO; 00044 } 00045 } 00046 00055 static inline void 00056 ConstructComplexError(NSError ** target, NSArray * errors) 00057 { 00058 unsigned int errorCount = [errors count]; 00059 00060 if (errorCount == 1) 00061 { 00062 *target = [errors objectAtIndex: 0]; 00063 } 00064 else if (errorCount > 1) 00065 { 00066 NSError * newError; 00067 NSDictionary * userInfo; 00068 00069 userInfo = [NSDictionary 00070 dictionaryWithObject: [[errors copy] autorelease] 00071 forKey: NSDetailedErrorsKey]; 00072 newError = [NSError errorWithDomain: NSCoreDataErrorDomain 00073 code: NSValidationMultipleErrorsError 00074 userInfo: userInfo]; 00075 00076 *target = newError; 00077 } 00078 } 00079 00085 /* 00086 static BOOL 00087 ValidateAttributeValue(NSAttributeDescription * attribute, 00088 id value, 00089 NSError ** error) 00090 { 00091 Class attrClass = NSClassFromString([attribute attributeValueClassName]); 00092 00093 // check the class is correct 00094 if ([value isKindOfClass: attrClass] == NO) 00095 { 00096 NSDictionary * userInfo = [NSDictionary dictionaryWithObjectsAndKeys: 00097 self, NSValidationObjectErrorKey, 00098 [attr name], NSValidationKeyErrorKey, 00099 value, NSValidationValueErrorKey, 00100 nil]; 00101 00102 SetNonNullError(error, [NSError 00103 errorWithDomain: NSCoreDataErrorDomain 00104 code: NSValidationValueOfIncorrectClassError 00105 userInfo: userInfo]); 00106 00107 return NO; 00108 } 00109 00110 return YES; 00111 } 00112 */ 00113 00117 /* 00118 static BOOL 00119 ValidateRelationshipValue(NSRelationshipDescription * relationship, 00120 id value, 00121 NSError ** error) 00122 NSEntityDescription * destEntity = [relationship destinationEntity]; 00123 Class managedObjectClass = [NSManagedObject class]; 00124 NSDictionary * errorUserInfo = [NSDictionary dictionaryWithObjectsAndKeys: 00125 self, NSValidationObjectErrorKey, 00126 [relationship name], NSValidationKeyErrorKey, 00127 value, NSValidationValueErrorKey, 00128 nil]; 00129 00130 // if the relationship is a to-many relationship and the passed 00131 // object is a collection, check that all contained objects are 00132 // NSManagedObject's and have the correct entity set. 00133 if ([rel isToMany] && IsCollection(value)) 00134 { 00135 NSEnumerator * e; 00136 id obj; 00137 int count; 00138 00139 // make sure correct cardinality is kept 00140 count = [value count]; 00141 if (count < [rel minCount]) 00142 { 00143 SetNonNullError(error, [NSError 00144 errorWithDomain: NSCoreDataErrorDomain 00145 code: NSValidationRelationshipLacksMinimumCountError 00146 userInfo: errorUserInfo]); 00147 00148 return NO; 00149 } 00150 if (count > [rel maxCount]) 00151 { 00152 SetNonNullError(error, [NSError 00153 errorWithDomain: NSCoreDataErrorDomain 00154 code: NSValidationRelationshipExceedsMaximumCountError 00155 userInfo: errorUserInfo]); 00156 00157 return NO; 00158 } 00159 00160 e = [value objectEnumerator]; 00161 while ((obj = [e nextObject]) != nil) 00162 { 00163 if ([obj isKindOfClass: managedObjectClass] == NO) 00164 { 00165 SetNonNullError(error, [NSError 00166 errorWithDomain: NSCoreDataErrorDomain 00167 code: NSValidationValueOfIncorrectClassError 00168 userInfo: errorUserInfo]); 00169 00170 return NO; 00171 } 00172 if ([[obj entity] _isSubentityOf: destEntity] == NO) 00173 { 00174 SetNonNullError(error, [NSError 00175 errorWithDomain: NSCoreDataErrorDomain 00176 code: NSValidationValueHasIncorrectEntityError 00177 userInfo: errorUserInfo]); 00178 00179 return NO; 00180 } 00181 } 00182 } 00183 // otherwise, check the value is an NSManagedObject itself 00184 else if ([value isKindOfClass: managedObjectClass]) 00185 { 00186 // make sure correct cardinality is kept 00187 if ([rel minCount] > 1) 00188 { 00189 SetNonNullError(error, [NSError 00190 errorWithDomain: NSCoreDataErrorDomain 00191 code: NSValidationRelationshipLacksMinimumCountError 00192 userInfo: errorUserInfo]); 00193 00194 return NO; 00195 } 00196 if ([rel maxCount] < 1) 00197 { 00198 SetNonNullError(error, [NSError 00199 errorWithDomain: NSCoreDataErrorDomain 00200 code: NSValidationRelationshipExceedsMaximumCountError 00201 userInfo: errorUserInfo]); 00202 00203 return NO; 00204 } 00205 00206 if ([[value entity] _isSubentityOf: destEntity] == NO) 00207 { 00208 SetNonNullError(error, [NSError 00209 errorWithDomain: NSCoreDataErrorDomain 00210 code: NSValidationValueHasIncorrectEntityError 00211 userInfo: errorUserInfo]); 00212 00213 return NO; 00214 } 00215 } 00216 // otherwise fail - incorrect value type 00217 else 00218 { 00219 SetNonNullError(error, [NSError 00220 errorWithDomain: NSCoreDataErrorDomain 00221 code: NSValidationValueOfIncorrectClassError 00222 userInfo: errorUserInfo]); 00223 00224 return NO; 00225 } 00226 }*/ 00227 00239 @implementation NSManagedObject 00240 00241 + (BOOL) automaticallyNotifiesObserversForKey: (NSString *) aKey 00242 { 00243 return NO; 00244 } 00245 00246 - (void) dealloc 00247 { 00248 TEST_RELEASE(_entity); 00249 TEST_RELEASE(_objectID); 00250 TEST_RELEASE(_changedValues); 00251 TEST_RELEASE(_data); 00252 00253 [super dealloc]; 00254 } 00255 00265 - (id) initWithEntity: (NSEntityDescription *) entity 00266 insertIntoManagedObjectContext: (NSManagedObjectContext *) ctxt 00267 { 00268 if ((self = [super init])) 00269 { 00270 if ([entity isAbstract]) 00271 { 00272 [NSException raise: NSInvalidArgumentException 00273 format: _(@"Tried to initialize a managed object " 00274 @"from an abstract entity (%@)."), 00275 [entity name]]; 00276 } 00277 00278 ASSIGN(_entity, entity); 00279 00280 [ctxt insertObject: self]; 00281 00282 } 00283 return self; 00284 } 00285 00290 - (NSManagedObjectContext *) managedObjectContext 00291 { 00292 return _context; 00293 } 00294 00298 - (NSEntityDescription *) entity 00299 { 00300 return _entity; 00301 } 00302 00308 - (NSManagedObjectID *) objectID 00309 { 00310 // get a new temporary object ID, if necessary 00311 if (_objectID == nil) 00312 { 00313 _objectID = [[NSManagedObjectID alloc] _initWithEntity: _entity]; 00314 } 00315 00316 return _objectID; 00317 } 00318 00323 - (BOOL) isInserted 00324 { 00325 return [[_context insertedObjects] containsObject: self]; 00326 } 00327 00333 - (BOOL) isUpdated 00334 { 00335 return [[_context updatedObjects] containsObject: self]; 00336 } 00337 00343 - (BOOL) isDeleted 00344 { 00345 return _isDeleted; 00346 } 00347 00352 - (BOOL) isFault 00353 { 00354 return _isFault; 00355 } 00356 00362 - (void) awakeFromFetch 00363 {} 00364 00369 - (void) awakeFromInsert 00370 {} 00371 00372 - (NSDictionary *) changedValues 00373 { 00374 return [[_changedValues copy] autorelease]; 00375 } 00376 00377 - (void) willSave 00378 {} 00379 00380 - (void) didSave 00381 {} 00382 00383 - (void) didTurnIntoFault 00384 {} 00385 00389 - (id) valueForKey: (NSString *) key 00390 { 00391 id value; 00392 00393 // just makes sure the key is valid 00394 [self _validatedPropertyForKey: key]; 00395 00396 [self willAccessValueForKey: key]; 00397 value = [self _primitiveValueForKey: key doValidation: NO]; 00398 [self didAccessValueForKey: key]; 00399 00400 return value; 00401 } 00402 00407 - (void) setValue: (id) value 00408 forKey: (NSString *) key 00409 { 00410 NSPropertyDescription * property; 00411 00412 property = [self _validatedPropertyForKey: key]; 00413 if ([self _validateValue: &value 00414 forKey: key 00415 error: NULL 00416 property: property] == NO) 00417 { 00418 [NSException raise: NSInvalidArgumentException 00419 format: _(@"Invalid value for key %@ specified."), key]; 00420 } 00421 00422 [self willChangeValueForKey: key]; 00423 [self _setPrimitiveValue: value forKey: key doValidation: NO]; 00424 [self didChangeValueForKey: key]; 00425 } 00426 00430 - (id) primitiveValueForKey: (NSString *) key 00431 { 00432 return [self _primitiveValueForKey: key doValidation: YES]; 00433 } 00434 00438 - (void) setPrimitiveValue: (id) value 00439 forKey: (NSString *) key 00440 { 00441 // Validate the value - internal methods invoke the internal method 00442 // explicitly, so this method is invoked only by external code from 00443 // which proper validation can't be expected. 00444 [self _setPrimitiveValue: value forKey: key doValidation: YES]; 00445 } 00446 00447 // Validation 00448 00449 - (BOOL) validateValue: (id *) value 00450 forKey: (NSString *) key 00451 error: (NSError **) error 00452 { 00453 return [self _validateValue: value 00454 forKey: key 00455 error: error 00456 property: [self _validatedPropertyForKey: key]]; 00457 } 00458 00465 - (BOOL) validateForDelete: (NSError **) error 00466 { 00467 NSEnumerator * e; 00468 NSRelationshipDescription * rel; 00469 NSMutableArray * errors = [NSMutableArray array]; 00470 00471 e = [[self _allPropertiesOfSubclass: [NSRelationshipDescription class]] 00472 objectEnumerator]; 00473 while ((rel = [e nextObject]) != nil) 00474 { 00475 NSString * key = [rel name]; 00476 id value = [self _primitiveValueForKey: key doValidation: NO]; 00477 00478 if ([rel deleteRule] == NSDenyDeleteRule && 00479 (([rel isToMany] && value != nil && [value count] != 0) || 00480 (value != nil))) 00481 { 00482 if (error == NULL) 00483 { 00484 return NO; 00485 } 00486 else 00487 { 00488 NSError * localError; 00489 NSDictionary * userInfo; 00490 00491 userInfo = [NSDictionary dictionaryWithObjectsAndKeys: 00492 value, NSValidationValueErrorKey, 00493 key, NSValidationKeyErrorKey, 00494 nil]; 00495 localError = [NSError 00496 errorWithDomain: NSCoreDataErrorDomain 00497 code: NSValidationRelationshipDeniedDeleteError 00498 userInfo: userInfo]; 00499 00500 [errors addObject: localError]; 00501 } 00502 } 00503 } 00504 00505 if ([errors count] > 0) 00506 { 00507 ConstructComplexError(error, errors); 00508 00509 return NO; 00510 } 00511 else 00512 { 00513 return YES; 00514 } 00515 } 00516 00517 - (BOOL) validateForInsert: (NSError **) error 00518 { 00519 // NB. What is this method actually supposed to do?? 00520 00521 return YES; 00522 } 00523 00524 - (BOOL) validateForUpdate: (NSError **) error 00525 { 00526 NSEnumerator * e; 00527 NSPropertyDescription * property; 00528 NSMutableArray * errors = [NSMutableArray array]; 00529 00530 e = [[self _allPropertiesOfSubclass: [NSPropertyDescription class]] 00531 objectEnumerator]; 00532 while ((property = [e nextObject]) != nil) 00533 { 00534 NSString * key = [property name]; 00535 id value = [self _primitiveValueForKey: key 00536 doValidation: NO]; 00537 NSError * localError; 00538 00539 if ([self _validateValue: &value 00540 forKey: key 00541 error: &localError 00542 property: property] == NO) 00543 { 00544 // if no errors are requested, stop at the first one 00545 if (error == NULL) 00546 { 00547 return NO; 00548 } 00549 else 00550 { 00551 [errors addObject: localError]; 00552 } 00553 } 00554 } 00555 00556 if ([errors count] > 0) 00557 { 00558 ConstructComplexError(error, errors); 00559 00560 return NO; 00561 } 00562 else 00563 { 00564 return YES; 00565 } 00566 } 00567 00568 // Key-value observing 00569 00570 - (void) didAccessValueForKey: (NSString *) key 00571 { 00572 } 00573 00574 - (void) didChangeValueForKey: (NSString *) key 00575 { 00576 [super didChangeValueForKey: key]; 00577 } 00578 00579 - (void) didChangeValueForKey: (NSString *) key 00580 withSetMutation: (NSKeyValueSetMutationKind) mutationKind 00581 usingObjects: (NSSet *) objects 00582 { 00583 [super didChangeValueForKey: key 00584 withSetMutation: mutationKind 00585 usingObjects: objects]; 00586 } 00587 00588 - (void *) observationInfo 00589 { 00590 return [super observationInfo]; 00591 } 00592 00593 - (void) setObservationInfo: (void *) info 00594 { 00595 [super setObservationInfo: info]; 00596 } 00597 00598 - (void) willAccessValueForKey: (NSString *) key 00599 { 00600 } 00601 00602 - (void) willChangeValueForKey: (NSString *) key 00603 { 00604 [super willChangeValueForKey: key]; 00605 } 00606 00607 - (void) willChangeValueForKey: (NSString *) key 00608 withSetMutation: (NSKeyValueSetMutationKind) mutationKind 00609 usingObjects: (NSSet *) objects 00610 { 00611 [super willChangeValueForKey: key 00612 withSetMutation: mutationKind 00613 usingObjects: objects]; 00614 } 00615 00616 00623 - (id) _initAsFaultWithEntity: (NSEntityDescription *) entity 00624 ownedByContext: (NSManagedObjectContext *) context 00625 { 00626 if ((self = [super init])) 00627 { 00628 if ([entity isAbstract]) 00629 { 00630 [NSException raise: NSInvalidArgumentException 00631 format: _(@"Tried to initialize a managed object " 00632 @"from an abstract entity (%@)."), 00633 [entity name]]; 00634 } 00635 00636 ASSIGN(_entity, entity); 00637 00638 _context = context; 00639 _isFault = YES; 00640 } 00641 return self; 00642 } 00643 00652 - (void) _setObjectID: (NSManagedObjectID *) newID 00653 { 00654 NSAssert([newID isTemporaryID] == NO, _(@"Tried to assign to a managed " 00655 @"object a temporary object ID.")); 00656 00657 ASSIGN(_objectID, newID); 00658 } 00659 00664 - (void) _setDeleted: (BOOL) flag 00665 { 00666 _isDeleted = flag; 00667 } 00668 00673 - (void) _setFault: (BOOL) flag 00674 { 00675 _isFault = flag; 00676 } 00677 00683 - (void) _insertedIntoContext: (NSManagedObjectContext *) ctxt 00684 { 00685 NSAssert(_context == nil || _context == ctxt, _(@"Tried to re-insert a " 00686 @"managed object into different managed object context.")); 00687 00688 _context = ctxt; 00689 } 00690 00699 - (void) _removedFromContext 00700 { 00701 NSAssert(_context != nil, _(@"Attempted to remove from a context an " 00702 @"already removed managed object.")); 00703 00704 _context = nil; 00705 } 00706 00713 - (NSPropertyDescription *) _validatedPropertyForKey: (NSString *) key 00714 { 00715 NSPropertyDescription * desc = nil; 00716 NSEntityDescription * entity; 00717 00718 // Look for the property by name, running upwards through the 00719 // entity hierarchy if necessary. 00720 for (entity = _entity; 00721 desc == nil && entity != nil; 00722 entity = [entity superentity]) 00723 { 00724 desc = [[entity propertiesByName] objectForKey: key]; 00725 } 00726 00727 if (desc != nil) 00728 { 00729 return desc; 00730 } 00731 else 00732 { 00733 //NSUnknownKeyException[NSException raise: NSInvalidArgumentException 00734 format: _(@"Invalid key specified. The key does not " 00735 @"exist in the model.")]; 00736 00737 return nil; 00738 } 00739 } 00740 00745 - (NSArray *) _allPropertiesOfSubclass: (Class) aClass 00746 { 00747 NSMutableArray * properties; 00748 NSEntityDescription * entity; 00749 00750 NSAssert(aClass != nil, _(@"Nil class argument.")); 00751 00752 properties = [NSMutableArray array]; 00753 00754 for (entity = _entity; entity != nil; entity = [entity superentity]) 00755 { 00756 NSEnumerator * e; 00757 NSPropertyDescription * property; 00758 00759 e = [[entity properties] objectEnumerator]; 00760 while ((property = [e nextObject]) != nil) 00761 { 00762 if ([property isKindOfClass: aClass]) 00763 { 00764 [properties addObject: property]; 00765 } 00766 } 00767 } 00768 00769 return [[properties copy] autorelease]; 00770 } 00771 00782 // TODO - finish this method. Validation is partially broken until 00783 // we have predicate support in Foundation. 00784 - (BOOL) _validateValue: (id *) val 00785 forKey: (NSString *) key 00786 error: (NSError **) error 00787 property: (NSPropertyDescription *) property 00788 { 00789 id value = *val; 00790 SEL customValidationSel; 00791 00792 // TODO - use predicates to validate the value 00793 00794 if (value != nil) 00795 { 00796 /* if ([desc isKindOfClass: [NSAttributeDescription class]]) 00797 { 00798 if (ValidateAttributeValue((NSAttributeDescription *) desc, 00799 value, error) == NO) 00800 { 00801 return NO; 00802 } 00803 } 00804 else if ([desc isKindOfClass: [NSRelationshipDescription class]]) 00805 { 00806 if (ValidateRelationshipValue((NSRelationshipDescription *) desc, 00807 value, error) == NO) 00808 { 00809 return NO; 00810 } 00811 else 00812 { 00813 [NSException raise: NSInternalInconsistencyException 00814 format: _(@"Passed non-attribute, non-relationship " 00815 @"property description to internal validation " 00816 @"method.")]; 00817 }*/ 00818 } 00819 // if `nil' is specified, the property must be optional 00820 else 00821 { 00822 if ([property isOptional] == NO) 00823 { 00824 SetNonNullError(error, [NSError 00825 errorWithDomain: NSCoreDataErrorDomain 00826 code: NSValidationMissingMandatoryPropertyError 00827 userInfo: [NSDictionary dictionaryWithObjectsAndKeys: 00828 self, NSValidationObjectErrorKey, 00829 key, NSValidationKeyErrorKey, 00830 nil]]); 00831 00832 return NO; 00833 } 00834 } 00835 00836 // now do the customizable "validate<Key>:error:" validation 00837 customValidationSel = NSSelectorFromString([NSString stringWithFormat: 00838 @"validate%@:error:", key]); 00839 if ([self respondsToSelector: customValidationSel]) 00840 { 00841 BOOL retval; 00842 NSInvocation * invocation = [[NSInvocation new] autorelease]; 00843 00844 [invocation setTarget: self]; 00845 [invocation setSelector: customValidationSel]; 00846 [invocation setArgument: &value 00847 atIndex: 2]; 00848 [invocation setArgument: &error 00849 atIndex: 3]; 00850 [invocation invoke]; 00851 [invocation getReturnValue: &retval]; 00852 00853 return retval; 00854 } 00855 else 00856 { 00857 return YES; 00858 } 00859 } 00860 00867 - (id) _primitiveValueForKey: (NSString *) key doValidation: (BOOL) validate 00868 { 00869 if (validate == YES) 00870 { 00871 [self _validatedPropertyForKey: key]; 00872 } 00873 if (_isFault) 00874 { 00875 [self _fireFault]; 00876 } 00877 00878 return [_data objectForKey: key]; 00879 } 00880 00890 - (void) _setPrimitiveValue: (id) value 00891 forKey: (NSString *) key 00892 doValidation: (BOOL) validate 00893 { 00894 NSPropertyDescription * property; 00895 NSError * error; 00896 00897 property = [self _validatedPropertyForKey: key]; 00898 00899 // validate the value if requested 00900 if (validate) 00901 { 00902 if ([self _validateValue: &value 00903 forKey: key 00904 error: &error 00905 property: property] != YES) 00906 { 00907 [NSException raise: NSInvalidArgumentException 00908 format: _(@"Invalid value for key \"%@\" specified."), 00909 key]; 00910 } 00911 } 00912 00913 if (_isFault) 00914 { 00915 [self _fireFault]; 00916 } 00917 00918 [_data setObject: value forKey: key]; 00919 00920 if ([property isTransient] == NO) 00921 { 00922 if (_changedValues == nil) 00923 { 00924 _changedValues = [NSMutableDictionary new]; 00925 } 00926 [_changedValues setObject: value forKey: key]; 00927 } 00928 } 00929 00930 @end