NSManagedObject.m
1 /* Copyright (c) 2008 Dan Knapp 2 3 Permission is hereby granted, free of charge, to any person obtaining a copy of 4 this software and associated documentation files (the "Software"), to deal in 5 the Software without restriction, including without limitation the rights to 6 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 the Software, and to permit persons to whom the Software is furnished to do so, 8 subject to the following conditions: 9 10 The above copyright notice and this permission notice shall be included in all 11 copies or substantial portions of the Software. 12 13 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ 19 #import "NSEntityDescription-Private.h" 20 #import "NSManagedObjectContext-Private.h" 21 #import "NSManagedObjectID-Private.h" 22 #import "NSManagedObjectMutableSet.h" 23 #import "NSManagedObjectSet.h" 24 #import "NSManagedObjectSetEnumerator.h" 25 #import <CoreData/NSAtomicStoreCacheNode.h> 26 #import <CoreData/NSAttributeDescription.h> 27 #import <CoreData/NSManagedObject.h> 28 #import <CoreData/NSRelationshipDescription.h> 29 #import <Foundation/NSRaise.h> 30 31 NSString *const NSValidationKeyErrorKey = @"NSValidationErrorKey"; 32 NSString *const NSValidationObjectErrorKey = @"NSValidationErrorObject"; 33 NSString *const NSValidationPredicateErrorKey = @"NSValidationErrorPredicate"; 34 NSString *const NSValidationValueErrorKey = @"NSValidationErrorValue"; 35 36 @implementation NSManagedObject 37 38 - init { 39 NSLog(@"Error - can't initialize an NSManagedObject with -init"); 40 return nil; 41 } 42 43 - initWithObjectID: (NSManagedObjectID *) objectID 44 managedObjectContext: (NSManagedObjectContext *) context 45 { 46 NSEntityDescription *entity = [objectID entity]; 47 NSString *className = [entity managedObjectClassName]; 48 Class class = NSClassFromString(className); 49 50 if (class == Nil) { 51 NSLog(@"Unable to find class %@ specified by entity %@ in the runtime, " 52 @"using NSManagedObject,objectID=%@", 53 className, [entity name], objectID); 54 55 class = [NSManagedObject class]; 56 } 57 58 [super dealloc]; 59 self = [class allocWithZone: NULL]; 60 61 _objectID = [objectID copy]; 62 _context = context; 63 _committedValues = nil; 64 _changedValues = [[NSMutableDictionary alloc] init]; 65 _isFault = YES; 66 return self; 67 } 68 69 - initWithEntity: (NSEntityDescription *) entity 70 insertIntoManagedObjectContext: (NSManagedObjectContext *) context 71 { 72 NSManagedObjectID *objectID = 73 [[[NSManagedObjectID alloc] initWithEntity: entity] autorelease]; 74 75 if ((self = [self initWithObjectID: objectID 76 managedObjectContext: context]) == nil) 77 return nil; 78 79 NSDictionary *attributes = [entity attributesByName]; 80 NSEnumerator *state = [attributes keyEnumerator]; 81 NSString *key; 82 83 while ((key = [state nextObject]) != nil) { 84 id object = [attributes objectForKey: key]; 85 id value = [object defaultValue]; 86 87 if (value != nil) 88 [self setPrimitiveValue: value forKey: key]; 89 } 90 91 [context insertObject: self]; 92 93 return self; 94 } 95 96 - (void) dealloc { 97 [self didTurnIntoFault]; 98 99 [_objectID release]; 100 [_changedValues release]; 101 [super dealloc]; 102 } 103 104 - (NSUInteger) hash { 105 return [_objectID hash]; 106 } 107 108 - (BOOL) isEqual: otherX { 109 if (![otherX isKindOfClass: [NSManagedObject class]]) 110 return NO; 111 112 NSManagedObject *other = otherX; 113 114 return [_objectID isEqual: other->_objectID]; 115 } 116 117 - (NSEntityDescription *) entity { 118 return [_objectID entity]; 119 } 120 121 - (NSManagedObjectID *) objectID { 122 return _objectID; 123 } 124 125 - self { 126 return self; 127 } 128 129 - (NSManagedObjectContext *) managedObjectContext { 130 return _context; 131 } 132 133 - (BOOL) isInserted { 134 return _isInserted; 135 } 136 137 - (BOOL) isUpdated { 138 return _isUpdated; 139 } 140 141 - (BOOL) isDeleted { 142 return _isDeleted; 143 } 144 145 - (BOOL) isFault { 146 return _isFault; 147 } 148 149 - (BOOL) hasFaultForRelationshipNamed: (NSString *) key { 150 NSUnimplementedMethod(); 151 return NO; 152 } 153 154 - (void) awakeFromFetch { 155 NSUnimplementedMethod(); 156 } 157 158 - (void) awakeFromInsert { 159 NSUnimplementedMethod(); 160 } 161 162 - (NSDictionary *) changedValues { 163 return _changedValues; 164 } 165 166 - (NSDictionary *) _committedValues { 167 168 if ([[self objectID] isTemporaryID]) 169 return nil; 170 171 if (_committedValues == nil) { 172 NSAtomicStoreCacheNode *node = 173 [_context _cacheNodeForObjectID: [self objectID]]; 174 NSDictionary *propertyCache = [node propertyCache]; 175 NSMutableDictionary *storedValues = [[NSMutableDictionary alloc] init]; 176 177 NSArray *properties = [[self entity] properties]; 178 179 for (NSPropertyDescription *property in properties) { 180 NSString *name = [property name]; 181 182 if ([property isKindOfClass: [NSAttributeDescription class]]) { 183 id value = [propertyCache objectForKey: name]; 184 185 if (value != nil) { 186 [storedValues setObject: value forKey: name]; 187 } 188 } else if ([property isKindOfClass: [NSRelationshipDescription 189 class]]) { 190 NSRelationshipDescription *relationship = 191 (NSRelationshipDescription *) property; 192 id indirectValue = [propertyCache objectForKey: name]; 193 194 if (indirectValue != nil) { 195 id value; 196 197 if (![relationship isToMany]) 198 value = [indirectValue objectID]; 199 else { 200 value = [NSMutableSet set]; 201 202 for (NSAtomicStoreCacheNode *relNode in indirectValue) { 203 [value addObject: [relNode objectID]]; 204 } 205 } 206 207 [storedValues setObject: value forKey: name]; 208 } 209 } 210 } 211 212 [_committedValues release]; 213 _committedValues = storedValues; 214 } 215 return _committedValues; 216 } 217 218 - (NSDictionary *) committedValuesForKeys: (NSArray *) keys { 219 if (keys == nil) 220 return [self _committedValues]; 221 else { 222 NSMutableDictionary *result = [NSMutableDictionary dictionary]; 223 224 for (NSString *key in keys) { 225 id object = [[self _committedValues] objectForKey: key]; 226 227 if (object != nil) { 228 [result setObject: object forKey: key]; 229 } 230 } 231 232 return result; 233 } 234 } 235 236 - (void) didSave { 237 } 238 239 - (void) willTurnIntoFault { 240 // do nothing per doc.s 241 } 242 243 - (void) didTurnIntoFault { 244 // do nothing also? 245 } 246 247 - (void) willSave { 248 } 249 250 - valueForKey: (NSString *) key { 251 if (!key) 252 return [self valueForUndefinedKey: nil]; 253 254 NSPropertyDescription *property = 255 [[self entity] _propertyForSelector: NSSelectorFromString(key)]; 256 NSString *propertyName = [property name]; 257 258 if ([property isKindOfClass: [NSAttributeDescription class]]) { 259 [self willAccessValueForKey: propertyName]; 260 261 id result = [self primitiveValueForKey: propertyName]; 262 263 [self didAccessValueForKey: propertyName]; 264 265 return result; 266 } else if ([property isKindOfClass: [NSRelationshipDescription class]]) { 267 NSRelationshipDescription *relationship = 268 (NSRelationshipDescription *) property; 269 270 [self willAccessValueForKey: propertyName]; 271 272 id result = [self primitiveValueForKey: propertyName]; 273 274 if (result != nil) { 275 if ([relationship isToMany]) 276 result = [[[NSManagedObjectSet alloc] 277 initWithManagedObjectContext: _context 278 set: result] autorelease]; 279 else 280 result = [_context objectWithID: result]; 281 } 282 283 [self didAccessValueForKey: propertyName]; 284 285 return result; 286 } 287 288 return [super valueForKey: key]; 289 } 290 291 - (void) setValue: value forKey: (NSString *) key { 292 NSPropertyDescription *property = 293 [[self entity] _propertyForSelector: NSSelectorFromString(key)]; 294 NSString *propertyName = [property name]; 295 296 if ([property isKindOfClass: [NSAttributeDescription class]]) { 297 [self willChangeValueForKey: propertyName]; 298 [self setPrimitiveValue: value forKey: propertyName]; 299 [self didChangeValueForKey: propertyName]; 300 return; 301 } else if ([property isKindOfClass: [NSRelationshipDescription class]]) { 302 NSRelationshipDescription *relationship = 303 (NSRelationshipDescription *) property; 304 NSRelationshipDescription *inverse = [relationship inverseRelationship]; 305 NSString *inverseName = [inverse name]; 306 id valueByID; 307 308 if ([relationship isToMany]) { 309 NSMutableSet *set = [NSMutableSet set]; 310 311 for (NSManagedObject *object in value) 312 [set addObject: [object objectID]]; 313 314 valueByID = set; 315 } else { 316 valueByID = [value objectID]; 317 } 318 319 if (inverse != nil) { 320 id primitivePrevious = [self primitiveValueForKey: key]; 321 322 if (primitivePrevious != nil) { 323 NSSet *allPrevious = 324 [relationship isToMany] 325 ? primitivePrevious 326 : [NSSet setWithObject: primitivePrevious]; 327 328 for (NSManagedObjectID *previousID in allPrevious) { 329 NSManagedObject *previous = 330 [_context objectWithID: previousID]; 331 332 // FIXME: should be using set mutation notifications for to 333 // many 334 [previous willChangeValueForKey: inverseName]; 335 336 if ([inverse isToMany]) 337 [[previous primitiveValueForKey: inverseName] 338 removeObject: [self objectID]]; 339 else 340 [previous setPrimitiveValue: nil forKey: inverseName]; 341 342 [previous didChangeValueForKey: inverseName]; 343 } 344 } 345 } 346 347 [self willChangeValueForKey: propertyName]; 348 [self setPrimitiveValue: valueByID forKey: propertyName]; 349 [self didChangeValueForKey: propertyName]; 350 351 if (inverse != nil) { 352 NSSet *allValues = [relationship isToMany] 353 ? valueByID 354 : [NSSet setWithObject: valueByID]; 355 356 for (NSManagedObjectID *valueID in allValues) { 357 NSManagedObject *value = [_context objectWithID: valueID]; 358 359 // FIXME: should be using set mutation notifications for to many 360 [value willChangeValueForKey: inverseName]; 361 362 if ([inverse isToMany]) { 363 NSMutableSet *set = 364 [value primitiveValueForKey: inverseName]; 365 366 if (set == nil) { 367 set = [NSMutableSet set]; 368 [value setPrimitiveValue: set forKey: inverseName]; 369 } 370 371 [set addObject: [self objectID]]; 372 } else { 373 [value setPrimitiveValue: [self objectID] 374 forKey: inverseName]; 375 } 376 377 [value didChangeValueForKey: inverseName]; 378 } 379 } 380 return; 381 } 382 383 [super setValue: value forKey: key]; 384 } 385 386 - (NSMutableSet *) mutableSetValueForKey: (NSString *) key { 387 return [[[NSManagedObjectMutableSet alloc] initWithManagedObject: self 388 key: key] 389 autorelease]; 390 } 391 392 - primitiveValueForKey: (NSString *) key { 393 id result = [_changedValues objectForKey: key]; 394 395 if (result == nil) 396 result = [[self _committedValues] objectForKey: key]; 397 398 return result; 399 } 400 401 - (void) setPrimitiveValue: value forKey: (NSString *) key { 402 if (value == nil) 403 [_changedValues removeObjectForKey: key]; 404 else { 405 [_changedValues setObject: value forKey: key]; 406 } 407 } 408 409 - (BOOL) validateValue: (id *) value 410 forKey: (NSString *) key 411 error: (NSError **) error 412 { 413 NSUnimplementedMethod(); 414 return YES; 415 } 416 417 - (BOOL) validateForDelete: (NSError **) error { 418 NSUnimplementedMethod(); 419 return YES; 420 } 421 422 - (BOOL) validateForInsert: (NSError **) error { 423 NSUnimplementedMethod(); 424 return YES; 425 } 426 427 - (BOOL) validateForUpdate: (NSError **) error { 428 NSUnimplementedMethod(); 429 return YES; 430 } 431 432 + (BOOL) automaticallyNotifiesObserversForKey: (NSString *) key { 433 // FIXME: Doc.s for NSManagedObject state this returns YES for unmodeled 434 // properties. 435 436 return NO; 437 } 438 439 - (void) didAccessValueForKey: (NSString *) key { 440 } 441 442 - (void *) observationInfo { 443 return _observationInfo; 444 } 445 446 - (void) setObservationInfo: (void *) value { 447 _observationInfo = value; 448 } 449 450 - (void) willAccessValueForKey: (NSString *) key { 451 } 452 453 - (NSString *) description { 454 NSMutableDictionary *values = [NSMutableDictionary 455 dictionaryWithDictionary: [self _committedValues]]; 456 457 [values addEntriesFromDictionary: _changedValues]; 458 459 return [NSString 460 stringWithFormat: @"<%@ %x:objectID=%@ entity name=%@, values=%@>", 461 [self class], self, _objectID, [self entity], 462 values]; 463 } 464 465 @end