1 /* Copyright (c) 2008 Dan Knapp
3 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
5 The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
7 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
8 #import <CoreData/NSManagedObject.h>
9 #import "NSManagedObjectID-Private.h"
10 #import "NSManagedObjectContext-Private.h"
11 #import "NSEntityDescription-Private.h"
12 #import <CoreData/NSAttributeDescription.h>
13 #import <CoreData/NSRelationshipDescription.h>
14 #import <CoreData/NSAtomicStoreCacheNode.h>
15 #import <Foundation/NSRaise.h>
16 #import "NSManagedObjectSet.h"
17 #import "NSManagedObjectSetEnumerator.h"
18 #import "NSManagedObjectMutableSet.h"
20 @implementation NSManagedObject
23 NSLog(@"Error - can't initialize an NSManagedObject with -init");
27 -initWithObjectID:(NSManagedObjectID *)objectID managedObjectContext:(NSManagedObjectContext *)context {
28 NSEntityDescription *entity=[objectID entity];
29 NSString *className=[entity managedObjectClassName];
30 Class class=NSClassFromString(className);
33 NSLog(@"Unable to find class %@ specified by entity %@ in the runtime, using NSManagedObject,objectID=%@",className,[entity name],objectID);
35 class=[NSManagedObject class];
39 self=[class allocWithZone:NULL];
41 _objectID=[objectID copy];
43 _committedValues = nil;
44 _changedValues = [[NSMutableDictionary alloc] init];
49 -initWithEntity:(NSEntityDescription *)entity insertIntoManagedObjectContext:(NSManagedObjectContext *)context {
50 NSManagedObjectID *objectID=[[[NSManagedObjectID alloc] initWithEntity:entity] autorelease];
52 if((self=[self initWithObjectID:objectID managedObjectContext:context])==nil)
55 NSDictionary *attributes=[entity attributesByName];
56 NSEnumerator *state=[attributes keyEnumerator];
59 while((key=[state nextObject])!=nil){
60 id object=[attributes objectForKey:key];
61 id value=[object defaultValue];
64 [self setPrimitiveValue:value forKey:key];
67 [context insertObject:self];
73 [self didTurnIntoFault];
76 [_changedValues release];
81 return [_objectID hash];
84 -(BOOL)isEqual:otherX {
85 if(![otherX isKindOfClass:[NSManagedObject class]])
88 NSManagedObject *other=otherX;
90 return [_objectID isEqual:other->_objectID];
93 -(NSEntityDescription *)entity {
94 return [_objectID entity];
97 -(NSManagedObjectID *)objectID {
105 -(NSManagedObjectContext *)managedObjectContext {
125 - (BOOL) hasFaultForRelationshipNamed:(NSString *) key {
126 NSUnimplementedMethod();
130 - (void) awakeFromFetch {
131 NSUnimplementedMethod();
134 - (void) awakeFromInsert {
135 NSUnimplementedMethod();
138 -(NSDictionary *)changedValues {
139 return _changedValues;
142 -(NSDictionary *)_committedValues {
144 if([[self objectID] isTemporaryID])
147 if(_committedValues==nil){
148 NSAtomicStoreCacheNode *node=[_context _cacheNodeForObjectID:[self objectID]];
149 NSDictionary *propertyCache=[node propertyCache];
150 NSMutableDictionary *storedValues=[[NSMutableDictionary alloc] init];
152 NSArray *properties=[[self entity] properties];
154 for(NSPropertyDescription *property in properties){
155 NSString *name=[property name];
157 if([property isKindOfClass:[NSAttributeDescription class]]){
158 id value=[propertyCache objectForKey:name];
161 [storedValues setObject:value forKey:name];
164 else if([property isKindOfClass:[NSRelationshipDescription class]]){
165 NSRelationshipDescription *relationship=(NSRelationshipDescription *)property;
166 id indirectValue=[propertyCache objectForKey:name];
168 if(indirectValue!=nil){
171 if(![relationship isToMany])
172 value=[indirectValue objectID];
174 value=[NSMutableSet set];
176 for(NSAtomicStoreCacheNode *relNode in indirectValue){
177 [value addObject:[relNode objectID]];
181 [storedValues setObject:value forKey:name];
186 [_committedValues release];
187 _committedValues=storedValues;
189 return _committedValues;
192 -(NSDictionary *)committedValuesForKeys:(NSArray *)keys {
194 return [self _committedValues];
196 NSMutableDictionary *result=[NSMutableDictionary dictionary];
198 for(NSString *key in keys){
199 id object=[[self _committedValues] objectForKey:key];
202 [result setObject:object forKey:key];
213 - (void) willTurnIntoFault {
214 // do nothing per doc.s
217 - (void) didTurnIntoFault {
225 -valueForKey:(NSString *)key {
227 return [self valueForUndefinedKey:nil];
229 NSPropertyDescription *property=[[self entity] _propertyForSelector:NSSelectorFromString(key)];
230 NSString *propertyName=[property name];
232 if([property isKindOfClass:[NSAttributeDescription class]]){
233 [self willAccessValueForKey:propertyName];
235 id result=[self primitiveValueForKey:propertyName];
237 [self didAccessValueForKey:propertyName];
241 else if([property isKindOfClass:[NSRelationshipDescription class]]){
242 NSRelationshipDescription *relationship=(NSRelationshipDescription *)property;
244 [self willAccessValueForKey:propertyName];
246 id result=[self primitiveValueForKey:propertyName];
249 if([relationship isToMany])
250 result=[[[NSManagedObjectSet alloc] initWithManagedObjectContext:_context set:result] autorelease];
252 result=[_context objectWithID:result];
255 [self didAccessValueForKey:propertyName];
260 return [super valueForKey:key];
264 -(void)setValue:value forKey:(NSString *) key {
265 NSPropertyDescription *property= [[self entity] _propertyForSelector:NSSelectorFromString(key)];
266 NSString *propertyName=[property name];
268 if([property isKindOfClass:[NSAttributeDescription class]]){
269 [self willChangeValueForKey:propertyName];
270 [self setPrimitiveValue:value forKey:propertyName];
271 [self didChangeValueForKey:propertyName];
274 else if([property isKindOfClass:[NSRelationshipDescription class]]){
275 NSRelationshipDescription *relationship=(NSRelationshipDescription *)property;
276 NSRelationshipDescription *inverse=[relationship inverseRelationship];
277 NSString *inverseName=[inverse name];
280 if([relationship isToMany]){
281 NSMutableSet *set=[NSMutableSet set];
283 for(NSManagedObject *object in value)
284 [set addObject:[object objectID]];
289 valueByID=[value objectID];
293 id primitivePrevious=[self primitiveValueForKey:key];
295 if(primitivePrevious!=nil){
296 NSSet *allPrevious=[relationship isToMany]?primitivePrevious:[NSSet setWithObject:primitivePrevious];
298 for(NSManagedObjectID *previousID in allPrevious){
299 NSManagedObject *previous=[_context objectWithID:previousID];
301 // FIXME: should be using set mutation notifications for to many
302 [previous willChangeValueForKey:inverseName];
304 if([inverse isToMany])
305 [[previous primitiveValueForKey:inverseName] removeObject:[self objectID]];
307 [previous setPrimitiveValue:nil forKey:inverseName];
309 [previous didChangeValueForKey:inverseName];
314 [self willChangeValueForKey:propertyName];
315 [self setPrimitiveValue:valueByID forKey:propertyName];
316 [self didChangeValueForKey:propertyName];
319 NSSet *allValues=[relationship isToMany]?valueByID:[NSSet setWithObject:valueByID];
321 for(NSManagedObjectID *valueID in allValues){
322 NSManagedObject *value=[_context objectWithID:valueID];
324 // FIXME: should be using set mutation notifications for to many
325 [value willChangeValueForKey:inverseName];
327 if([inverse isToMany]){
328 NSMutableSet *set=[value primitiveValueForKey:inverseName];
331 set=[NSMutableSet set];
332 [value setPrimitiveValue:set forKey:inverseName];
335 [set addObject:[self objectID]];
338 [value setPrimitiveValue:[self objectID] forKey:inverseName];
341 [value didChangeValueForKey:inverseName];
347 [super setValue:value forKey:key];
350 -(NSMutableSet *) mutableSetValueForKey:(NSString *) key {
351 return [[[NSManagedObjectMutableSet alloc] initWithManagedObject:self key:key] autorelease];
354 -primitiveValueForKey:(NSString *) key {
355 id result=[_changedValues objectForKey:key];
358 result=[[self _committedValues] objectForKey:key];
363 -(void)setPrimitiveValue:value forKey:(NSString *)key {
365 [_changedValues removeObjectForKey:key];
367 [_changedValues setObject:value forKey:key];
371 - (BOOL) validateValue:(id *) value forKey:(NSString *) key error:(NSError **) error {
372 NSUnimplementedMethod();
377 - (BOOL) validateForDelete:(NSError **) error {
378 NSUnimplementedMethod();
383 - (BOOL) validateForInsert:(NSError **) error {
384 NSUnimplementedMethod();
389 - (BOOL) validateForUpdate:(NSError **) error {
390 NSUnimplementedMethod();
395 +(BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
396 // FIXME: Doc.s for NSManagedObject state this returns YES for unmodeled properties.
401 -(void)didAccessValueForKey:(NSString *) key {
404 -(void *)observationInfo {
405 return _observationInfo;
409 -(void)setObservationInfo:(void *) value {
410 _observationInfo=value;
413 -(void)willAccessValueForKey:(NSString *)key {
416 -(NSString *)description {
417 NSMutableDictionary *values=[NSMutableDictionary dictionaryWithDictionary:[self _committedValues]];
419 [values addEntriesFromDictionary:_changedValues];
421 return [NSString stringWithFormat:@"<%@ %x:objectID=%@ entity name=%@, values=%@>",isa,self,_objectID,[self entity],values];