/ CoreData / NSEntityDescription.m
NSEntityDescription.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 <CoreData/NSAttributeDescription.h>
 21  #import <CoreData/NSManagedObject.h>
 22  #import <CoreData/NSManagedObjectContext.h>
 23  #import <CoreData/NSManagedObjectModel.h>
 24  #import <CoreData/NSPersistentStoreCoordinator.h>
 25  #import <CoreData/NSPropertyDescription.h>
 26  #import <CoreData/NSRelationshipDescription.h>
 27  #import <Foundation/NSKeyedUnarchiver.h>
 28  #import <Foundation/NSRaise.h>
 29  #import <ctype.h>
 30  #import <objc/objc-class.h>
 31  #import <stdint.h>
 32  #import <string.h>
 33  
 34  @implementation NSEntityDescription
 35  
 36  static id getValue(id self, SEL selector) {
 37      NSPropertyDescription *property =
 38              [[self entity] _propertyForSelector: selector];
 39      id result = [self valueForKey: [property name]];
 40      return result;
 41  }
 42  
 43  static void setValue(id self, SEL selector, id newValue) {
 44      NSPropertyDescription *property =
 45              [[self entity] _propertyForSelector: selector];
 46      [self setValue: newValue forKey: [property name]];
 47  }
 48  
 49  static void addObject(id self, SEL selector, id value) {
 50      NSPropertyDescription *property =
 51              [[self entity] _propertyForSelector: selector];
 52      NSMutableSet *set = [self mutableSetValueForKey: [property name]];
 53  
 54      [set addObject: value];
 55  }
 56  
 57  static void removeObject(id self, SEL selector, id value) {
 58      NSPropertyDescription *property =
 59              [[self entity] _propertyForSelector: selector];
 60      NSMutableSet *set = [self mutableSetValueForKey: [property name]];
 61      [set removeObject: value];
 62  }
 63  
 64  static void addObjectSet(id self, SEL selector, NSSet *values) {
 65      NSPropertyDescription *property =
 66              [[self entity] _propertyForSelector: selector];
 67      NSMutableSet *set = [self mutableSetValueForKey: [property name]];
 68      [set unionSet: values];
 69  }
 70  
 71  static void removeObjectSet(id self, SEL selector, NSSet *values) {
 72      NSPropertyDescription *property =
 73              [[self entity] _propertyForSelector: selector];
 74      NSMutableSet *set = [self mutableSetValueForKey: [property name]];
 75      [set minusSet: values];
 76  }
 77  
 78  id keyObjectForSelector(SEL selector) {
 79      return [NSNumber numberWithInteger: (NSInteger) selector];
 80  }
 81  
 82  static void appendMethodToList(Class class, NSString *selectorName, IMP imp,
 83                                 const char *types, SEL *selectorp)
 84  {
 85  
 86      SEL selector = NSSelectorFromString(selectorName);
 87  
 88      class_addMethod(class, selector, imp, types);
 89  
 90      *selectorp = selector;
 91  }
 92  
 93  - initWithCoder: (NSCoder *) coder {
 94      if (![coder allowsKeyedCoding]) {
 95          [NSException raise: NSInvalidArgumentException
 96                      format: @"%@ can not initWithCoder:%@", [self class],
 97                              [coder class]];
 98          return nil;
 99      }
100  
101      _className = [[coder decodeObjectForKey: @"NSClassNameForEntity"] retain];
102      _name = [[coder decodeObjectForKey: @"NSEntityName"] retain];
103      _model = [coder decodeObjectForKey: @"NSManagedObjectModel"];
104      _properties = [[coder decodeObjectForKey: @"NSProperties"] retain];
105      _subentities = [[coder decodeObjectForKey: @"NSSubentities"] retain];
106      _superentity = [[coder decodeObjectForKey: @"NSSuperentity"] retain];
107      _userInfo = [[coder decodeObjectForKey: @"NSUserInfo"] retain];
108      _versionHashModifier =
109              [[coder decodeObjectForKey: @"NSVersionHashModifier"] retain];
110  
111      _selectorPropertyMap = [[NSMutableDictionary alloc] init];
112  
113      _hasBeenInstantiated = NO;
114  
115      if (_className) {
116          Class class = NSClassFromString(_className);
117  
118          for (NSPropertyDescription *property in [_properties allValues]) {
119              NSString *propertyName = [property name];
120              NSString *upperName = [[[propertyName substringToIndex: 1]
121                      uppercaseString]
122                      stringByAppendingString: [propertyName
123                                                       substringFromIndex: 1]];
124              NSString *selectorName;
125              SEL selector;
126  
127              appendMethodToList(class, propertyName, (IMP) getValue,
128                                 "@@:", &selector);
129              [_selectorPropertyMap setObject: property
130                                       forKey: keyObjectForSelector(selector)];
131  
132              appendMethodToList(
133                      class, [NSString stringWithFormat: @"set%@:", upperName],
134                      (IMP) setValue, "v@:@", &selector);
135              [_selectorPropertyMap setObject: property
136                                       forKey: keyObjectForSelector(selector)];
137  
138              if ([property isKindOfClass: [NSRelationshipDescription class]]) {
139                  NSRelationshipDescription *relationship =
140                          (NSRelationshipDescription *) property;
141  
142                  if ([relationship isToMany]) {
143                      appendMethodToList(
144                              class,
145                              [NSString stringWithFormat: @"add%@Object:",
146                                                          upperName],
147                              (IMP) addObject, "v@:@", &selector);
148                      [_selectorPropertyMap
149                              setObject: property
150                                 forKey: keyObjectForSelector(selector)];
151  
152                      appendMethodToList(
153                              class,
154                              [NSString stringWithFormat: @"remove%@Object:",
155                                                          upperName],
156                              (IMP) removeObject, "v@:@", &selector);
157                      [_selectorPropertyMap
158                              setObject: property
159                                 forKey: keyObjectForSelector(selector)];
160  
161                      appendMethodToList(
162                              class,
163                              [NSString stringWithFormat: @"add%@:", upperName],
164                              (IMP) addObjectSet, "v@:@", &selector);
165                      [_selectorPropertyMap
166                              setObject: property
167                                 forKey: keyObjectForSelector(selector)];
168  
169                      appendMethodToList(class,
170                                         [NSString stringWithFormat: @"remove%@:",
171                                                                     upperName],
172                                         (IMP) removeObjectSet, "v@:@",
173                                         &selector);
174                      [_selectorPropertyMap
175                              setObject: property
176                                 forKey: keyObjectForSelector(selector)];
177                  }
178              }
179          }
180      }
181      return self;
182  }
183  
184  - (NSString *) description {
185      return [NSString stringWithFormat: @"<NSEntityDescription %@>", _name];
186  }
187  
188  - (NSPropertyDescription *) _propertyForSelector: (SEL) selector {
189      id keyObject = keyObjectForSelector(selector);
190      NSEntityDescription *entity;
191  
192      for (entity = self; entity; entity = [entity superentity]) {
193          NSPropertyDescription *result =
194                  [entity->_selectorPropertyMap objectForKey: keyObject];
195  
196          if (result)
197              return result;
198      }
199      return nil;
200  }
201  
202  - (BOOL) _hasBeenInstantiated {
203      return _hasBeenInstantiated;
204  }
205  
206  /*
207  - (BOOL) _computeAttribute: (NSAttributeDescription *) attribute
208                            getter: (IMP *) getter
209                            setter: (IMP *) setter
210  {
211      switch([attribute attributeType]) {
212      case NSUndefinedAttributeType:
213          NSLog(@"Undefined-type attributes not implemented for %@.%@",
214                _className, [attribute name]);
215          return NO;
216  
217      case NSInteger16AttributeType:
218          NSLog(@"Integer 16 attributes not implemented for %@.%@",
219                _className, [attribute name]);
220          return NO;
221  
222      case NSInteger32AttributeType:
223          *getter = (IMP) getInt32;
224          *setter = (IMP) setInt32;
225          return [NSString stringWithUTF8String: @encode(int32_t)];
226  
227      case NSInteger64AttributeType:
228          NSLog(@"Integer 64 attributes not implemented for %@.%@",
229                _className, [attribute name]);
230          return NO;
231  
232      case NSDecimalAttributeType:
233          NSLog(@"Decimal attributes not implemented for %@.%@",
234                _className, [attribute name]);
235          return NO;
236  
237      case NSDoubleAttributeType:
238          NSLog(@"Double attributes not implemented for %@.%@",
239                _className, [attribute name]);
240          return NO;
241  
242      case NSFloatAttributeType:
243          NSLog(@"Float attributes not implemented for %@.%@",
244                _className, [attribute name]);
245          return NO;
246  
247      case NSStringAttributeType:
248          *getter = (IMP) getString;
249          *setter = (IMP) setString;
250          return [NSString stringWithUTF8String: @encode(NSString *)];
251  
252      case NSBooleanAttributeType:
253          *getter = (IMP) getBool;
254          *setter = (IMP) setBool;
255          return [NSString stringWithUTF8String: @encode(BOOL)];
256  
257      case NSDateAttributeType:
258          NSLog(@"Date attributes not implemented for %@.%@",
259                _className, [attribute name]);
260          return NO;
261  
262      case NSBinaryDataAttributeType:
263          NSLog(@"Binary data attributes not implemented for %@.%@",
264                _className, [attribute name]);
265          return NO;
266  
267      case NSTransformableAttributeType:
268          NSLog(@"Transformable attributes not implemented for %@.%@",
269                _className, [attribute name]);
270          return NO;
271  
272      default:
273          NSLog(@"Unknown attribute type for %@.%@",
274                _className, [attribute name]);
275          return NO;
276      }
277  }
278  */
279  
280  + (NSEntityDescription *) entityForName: (NSString *) entityName
281                   inManagedObjectContext: (NSManagedObjectContext *) context
282  {
283      NSDictionary *entities = [[[context persistentStoreCoordinator]
284              managedObjectModel] entitiesByName];
285  
286      return [entities objectForKey: entityName];
287  }
288  
289  + insertNewObjectForEntityForName: (NSString *) entityName
290             inManagedObjectContext: (NSManagedObjectContext *) context
291  {
292      NSEntityDescription *entity = [self entityForName: entityName
293                                 inManagedObjectContext: context];
294      NSString *className = [entity managedObjectClassName];
295      Class class;
296  
297      if (className)
298          class = NSClassFromString(className);
299      else {
300          NSLog(@"Unable to find class %@ specified by entity %@ in the runtime",
301                className, [entity name]);
302  
303          class = [NSManagedObject class];
304      }
305  
306      return [[class alloc] initWithEntity: entity
307              insertIntoManagedObjectContext: context];
308  }
309  
310  - (NSManagedObjectModel *) managedObjectModel {
311      return _model;
312  }
313  
314  - (NSString *) name {
315      return _name;
316  }
317  
318  - (BOOL) isAbstract {
319      return _isAbstract;
320  }
321  
322  - (NSString *) managedObjectClassName {
323      // should probably init _className like this but initWithCoder logic needs
324      // to be changed
325      if (_className == nil)
326          return @"NSManagedObject";
327  
328      return _className;
329  }
330  
331  - (NSArray *) properties {
332      return [_properties allValues];
333  }
334  
335  - (NSArray *) subentities {
336      return [_subentities allValues];
337  }
338  
339  - (NSDictionary *) userInfo {
340      return _userInfo;
341  }
342  
343  - (void) setName: (NSString *) value {
344      if (_hasBeenInstantiated) {
345          NSLog(@"Attempt to modify entity after instantiating it.");
346          return;
347      }
348  
349      value = [value copy];
350      [_name release];
351      _name = value;
352  }
353  
354  - (void) setAbstract: (BOOL) value {
355      if (_hasBeenInstantiated) {
356          NSLog(@"Attempt to modify entity after instantiating it.");
357          return;
358      }
359  
360      _isAbstract = value;
361  }
362  
363  - (void) setManagedObjectClassName: (NSString *) value {
364      if (_hasBeenInstantiated) {
365          NSLog(@"Attempt to modify entity after instantiating it.");
366          return;
367      }
368  
369      value = [value copy];
370      [_className release];
371      _className = value;
372  }
373  
374  - (void) setProperties: (NSArray *) value {
375      if (_hasBeenInstantiated) {
376          NSLog(@"Attempt to modify entity after instantiating it.");
377          return;
378      }
379  
380      NSUnimplementedMethod();
381  }
382  
383  - (void) setSubentities: (NSArray *) value {
384      if (_hasBeenInstantiated) {
385          NSLog(@"Attempt to modify entity after instantiating it.");
386          return;
387      }
388  
389      NSUnimplementedMethod();
390  }
391  
392  - (void) setUserInfo: (NSDictionary *) value {
393      if (_hasBeenInstantiated) {
394          NSLog(@"Attempt to modify entity after instantiating it.");
395          return;
396      }
397  
398      value = [value copy];
399      [_userInfo release];
400      _userInfo = value;
401  }
402  
403  - (NSEntityDescription *) superentity {
404      return _superentity;
405  }
406  
407  - (NSDictionary *) subentitiesByName {
408      NSUnimplementedMethod();
409      return nil;
410  }
411  
412  - (NSDictionary *) attributesByName {
413      NSMutableDictionary *result = [NSMutableDictionary dictionary];
414  
415      for (NSPropertyDescription *check in [_properties allValues]) {
416          if ([check isKindOfClass: [NSAttributeDescription class]]) {
417              [result setObject: check forKey: [check name]];
418          }
419      }
420  
421      return result;
422  }
423  
424  - (NSDictionary *) propertiesByName {
425      return _properties;
426  }
427  
428  - (NSDictionary *) relationshipsByName {
429      NSMutableDictionary *result = [NSMutableDictionary dictionary];
430  
431      for (NSPropertyDescription *check in [_properties allValues]) {
432          if ([check isKindOfClass: [NSRelationshipDescription class]])
433              [result setObject: check forKey: [check name]];
434      }
435  
436      return result;
437  }
438  
439  - (NSArray *) relationshipsWithDestinationEntity: (NSEntityDescription *) entity
440  {
441      NSMutableArray *result = [NSMutableArray array];
442  
443      for (NSPropertyDescription *check in [_properties allValues]) {
444          if ([check isKindOfClass: [NSRelationshipDescription class]]) {
445              if ([[check entity] isEqual: entity])
446                  [result addObject: check];
447          }
448      }
449  
450      return result;
451  }
452  
453  - (BOOL) _isKindOfEntity: (NSEntityDescription *) other {
454      NSEntityDescription *check = self;
455  
456      for (; check != nil; check = check->_superentity)
457          if (check == other)
458              return YES;
459  
460      return NO;
461  }
462  
463  @end