GNUstep Core Data  0.1
NSManagedObjectModel.m
1 /* Implementation of the NSManagedObjectModel class for the GNUstep
2  Core Data framework.
3  Copyright (C) 2005 Free Software Foundation, Inc.
4 
5  Written by: Saso Kiselkov <diablos@manga.sk>
6  Date: August 2005
7 
8  This file is part of the GNUstep Core Data framework.
9 
10  This library is free software; you can redistribute it and/or
11  modify it under the terms of the GNU Lesser General Public
12  License as published by the Free Software Foundation; either
13  version 2.1 of the License, or (at your option) any later version.
14 
15  This library is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18  Lesser General Public License for more details.
19 
20  You should have received a copy of the GNU Lesser General Public
21  License along with this library; if not, write to the Free
22  Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111 USA.
23  */
24 
25 #import "CoreDataHeaders.h"
26 
33 static void EnsureEntitiesHaveProperNames(NSArray * entities)
34 {
35  NSMutableSet * knownNames;
36  NSEnumerator * e;
37  NSEntityDescription * entity;
38 
39  knownNames = [NSMutableSet setWithCapacity: [entities count]];
40  e = [entities objectEnumerator];
41  while ((entity = [e nextObject]) != nil)
42  {
43  NSString * entityName = [entity name];
44 
45  if (entityName == nil)
46  {
47  [NSException raise: NSInvalidArgumentException
48  format: _(@"Tried to add an entity without a name "
49  @"to a managed object model.")];
50  }
51  if ([knownNames containsObject: entityName])
52  {
53  [NSException raise: NSInvalidArgumentException
54  format: _(@"Tried to add several entities with the "
55  @"same name to a managed object model.")];
56  }
57  [knownNames addObject: entityName];
58  }
59 }
60 
61 @interface NSManagedObjectModel (GSCoreDataInternal)
62 
67 - (void) _grabEntities: (NSArray *) entities;
68 
70 - (void) _ungrabEntities: (NSArray *) entities;
71 
77 - (void) _ensureEditableWithReason: (NSString *) reason;
78 
79 @end
80 
81 @implementation NSManagedObjectModel (GSCoreDataInternal)
82 
83 - (void) _grabEntities: (NSArray *) entities
84 {
85  NSEnumerator * e;
86  NSEntityDescription * ent;
87 
88  e = [entities objectEnumerator];
89  while ((ent = [e nextObject]) != nil)
90  {
91  if ([ent managedObjectModel] != nil && [ent managedObjectModel] != self)
92  {
93  [NSException raise: NSInvalidArgumentException
94  format: _(@"Passed an entity to an object model already "
95  @"in use by some other model")];
96  }
97  [ent _addReferenceToManagedObjectModel: self];
98  }
99 }
100 
101 - (void) _ungrabEntities: (NSArray *) entities
102 {
103  NSEnumerator * e;
104  NSEntityDescription * ent;
105 
106  e = [entities objectEnumerator];
107  while ((ent = [e nextObject]) != nil)
108  {
109  [ent _removeReferenceToManagedObjectModel: self];
110  }
111 }
112 
113 - (void) _ensureEditableWithReason: (NSString *) reason
114 {
115  if (_usedByPersistentStoreCoordinators)
116  {
117  // which exception to raise??
118  [NSException raise: NSGenericException format: _(reason)];
119  }
120 }
121 
122 @end
123 
124 @implementation NSManagedObjectModel
125 
126 - (void) dealloc
127 {
128  NSEnumerator * e;
129  NSArray * entities;
130 
131  // ungrab the entities before we disappear
132  [self _ungrabEntities: _entities];
133  TEST_RELEASE(_entities);
134 
135  e = [_configurations objectEnumerator];
136  while ((entities = [e nextObject]) != nil)
137  {
138  [self _ungrabEntities: entities];
139  }
140  TEST_RELEASE(_configurations);
141 
142  TEST_RELEASE(_fetchRequests);
143 
144  [super dealloc];
145 }
146 
147 + (NSManagedObjectModel *) modelByMergingModels: (NSArray *) models
148 {
149  NSManagedObjectModel * newModel;
150 
151  NSMutableArray * entities;
152  NSMutableDictionary * confs;
153  NSMutableDictionary * fetchRequests;
154 
155  NSEnumerator * e;
156  NSManagedObjectModel * model;
157 
158  NSString * confName;
159  NSString * fetchRequestName;
160 
161  newModel = [[NSManagedObjectModel new] autorelease];
162 
163  entities = [NSMutableArray array];
164  confs = [NSMutableDictionary dictionary];
165  fetchRequests = [NSMutableDictionary dictionary];
166 
167  // copy and merge all the contents from all models
168  e = [models objectEnumerator];
169  while ((model = [e nextObject]) != nil)
170  {
171  [entities addObjectsFromArray: [[[NSArray alloc]
172  initWithArray: [model entities] copyItems: YES] autorelease]];
173 
174  [confs addEntriesFromDictionary: [[[NSDictionary alloc]
175  initWithDictionary: [model _configurationsByName] copyItems: YES]
176  autorelease]];
177 
178  // fetch requests can be shared
179  [fetchRequests addEntriesFromDictionary: [model fetchRequestsByName]];
180  }
181 
182  // and set the merged contents into the new model
183  [newModel setEntities: entities];
184 
185  e = [[confs allKeys] objectEnumerator];
186  while ((confName = [e nextObject]) != nil)
187  {
188  [newModel setEntities: [confs objectForKey: confName]
189  forConfiguration: confName];
190  }
191 
192  e = [[fetchRequests allKeys] objectEnumerator];
193  while ((fetchRequestName = [e nextObject]) != nil)
194  {
195  [newModel setFetchRequestTemplate: [fetchRequests objectForKey:
196  fetchRequestName]
197  forName: fetchRequestName];
198  }
199 
200  return newModel;
201 }
202 
203 + (NSManagedObjectModel *) mergedModelFromBundles: (NSArray *) bundles
204 {
205  NSArray * modelPaths;
206  NSMutableArray * models;
207 
208  NSEnumerator * e;
209  NSString * modelPath;
210 
211  // find the involved .gsdatamodel files
212  if (bundles != nil)
213  // search specified bundles
214  {
215  NSEnumerator * e;
216  NSBundle * bundle;
217  NSMutableArray * array;
218 
219  array = [NSMutableArray array];
220 
221  e = [bundles objectEnumerator];
222  while ((bundle = [e nextObject]) != nil)
223  {
224  [array addObjectsFromArray:
225  [bundle pathsForResourcesOfType: @"gsdatamodel" inDirectory: nil]];
226  }
227 
228  modelPaths = array;
229  }
230  else
231  // search the main bundle
232  {
233  modelPaths = [[NSBundle mainBundle]
234  pathsForResourcesOfType: @"gsdatamodel" inDirectory: nil];
235  }
236 
237 
238  // initialize the models from them
239  models = [NSMutableArray arrayWithCapacity: [modelPaths count]];
240  e = [modelPaths objectEnumerator];
241  while ((modelPath = [e nextObject]) != nil)
242  {
243  [models addObject: [[[NSManagedObjectModel alloc]
244  initWithContentsOfFile: modelPath]
245  autorelease]];
246  }
247 
248  // and return the merged result
249  return [self modelByMergingModels: models];
250 }
251 
252 - (id) initWithContentsOfURL: (NSURL *) url
253 {
254  NSData * data;
255 
256  // release the old instance - we'll return a new one
257  [self release];
258 
259  if ((data = [NSData dataWithContentsOfURL: url]) == nil)
260  {
261  NSLog(_(@"Failed to access managed object model archive at: %@"),
262  [url description]);
263 
264  return nil;
265  }
266 
267  return [NSKeyedUnarchiver unarchiveObjectWithData: data];
268 }
269 
270 - (id) _initWithContentsOfFile: (NSString *) file
271 {
272  return [self initWithContentsOfURL: [NSURL fileURLWithPath: file]];
273 }
274 
275 - (id) init
276 {
277  if ((self = [super init]))
278  {
279  _configurations = [NSMutableDictionary new];
280  _fetchRequests = [NSMutableDictionary new];
281  }
282  return self;
283 }
284 
285 - (NSArray *) entities
286 {
287  return _entities;
288 }
289 
290 - (NSDictionary *) entitiesByName
291 {
292  NSMutableDictionary * dict = [NSMutableDictionary
293  dictionaryWithCapacity: [_entities count]];
294  NSEnumerator * e = [_entities objectEnumerator];
295  NSEntityDescription * entity;
296 
297  while ((entity = [e nextObject]) != nil)
298  {
299  [dict setObject: entity forKey: [entity name]];
300  }
301 
302  return [[dict copy] autorelease];
303 }
304 
305 - (void) setEntities: (NSArray *) someEntities
306 {
307  [self _ensureEditableWithReason: @"Tried to set entities of a "
308  @"managed object model already in use by an object graph manager."];
309  EnsureEntitiesHaveProperNames(someEntities);
310 
311  if (_entities != nil)
312  {
313  [self _ungrabEntities: _entities];
314  DESTROY(_entities);
315  }
316  if (someEntities != nil)
317  {
318  _entities = [someEntities copy];
319  [self _grabEntities: _entities];
320  }
321 }
322 
323 - (NSArray *) configurations
324 {
325  return [_configurations allKeys];
326 }
327 
328 - (NSArray *) entitiesForConfiguration: (NSString *) conf
329 {
330  return [_configurations objectForKey: conf];
331 }
332 
333 - (void) setEntities: (NSArray *) entities
334  forConfiguration: (NSString *) conf
335 {
336  NSArray * oldEntities;
337 
338  [self _ensureEditableWithReason: @"Tried to set entities "
339  @"for a configuration of a managed object model already in use "
340  @"by an object graph manager."];
341  EnsureEntitiesHaveProperNames(entities);
342 
343  oldEntities = [_configurations objectForKey: conf];
344  if (oldEntities != nil)
345  {
346  [self _ungrabEntities: oldEntities];
347  [_configurations removeObjectForKey: conf];
348  }
349  if (entities != nil)
350  {
351  [_configurations setObject: [[entities copy] autorelease]
352  forKey: conf];
353  [self _grabEntities: entities];
354  }
355 }
356 
357 - (NSDictionary *) _configurationsByName
358 {
359  return [[_configurations copy] autorelease];
360 }
361 
362 - (NSFetchRequest *) fetchRequestTemplateForName: (NSString *) aName
363 {
364  return [_fetchRequests objectForKey: aName];
365 }
366 
367 - (NSFetchRequest *) fetchRequestFromTemplateWithName: (NSString *) name
368  substitutionVariables: (NSDictionary *) vars
369 {
370  NSFetchRequest * req, * template;
371 
372  template = [_fetchRequests objectForKey: name];
373  if (template == nil)
374  {
375  return nil;
376  }
377 
378  req = [[template copy] autorelease];
379  if ([req predicate] != nil)
380  {
381  [req setPredicate: [[req predicate]
382  predicateWithSubstitutionVariables: vars]];
383  }
384 
385  return req;
386 }
387 
388 - (void) setFetchRequestTemplate: (NSFetchRequest *) request
389  forName: (NSString *) name
390 {
391  if (_usedByPersistentStoreCoordinators)
392  {
393  [NSException raise: NSGenericException
394  format: _(@"Tried to set a fetch request template "
395  @"for a managed object model already in use "
396  @"by an object graph manager.")];
397  }
398 
399  // N.B. is this the way it should behave?
400  if (request != nil)
401  {
402  [_fetchRequests setObject: request forKey: name];
403  }
404  else
405  {
406  [_fetchRequests removeObjectForKey: name];
407  }
408 }
409 
410 - (void) _removeFetchRequestTemplateForName: (NSString *) name
411 {
412  if (_usedByPersistentStoreCoordinators)
413  {
414  [NSException raise: NSGenericException
415  format: _(@"Tried to remove a fetch request template "
416  @"from a managed object model already in use "
417  @"by an object graph manager.")];
418  }
419 
420  [_fetchRequests removeObjectForKey: name];
421 }
422 
423 - (NSDictionary *) fetchRequestsByName
424 {
425  return [[_fetchRequests copy] autorelease];
426 }
427 
428 - (NSDictionary *) localizationDictionary
429 {
430  // FIXME: what is this supposed to do ???
431 
432  return nil;
433 }
434 
435 - (void) setLocalizationDictionary: (NSDictionary *) dict
436 {
437  // FIXME: what is this supposed to do ???
438 }
439 
440 - (BOOL) _isEditable
441 {
442  return (_usedByPersistentStoreCoordinators == 0);
443 }
444 
445 // NSCoding
446 
447 - (id) initWithCoder: (NSCoder *) coder
448 {
449  if ((self = [super init]))
450  {
451  if ([coder allowsKeyedCoding])
452  {
453  ASSIGN(_entities, [coder decodeObjectForKey: @"Entities"]);
454  ASSIGN(_configurations, [coder decodeObjectForKey:
455  @"Configurations"]);
456  ASSIGN(_fetchRequests, [coder decodeObjectForKey: @"FetchRequests"]);
457  }
458  else
459  {
460  ASSIGN(_entities, [coder decodeObject]);
461  ASSIGN(_configurations, [coder decodeObject]);
462  ASSIGN(_fetchRequests, [coder decodeObject]);
463  }
464  }
465  return self;
466 }
467 
468 - (void) encodeWithCoder: (NSCoder *) coder
469 {
470  if ([coder allowsKeyedCoding])
471  {
472  [coder encodeObject: _entities forKey: @"Entities"];
473  [coder encodeObject: _configurations forKey: @"Configurations"];
474  [coder encodeObject: _fetchRequests forKey: @"FetchRequests"];
475  }
476  else
477  {
478  [coder encodeObject: _entities];
479  [coder encodeObject: _configurations];
480  [coder encodeObject: _fetchRequests];
481  }
482 }
483 
484 // NSCopying
485 
486 - (id) copyWithZone: (NSZone *) zone
487 {
488  NSManagedObjectModel * model;
489 
490  NSEnumerator * e;
491  NSString * conf;
492  NSString * fetchRequestName;
493 
494  model = [[NSManagedObjectModel allocWithZone: zone] init];
495 
496  // We must copy entities and configurations themselves too - they are
497  // not shareable between several models. (FIXME: is this true? Apple
498  // spec doesn't say a word about this - I just *guessed* it)
499  [model setEntities: [[[NSArray alloc]
500  initWithArray: _entities copyItems: YES]
501  autorelease]];
502 
503  e = [[_configurations allKeys] objectEnumerator];
504  while ((conf = [e nextObject]) != nil)
505  {
506  [model setEntities: [[[NSArray alloc]
507  initWithArray: [_configurations objectForKey: conf]
508  copyItems: YES]
509  autorelease]
510  forConfiguration: conf];
511  }
512 
513  // fetch requests appear to be shareable, so just set them
514  e = [[_fetchRequests allKeys] objectEnumerator];
515  while ((fetchRequestName = [e nextObject]) != nil)
516  {
517  [model setFetchRequestTemplate: [_fetchRequests objectForKey:
518  fetchRequestName]
519  forName: fetchRequestName];
520  }
521 
522  return model;
523 }
524 
529 - (void) _incrementUseCount
530 {
531  _usedByPersistentStoreCoordinators++;
532 }
533 
539 - (void) _decrementUseCount
540 {
541  NSAssert(_usedByPersistentStoreCoordinators > 0,
542  _(@"Tried to underflow managed object model use count."));
543 
544  _usedByPersistentStoreCoordinators--;
545 }
546 
547 @end
An object for storing details about managed object fetches.
void setPredicate:(NSPredicate *aPredicate)
Sets the predicate of the receiver.
NSPredicate * predicate()
Returns the predicate of the receiver.