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
33static 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
void setPredicate:(NSPredicate *aPredicate)
Sets the predicate of the receiver.
NSPredicate * predicate()
Returns the predicate of the receiver.