4 Copyright (C) 1996 Free Software Foundation, Inc.
6 Author: Mircea Oancea <mircea@jupiter.elcom.pub.ro>
9 This file is part of the GNUstep Database Library.
11 This library is free software; you can redistribute it and/or
12 modify it under the terms of the GNU Library General Public
13 License as published by the Free Software Foundation; either
14 version 2 of the License, or (at your option) any later version.
16 This library is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 Library General Public License for more details.
21 You should have received a copy of the GNU Library General Public
22 License along with this library; see the file COPYING.LIB.
23 If not, write to the Free Software Foundation,
24 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
28 #import "EODatabase.h"
32 #import "EOGenericRecord.h"
33 #import "EODatabaseContext.h"
34 #import "EOObjectUniquer.h"
35 #import "EODatabaseFault.h"
37 NSTimeInterval NSDistantPastTimeInterval = 0.0;
39 @implementation EODatabase
41 // Database Global Methods
43 static NSMutableArray *databaseInstances = nil;
44 static NSRecursiveLock *lock = nil;
47 static BOOL isInitialized = NO;
51 databaseInstances = [[NSMutableArray alloc] init];
52 lock = [[NSRecursiveLock alloc] init];
56 static inline void _addDatabaseInstance(EODatabase *_db) {
58 [databaseInstances addObject:[NSValue valueWithNonretainedObject:_db]];
61 static inline void _removeDatabaseInstance(EODatabase *_db) {
66 for (i = [databaseInstances count] - 1; i >= 0; i--) {
69 db = [[databaseInstances objectAtIndex:i] nonretainedObjectValue];
71 [databaseInstances removeObjectAtIndex:i];
80 * Initializing new instances
83 - (id)initWithAdaptor:(EOAdaptor *)_adaptor {
84 if (_adaptor == nil) {
89 self->adaptor = RETAIN(_adaptor);
90 self->objectsDictionary = [[EOObjectUniquer allocWithZone:[self zone]] init];
91 self->contexts = [[NSMutableArray allocWithZone:[self zone]] init];
93 self->flags.isUniquingObjects = YES;
94 self->flags.isKeepingSnapshots = YES;
95 self->flags.isLoggingWarnings = YES;
97 _addDatabaseInstance(self);
102 - (id)initWithModel:(EOModel *)_model {
103 return [self initWithAdaptor:[EOAdaptor adaptorWithModel:_model]];
106 return [self initWithAdaptor:nil];
110 _removeDatabaseInstance(self);
112 RELEASE(self->adaptor);
113 RELEASE(self->objectsDictionary);
114 RELEASE(self->contexts);
120 - (EOAdaptor *)adaptor {
121 return self->adaptor;
124 - (EOObjectUniquer *)objectUniquer {
125 return self->objectsDictionary;
128 // Checking connection status
130 - (BOOL)hasOpenChannels {
133 for (i = ([self->contexts count] - 1); i >= 0; i--) {
134 if ([[[self->contexts objectAtIndex:i] nonretainedObjectValue]
142 * Getting the database contexts
145 - (id)createContext {
146 return AUTORELEASE([[EODatabaseContext alloc] initWithDatabase:self]);
149 - (NSArray *)contexts {
150 NSMutableArray *array = nil;
153 n = [self->contexts count];
154 array = [[NSMutableArray alloc] initWithCapacity:n];
156 for (i = 0; i < n; i++) {
157 EODatabaseContext *ctx;
159 ctx = [[self->contexts objectAtIndex:i] nonretainedObjectValue];
160 [array addObject:ctx];
162 return AUTORELEASE(array);
165 - (void)contextDidInit:(id)_context {
166 [self->contexts addObject:[NSValue valueWithNonretainedObject:_context]];
169 - (void)contextWillDealloc:(id)aContext {
172 for (i = [self->contexts count]-1; i >= 0; i--) {
173 if ([[self->contexts objectAtIndex:i] nonretainedObjectValue] == aContext) {
174 [self->contexts removeObjectAtIndex:i];
181 * Uniquing/snapshotting
184 - (void)setUniquesObjects:(BOOL)yn {
185 if ([self hasOpenChannels]) {
186 [NSException raise:NSInvalidArgumentException
188 @"EODatabase:%x: All channels must be closed when changing "
189 @"uniquing mode in the EODatabase, "
190 @"in [EODatabase setUniquesObjects:]",
194 if ((!yn) && (self->flags.isUniquingObjects))
195 [self->objectsDictionary forgetAllObjects];
196 self->flags.isUniquingObjects = yn;
198 - (BOOL)uniquesObjects {
199 return self->flags.isUniquingObjects;
202 - (void)setKeepsSnapshots:(BOOL)yn {
203 if ([self hasOpenChannels]) {
204 [NSException raise:NSInvalidArgumentException
206 @"EODatabase:%x: All channels must be closed when changing "
207 @"snapshoting mode in the EODatabase, "
208 @"in [EODatabase setKeepsSnapshots:]",
212 if ((yn == NO) && self->flags.isKeepingSnapshots)
213 [self->objectsDictionary forgetAllSnapshots];
215 self->flags.isKeepingSnapshots = yn;
217 - (BOOL)keepsSnapshots {
218 return self->flags.isKeepingSnapshots;
221 // ******************** Handle Objects ********************
223 - (void)forgetAllObjects {
224 [self->objectsDictionary forgetAllObjects];
227 + (void)forgetObject:(id)_object {
228 static Class UniquerClass = Nil;
229 if (UniquerClass == Nil) UniquerClass = [EOObjectUniquer class];
230 [(EOObjectUniquer *)UniquerClass forgetObject:_object];
236 for (i = [databaseInstances count] - 1; i >= 0; i--) {
239 db = [[databaseInstances objectAtIndex:i] nonretainedObjectValue];
240 [db forgetObject:_object];
246 - (void)forgetObject:(id)_object {
248 NSLog(@"DB[0x%08X]: forget object 0x%08X<%s> entity=%@",
249 self, _object, class_get_class_name(*(Class *)_object),
250 [[_object entity] name]);
252 [self->objectsDictionary forgetObject:_object];
255 - (void)forgetAllSnapshots {
256 [self->objectsDictionary forgetAllSnapshots];
259 - (id)objectForPrimaryKey:(NSDictionary *)_key entity:(EOEntity *)_entity {
260 if (self->flags.isUniquingObjects && (_key != nil) && (_entity != nil)) {
261 _key = [_entity primaryKeyForRow:_key];
265 id object = [self->objectsDictionary objectForPrimaryKey:_key entity:_entity];
269 if (![object isKindOfClass:[EOGenericRecord class]])
270 NSLog(@"object 0x%08X pkey=%@ entity=%@", object, _key, _entity);
279 - (NSDictionary *)snapshotForObject:_object {
280 EOUniquerRecord* rec = [self->objectsDictionary recordForObject:_object];
282 return rec ? rec->snapshot : nil;
285 - (NSDictionary *)primaryKeyForObject:(id)_object {
286 EOUniquerRecord* rec = [self->objectsDictionary recordForObject:_object];
288 return rec ? rec->pkey : nil;
291 - (void)primaryKey:(NSDictionary**)_key
292 andSnapshot:(NSDictionary**)_snapshot
293 forObject:(id)_object {
295 EOUniquerRecord *rec = [self->objectsDictionary recordForObject:_object];
298 if (_key) *_key = rec->pkey;
299 if (_snapshot) *_snapshot = rec->snapshot;
302 if (_key) *_key = nil;
303 if (_snapshot) *_snapshot = nil;
307 - (void)recordObject:(id)_object
308 primaryKey:(NSDictionary *)_key
309 snapshot:(NSDictionary *)_snapshot {
313 entity = [_object respondsToSelector:@selector(entity)]
315 : [[self->adaptor model] entityForObject:_object];
317 [self recordObject:_object
323 - (void)recordObject:(id)_object
324 primaryKey:(NSDictionary *)_key
325 entity:(EOEntity *)_entity
326 snapshot:(NSDictionary *)_snapshot {
328 if (_object == nil) {
329 [NSException raise:NSInvalidArgumentException
331 @"EODatabase:%x: Cannot record null object, "
332 @"in [EODatabase recordObject:primaryKey:entity:snapshot:]",
335 if ((_entity == nil) && self->flags.isUniquingObjects) {
336 [NSException raise:NSInvalidArgumentException
338 @"EODatabase:%x: Cannot record object with null entity "
339 @"when the database is uniquing objects, "
340 @"in [EODatabase recordObject:primaryKey:entity:snapshot:]",
343 _key = [_entity primaryKeyForRow:_key];
344 if ((_key == nil) && self->flags.isUniquingObjects) {
345 [NSException raise:NSInvalidArgumentException
347 @"EODatabase:%x: Cannot record object with null key "
348 @"when the database is uniquing objects, "
349 @"in [EODatabase recordObject:primaryKey:entity:snapshot:]",
352 if ((_snapshot == nil) && self->flags.isKeepingSnapshots) {
353 [NSException raise:NSInvalidArgumentException
355 @"EODatabase:%x: Cannot record object with null snapshot "
356 @"when the database is keeping snapshots, "
357 @"in [EODatabase recordObject:primaryKey:entity:snapshot:]",
361 [objectsDictionary recordObject:_object
362 primaryKey:self->flags.isUniquingObjects?_key:nil
363 entity:self->flags.isUniquingObjects ?_entity:nil
364 snapshot:self->flags.isKeepingSnapshots?_snapshot:nil];
367 - (BOOL)isObject:(id)_object
368 updatedOutsideContext:(EODatabaseContext *)_context
372 for (i = [contexts count] - 1; i >= 0; i--) {
373 EODatabaseContext *ctx;
375 ctx = [[self->contexts objectAtIndex:i] nonretainedObjectValue];
377 if ((ctx != _context) && [ctx isObjectUpdated:_object])
383 // ******************** Error messages ********************
385 - (void)setLogsErrorMessages:(BOOL)yn {
386 self->flags.isLoggingWarnings = yn;
388 - (BOOL)logsErrorMessages {
389 return self->flags.isLoggingWarnings;
392 - (void)reportErrorFormat:(NSString*)format, ... {
395 va_start(va, format);
396 [self reportErrorFormat:format arguments:va];
400 - (void)reportErrorFormat:(NSString*)format arguments:(va_list)arguments {
401 [self reportError:AUTORELEASE([[NSString alloc] initWithFormat:format
402 arguments:arguments])];
405 - (void)reportError:(NSString*)error {
406 if (self->flags.isLoggingWarnings)
407 NSLog(@"EODatabase:%x:%@", self, error);
410 @end /* EODatabase */
412 @implementation EODatabase(EOF2Additions)
414 - (NSArray *)models {
417 model = [[self adaptor] model];
418 return model ? [NSArray arrayWithObject:model] : nil;
421 - (void)addModel:(EOModel *)_model {
424 model = [[self adaptor] model];
427 [[self adaptor] setModel:_model];
429 [self notImplemented:_cmd];
432 - (BOOL)addModelIfCompatible:(EOModel *)_model {
436 if (![[self adaptor] canServiceModel:_model])
439 e = [[self models] objectEnumerator];
440 while ((m = [e nextObject])) {
444 if (![[m adaptorName] isEqualToString:[_model adaptorName]])
448 [self addModel:_model];
452 - (EOEntity *)entityForObject:(id)_object {
453 return [[[self adaptor] model] entityForObject:_object];
455 - (EOEntity *)entityNamed:(NSString *)_name {
456 return [[[self adaptor] model] entityNamed:_name];
461 - (void)forgetSnapshotsForGlobalIDs:(NSArray *)_gids {
465 e = [_gids objectEnumerator];
466 while ((gid = [e nextObject]))
467 [self forgetSnapshotsForGlobalID:gid];
469 - (void)forgetSnapshotsForGlobalID:(EOGlobalID *)_gid {
470 [self notImplemented:_cmd];
473 - (void)recordSnapshot:(NSDictionary *)_snapshot forGlobalID:(EOGlobalID *)_gid {
474 [self notImplemented:_cmd];
476 - (void)recordSnapshots:(NSDictionary *)_snapshots {
480 gids = [_snapshots keyEnumerator];
481 while ((gid = [gids nextObject]))
482 [self recordSnapshot:[_snapshots objectForKey:gid] forGlobalID:gid];
485 - (void)recordSnapshot:(NSArray *)_gids
486 forSourceGlobalID:(EOGlobalID *)_gid
487 relationshipName:(NSString *)_name
489 /* to-many snapshot */
490 [self notImplemented:_cmd];
492 - (void)recordToManySnapshots:(NSDictionary *)_snapshots {
496 gids = [_snapshots keyEnumerator];
497 while ((gid = [gids nextObject])) {
499 NSEnumerator *relNames;
502 d = [_snapshots objectForKey:gid];
503 relNames = [d keyEnumerator];
505 while ((relName = [relNames nextObject])) {
506 [self recordSnapshot:[d objectForKey:relName]
507 forSourceGlobalID:gid
508 relationshipName:relName];
513 - (NSDictionary *)snapshotForGlobalID:(EOGlobalID *)_gid
514 after:(NSTimeInterval)_duration
516 NSLog(@"ERROR(%s): subclasses must override this method!",
517 __PRETTY_FUNCTION__);
521 - (NSDictionary *)snapshotForGlobalID:(EOGlobalID *)_gid {
522 return [self snapshotForGlobalID:_gid after:NSDistantPastTimeInterval];
525 @end /* EODatabase(EOF2Additions) */