GNUstep Core Data
0.1
|
00001 /* Implementation of the NSManagedObjectModel class for the GNUstep 00002 Core Data framework. 00003 Copyright (C) 2005 Free Software Foundation, Inc. 00004 00005 Written by: Saso Kiselkov <diablos@manga.sk> 00006 Date: August 2005 00007 00008 This file is part of the GNUstep Core Data framework. 00009 00010 This library is free software; you can redistribute it and/or 00011 modify it under the terms of the GNU Lesser General Public 00012 License as published by the Free Software Foundation; either 00013 version 2.1 of the License, or (at your option) any later version. 00014 00015 This library is distributed in the hope that it will be useful, 00016 but WITHOUT ANY WARRANTY; without even the implied warranty of 00017 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00018 Lesser General Public License for more details. 00019 00020 You should have received a copy of the GNU Lesser General Public 00021 License along with this library; if not, write to the Free 00022 Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111 USA. 00023 */ 00024 00025 #import "CoreDataHeaders.h" 00026 00033 static void EnsureEntitiesHaveProperNames(NSArray * entities) 00034 { 00035 NSMutableSet * knownNames; 00036 NSEnumerator * e; 00037 NSEntityDescription * entity; 00038 00039 knownNames = [NSMutableSet setWithCapacity: [entities count]]; 00040 e = [entities objectEnumerator]; 00041 while ((entity = [e nextObject]) != nil) 00042 { 00043 NSString * entityName = [entity name]; 00044 00045 if (entityName == nil) 00046 { 00047 [NSException raise: NSInvalidArgumentException 00048 format: _(@"Tried to add an entity without a name " 00049 @"to a managed object model.")]; 00050 } 00051 if ([knownNames containsObject: entityName]) 00052 { 00053 [NSException raise: NSInvalidArgumentException 00054 format: _(@"Tried to add several entities with the " 00055 @"same name to a managed object model.")]; 00056 } 00057 [knownNames addObject: entityName]; 00058 } 00059 } 00060 00061 @interface NSManagedObjectModel (GSCoreDataInternal) 00062 00067 - (void) _grabEntities: (NSArray *) entities; 00068 00070 - (void) _ungrabEntities: (NSArray *) entities; 00071 00077 - (void) _ensureEditableWithReason: (NSString *) reason; 00078 00079 @end 00080 00081 @implementation NSManagedObjectModel (GSCoreDataInternal) 00082 00083 - (void) _grabEntities: (NSArray *) entities 00084 { 00085 NSEnumerator * e; 00086 NSEntityDescription * ent; 00087 00088 e = [entities objectEnumerator]; 00089 while ((ent = [e nextObject]) != nil) 00090 { 00091 if ([ent managedObjectModel] != nil && [ent managedObjectModel] != self) 00092 { 00093 [NSException raise: NSInvalidArgumentException 00094 format: _(@"Passed an entity to an object model already " 00095 @"in use by some other model")]; 00096 } 00097 [ent _addReferenceToManagedObjectModel: self]; 00098 } 00099 } 00100 00101 - (void) _ungrabEntities: (NSArray *) entities 00102 { 00103 NSEnumerator * e; 00104 NSEntityDescription * ent; 00105 00106 e = [entities objectEnumerator]; 00107 while ((ent = [e nextObject]) != nil) 00108 { 00109 [ent _removeReferenceToManagedObjectModel: self]; 00110 } 00111 } 00112 00113 - (void) _ensureEditableWithReason: (NSString *) reason 00114 { 00115 if (_usedByPersistentStoreCoordinators) 00116 { 00117 // which exception to raise?? 00118 [NSException raise: NSGenericException format: _(reason)]; 00119 } 00120 } 00121 00122 @end 00123 00124 @implementation NSManagedObjectModel 00125 00126 - (void) dealloc 00127 { 00128 NSEnumerator * e; 00129 NSArray * entities; 00130 00131 // ungrab the entities before we disappear 00132 [self _ungrabEntities: _entities]; 00133 TEST_RELEASE(_entities); 00134 00135 e = [_configurations objectEnumerator]; 00136 while ((entities = [e nextObject]) != nil) 00137 { 00138 [self _ungrabEntities: entities]; 00139 } 00140 TEST_RELEASE(_configurations); 00141 00142 TEST_RELEASE(_fetchRequests); 00143 00144 [super dealloc]; 00145 } 00146 00147 + (NSManagedObjectModel *) modelByMergingModels: (NSArray *) models 00148 { 00149 NSManagedObjectModel * newModel; 00150 00151 NSMutableArray * entities; 00152 NSMutableDictionary * confs; 00153 NSMutableDictionary * fetchRequests; 00154 00155 NSEnumerator * e; 00156 NSManagedObjectModel * model; 00157 00158 NSString * confName; 00159 NSString * fetchRequestName; 00160 00161 newModel = [[NSManagedObjectModel new] autorelease]; 00162 00163 entities = [NSMutableArray array]; 00164 confs = [NSMutableDictionary dictionary]; 00165 fetchRequests = [NSMutableDictionary dictionary]; 00166 00167 // copy and merge all the contents from all models 00168 e = [models objectEnumerator]; 00169 while ((model = [e nextObject]) != nil) 00170 { 00171 [entities addObjectsFromArray: [[[NSArray alloc] 00172 initWithArray: [model entities] copyItems: YES] autorelease]]; 00173 00174 [confs addEntriesFromDictionary: [[[NSDictionary alloc] 00175 initWithDictionary: [model _configurationsByName] copyItems: YES] 00176 autorelease]]; 00177 00178 // fetch requests can be shared 00179 [fetchRequests addEntriesFromDictionary: [model fetchRequestsByName]]; 00180 } 00181 00182 // and set the merged contents into the new model 00183 [newModel setEntities: entities]; 00184 00185 e = [[confs allKeys] objectEnumerator]; 00186 while ((confName = [e nextObject]) != nil) 00187 { 00188 [newModel setEntities: [confs objectForKey: confName] 00189 forConfiguration: confName]; 00190 } 00191 00192 e = [[fetchRequests allKeys] objectEnumerator]; 00193 while ((fetchRequestName = [e nextObject]) != nil) 00194 { 00195 [newModel setFetchRequestTemplate: [fetchRequests objectForKey: 00196 fetchRequestName] 00197 forName: fetchRequestName]; 00198 } 00199 00200 return newModel; 00201 } 00202 00203 + (NSManagedObjectModel *) mergedModelFromBundles: (NSArray *) bundles 00204 { 00205 NSArray * modelPaths; 00206 NSMutableArray * models; 00207 00208 NSEnumerator * e; 00209 NSString * modelPath; 00210 00211 // find the involved .gsdatamodel files 00212 if (bundles != nil) 00213 // search specified bundles 00214 { 00215 NSEnumerator * e; 00216 NSBundle * bundle; 00217 NSMutableArray * array; 00218 00219 array = [NSMutableArray array]; 00220 00221 e = [bundles objectEnumerator]; 00222 while ((bundle = [e nextObject]) != nil) 00223 { 00224 [array addObjectsFromArray: 00225 [bundle pathsForResourcesOfType: @"gsdatamodel" inDirectory: nil]]; 00226 } 00227 00228 modelPaths = array; 00229 } 00230 else 00231 // search the main bundle 00232 { 00233 modelPaths = [[NSBundle mainBundle] 00234 pathsForResourcesOfType: @"gsdatamodel" inDirectory: nil]; 00235 } 00236 00237 00238 // initialize the models from them 00239 models = [NSMutableArray arrayWithCapacity: [modelPaths count]]; 00240 e = [modelPaths objectEnumerator]; 00241 while ((modelPath = [e nextObject]) != nil) 00242 { 00243 [models addObject: [[[NSManagedObjectModel alloc] 00244 initWithContentsOfFile: modelPath] 00245 autorelease]]; 00246 } 00247 00248 // and return the merged result 00249 return [self modelByMergingModels: models]; 00250 } 00251 00252 - (id) initWithContentsOfURL: (NSURL *) url 00253 { 00254 NSData * data; 00255 00256 // release the old instance - we'll return a new one 00257 [self release]; 00258 00259 if ((data = [NSData dataWithContentsOfURL: url]) == nil) 00260 { 00261 NSLog(_(@"Failed to access managed object model archive at: %@"), 00262 [url description]); 00263 00264 return nil; 00265 } 00266 00267 return [NSKeyedUnarchiver unarchiveObjectWithData: data]; 00268 } 00269 00270 - (id) _initWithContentsOfFile: (NSString *) file 00271 { 00272 return [self initWithContentsOfURL: [NSURL fileURLWithPath: file]]; 00273 } 00274 00275 - (id) init 00276 { 00277 if ((self = [super init])) 00278 { 00279 _configurations = [NSMutableDictionary new]; 00280 _fetchRequests = [NSMutableDictionary new]; 00281 } 00282 return self; 00283 } 00284 00285 - (NSArray *) entities 00286 { 00287 return _entities; 00288 } 00289 00290 - (NSDictionary *) entitiesByName 00291 { 00292 NSMutableDictionary * dict = [NSMutableDictionary 00293 dictionaryWithCapacity: [_entities count]]; 00294 NSEnumerator * e = [_entities objectEnumerator]; 00295 NSEntityDescription * entity; 00296 00297 while ((entity = [e nextObject]) != nil) 00298 { 00299 [dict setObject: entity forKey: [entity name]]; 00300 } 00301 00302 return [[dict copy] autorelease]; 00303 } 00304 00305 - (void) setEntities: (NSArray *) someEntities 00306 { 00307 [self _ensureEditableWithReason: @"Tried to set entities of a " 00308 @"managed object model already in use by an object graph manager."]; 00309 EnsureEntitiesHaveProperNames(someEntities); 00310 00311 if (_entities != nil) 00312 { 00313 [self _ungrabEntities: _entities]; 00314 DESTROY(_entities); 00315 } 00316 if (someEntities != nil) 00317 { 00318 _entities = [someEntities copy]; 00319 [self _grabEntities: _entities]; 00320 } 00321 } 00322 00323 - (NSArray *) configurations 00324 { 00325 return [_configurations allKeys]; 00326 } 00327 00328 - (NSArray *) entitiesForConfiguration: (NSString *) conf 00329 { 00330 return [_configurations objectForKey: conf]; 00331 } 00332 00333 - (void) setEntities: (NSArray *) entities 00334 forConfiguration: (NSString *) conf 00335 { 00336 NSArray * oldEntities; 00337 00338 [self _ensureEditableWithReason: @"Tried to set entities " 00339 @"for a configuration of a managed object model already in use " 00340 @"by an object graph manager."]; 00341 EnsureEntitiesHaveProperNames(entities); 00342 00343 oldEntities = [_configurations objectForKey: conf]; 00344 if (oldEntities != nil) 00345 { 00346 [self _ungrabEntities: oldEntities]; 00347 [_configurations removeObjectForKey: conf]; 00348 } 00349 if (entities != nil) 00350 { 00351 [_configurations setObject: [[entities copy] autorelease] 00352 forKey: conf]; 00353 [self _grabEntities: entities]; 00354 } 00355 } 00356 00357 - (NSDictionary *) _configurationsByName 00358 { 00359 return [[_configurations copy] autorelease]; 00360 } 00361 00362 - (NSFetchRequest *) fetchRequestTemplateForName: (NSString *) aName 00363 { 00364 return [_fetchRequests objectForKey: aName]; 00365 } 00366 00367 - (NSFetchRequest *) fetchRequestFromTemplateWithName: (NSString *) name 00368 substitutionVariables: (NSDictionary *) vars 00369 { 00370 NSFetchRequest * req, * template; 00371 00372 template = [_fetchRequests objectForKey: name]; 00373 if (template == nil) 00374 { 00375 return nil; 00376 } 00377 00378 req = [[template copy] autorelease]; 00379 if ([req predicate] != nil) 00380 { 00381 [req setPredicate: [[req predicate] 00382 predicateWithSubstitutionVariables: vars]]; 00383 } 00384 00385 return req; 00386 } 00387 00388 - (void) setFetchRequestTemplate: (NSFetchRequest *) request 00389 forName: (NSString *) name 00390 { 00391 if (_usedByPersistentStoreCoordinators) 00392 { 00393 [NSException raise: NSGenericException 00394 format: _(@"Tried to set a fetch request template " 00395 @"for a managed object model already in use " 00396 @"by an object graph manager.")]; 00397 } 00398 00399 // N.B. is this the way it should behave? 00400 if (request != nil) 00401 { 00402 [_fetchRequests setObject: request forKey: name]; 00403 } 00404 else 00405 { 00406 [_fetchRequests removeObjectForKey: name]; 00407 } 00408 } 00409 00410 - (void) _removeFetchRequestTemplateForName: (NSString *) name 00411 { 00412 if (_usedByPersistentStoreCoordinators) 00413 { 00414 [NSException raise: NSGenericException 00415 format: _(@"Tried to remove a fetch request template " 00416 @"from a managed object model already in use " 00417 @"by an object graph manager.")]; 00418 } 00419 00420 [_fetchRequests removeObjectForKey: name]; 00421 } 00422 00423 - (NSDictionary *) fetchRequestsByName 00424 { 00425 return [[_fetchRequests copy] autorelease]; 00426 } 00427 00428 - (NSDictionary *) localizationDictionary 00429 { 00430 // FIXME: what is this supposed to do ??? 00431 00432 return nil; 00433 } 00434 00435 - (void) setLocalizationDictionary: (NSDictionary *) dict 00436 { 00437 // FIXME: what is this supposed to do ??? 00438 } 00439 00440 - (BOOL) _isEditable 00441 { 00442 return (_usedByPersistentStoreCoordinators == 0); 00443 } 00444 00445 // NSCoding 00446 00447 - (id) initWithCoder: (NSCoder *) coder 00448 { 00449 if ((self = [super init])) 00450 { 00451 if ([coder allowsKeyedCoding]) 00452 { 00453 ASSIGN(_entities, [coder decodeObjectForKey: @"Entities"]); 00454 ASSIGN(_configurations, [coder decodeObjectForKey: 00455 @"Configurations"]); 00456 ASSIGN(_fetchRequests, [coder decodeObjectForKey: @"FetchRequests"]); 00457 } 00458 else 00459 { 00460 ASSIGN(_entities, [coder decodeObject]); 00461 ASSIGN(_configurations, [coder decodeObject]); 00462 ASSIGN(_fetchRequests, [coder decodeObject]); 00463 } 00464 } 00465 return self; 00466 } 00467 00468 - (void) encodeWithCoder: (NSCoder *) coder 00469 { 00470 if ([coder allowsKeyedCoding]) 00471 { 00472 [coder encodeObject: _entities forKey: @"Entities"]; 00473 [coder encodeObject: _configurations forKey: @"Configurations"]; 00474 [coder encodeObject: _fetchRequests forKey: @"FetchRequests"]; 00475 } 00476 else 00477 { 00478 [coder encodeObject: _entities]; 00479 [coder encodeObject: _configurations]; 00480 [coder encodeObject: _fetchRequests]; 00481 } 00482 } 00483 00484 // NSCopying 00485 00486 - (id) copyWithZone: (NSZone *) zone 00487 { 00488 NSManagedObjectModel * model; 00489 00490 NSEnumerator * e; 00491 NSString * conf; 00492 NSString * fetchRequestName; 00493 00494 model = [[NSManagedObjectModel allocWithZone: zone] init]; 00495 00496 // We must copy entities and configurations themselves too - they are 00497 // not shareable between several models. (FIXME: is this true? Apple 00498 // spec doesn't say a word about this - I just *guessed* it) 00499 [model setEntities: [[[NSArray alloc] 00500 initWithArray: _entities copyItems: YES] 00501 autorelease]]; 00502 00503 e = [[_configurations allKeys] objectEnumerator]; 00504 while ((conf = [e nextObject]) != nil) 00505 { 00506 [model setEntities: [[[NSArray alloc] 00507 initWithArray: [_configurations objectForKey: conf] 00508 copyItems: YES] 00509 autorelease] 00510 forConfiguration: conf]; 00511 } 00512 00513 // fetch requests appear to be shareable, so just set them 00514 e = [[_fetchRequests allKeys] objectEnumerator]; 00515 while ((fetchRequestName = [e nextObject]) != nil) 00516 { 00517 [model setFetchRequestTemplate: [_fetchRequests objectForKey: 00518 fetchRequestName] 00519 forName: fetchRequestName]; 00520 } 00521 00522 return model; 00523 } 00524 00529 - (void) _incrementUseCount 00530 { 00531 _usedByPersistentStoreCoordinators++; 00532 } 00533 00539 - (void) _decrementUseCount 00540 { 00541 NSAssert(_usedByPersistentStoreCoordinators > 0, 00542 _(@"Tried to underflow managed object model use count.")); 00543 00544 _usedByPersistentStoreCoordinators--; 00545 } 00546 00547 @end