GNUstep Core Data  0.1
NSManagedObject.m
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