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 [EOObjectUniquer forgetObject:_object];
234 for (i = [databaseInstances count] - 1; i >= 0; i--) {
237 db = [[databaseInstances objectAtIndex:i] nonretainedObjectValue];
238 [db forgetObject:_object];
244 - (void)forgetObject:(id)_object {
246 NSLog(@"DB[0x%08X]: forget object 0x%08X<%s> entity=%@",
247 self, _object, class_get_class_name(*(Class *)_object),
248 [[_object entity] name]);
250 [self->objectsDictionary forgetObject:_object];
253 - (void)forgetAllSnapshots {
254 [self->objectsDictionary forgetAllSnapshots];
257 - (id)objectForPrimaryKey:(NSDictionary *)_key entity:(EOEntity *)_entity {
258 if (self->flags.isUniquingObjects && (_key != nil) && (_entity != nil)) {
259 _key = [_entity primaryKeyForRow:_key];
263 id object = [self->objectsDictionary objectForPrimaryKey:_key entity:_entity];
267 if (![object isKindOfClass:[EOGenericRecord class]])
268 NSLog(@"object 0x%08X pkey=%@ entity=%@", object, _key, _entity);
277 - (NSDictionary *)snapshotForObject:_object {
278 EOUniquerRecord* rec = [self->objectsDictionary recordForObject:_object];
280 return rec ? rec->snapshot : nil;
283 - (NSDictionary *)primaryKeyForObject:(id)_object {
284 EOUniquerRecord* rec = [self->objectsDictionary recordForObject:_object];
286 return rec ? rec->pkey : nil;
289 - (void)primaryKey:(NSDictionary**)_key
290 andSnapshot:(NSDictionary**)_snapshot
291 forObject:(id)_object {
293 EOUniquerRecord *rec = [self->objectsDictionary recordForObject:_object];
296 if (_key) *_key = rec->pkey;
297 if (_snapshot) *_snapshot = rec->snapshot;
300 if (_key) *_key = nil;
301 if (_snapshot) *_snapshot = nil;
305 - (void)recordObject:(id)_object
306 primaryKey:(NSDictionary *)_key
307 snapshot:(NSDictionary *)_snapshot {
311 entity = [_object respondsToSelector:@selector(entity)]
313 : [[self->adaptor model] entityForObject:_object];
315 [self recordObject:_object
321 - (void)recordObject:(id)_object
322 primaryKey:(NSDictionary *)_key
323 entity:(EOEntity *)_entity
324 snapshot:(NSDictionary *)_snapshot {
326 if (_object == nil) {
327 [NSException raise:NSInvalidArgumentException
329 @"EODatabase:%x: Cannot record null object, "
330 @"in [EODatabase recordObject:primaryKey:entity:snapshot:]",
333 if ((_entity == nil) && self->flags.isUniquingObjects) {
334 [NSException raise:NSInvalidArgumentException
336 @"EODatabase:%x: Cannot record object with null entity "
337 @"when the database is uniquing objects, "
338 @"in [EODatabase recordObject:primaryKey:entity:snapshot:]",
341 _key = [_entity primaryKeyForRow:_key];
342 if ((_key == nil) && self->flags.isUniquingObjects) {
343 [NSException raise:NSInvalidArgumentException
345 @"EODatabase:%x: Cannot record object with null key "
346 @"when the database is uniquing objects, "
347 @"in [EODatabase recordObject:primaryKey:entity:snapshot:]",
350 if ((_snapshot == nil) && self->flags.isKeepingSnapshots) {
351 [NSException raise:NSInvalidArgumentException
353 @"EODatabase:%x: Cannot record object with null snapshot "
354 @"when the database is keeping snapshots, "
355 @"in [EODatabase recordObject:primaryKey:entity:snapshot:]",
359 [objectsDictionary recordObject:_object
360 primaryKey:self->flags.isUniquingObjects?_key:nil
361 entity:self->flags.isUniquingObjects ?_entity:nil
362 snapshot:self->flags.isKeepingSnapshots?_snapshot:nil];
365 - (BOOL)isObject:(id)_object
366 updatedOutsideContext:(EODatabaseContext *)_context
370 for (i = [contexts count] - 1; i >= 0; i--) {
371 EODatabaseContext *ctx;
373 ctx = [[self->contexts objectAtIndex:i] nonretainedObjectValue];
375 if ((ctx != _context) && [ctx isObjectUpdated:_object])
381 // ******************** Error messages ********************
383 - (void)setLogsErrorMessages:(BOOL)yn {
384 self->flags.isLoggingWarnings = yn;
386 - (BOOL)logsErrorMessages {
387 return self->flags.isLoggingWarnings;
390 - (void)reportErrorFormat:(NSString*)format, ... {
393 va_start(va, format);
394 [self reportErrorFormat:format arguments:va];
398 - (void)reportErrorFormat:(NSString*)format arguments:(va_list)arguments {
399 [self reportError:AUTORELEASE([[NSString alloc] initWithFormat:format
400 arguments:arguments])];
403 - (void)reportError:(NSString*)error {
404 if (self->flags.isLoggingWarnings)
405 NSLog(@"EODatabase:%x:%@", self, error);
408 @end /* EODatabase */
410 @implementation EODatabase(EOF2Additions)
412 - (NSArray *)models {
415 model = [[self adaptor] model];
416 return model ? [NSArray arrayWithObject:model] : nil;
419 - (void)addModel:(EOModel *)_model {
422 model = [[self adaptor] model];
425 [[self adaptor] setModel:_model];
427 [self notImplemented:_cmd];
430 - (BOOL)addModelIfCompatible:(EOModel *)_model {
434 if (![[self adaptor] canServiceModel:_model])
437 e = [[self models] objectEnumerator];
438 while ((m = [e nextObject])) {
442 if (![[m adaptorName] isEqualToString:[_model adaptorName]])
446 [self addModel:_model];
450 - (EOEntity *)entityForObject:(id)_object {
451 return [[[self adaptor] model] entityForObject:_object];
453 - (EOEntity *)entityNamed:(NSString *)_name {
454 return [[[self adaptor] model] entityNamed:_name];
459 - (void)forgetSnapshotsForGlobalIDs:(NSArray *)_gids {
463 e = [_gids objectEnumerator];
464 while ((gid = [e nextObject]))
465 [self forgetSnapshotsForGlobalID:gid];
467 - (void)forgetSnapshotsForGlobalID:(EOGlobalID *)_gid {
468 [self notImplemented:_cmd];
471 - (void)recordSnapshot:(NSDictionary *)_snapshot forGlobalID:(EOGlobalID *)_gid {
472 [self notImplemented:_cmd];
474 - (void)recordSnapshots:(NSDictionary *)_snapshots {
478 gids = [_snapshots keyEnumerator];
479 while ((gid = [gids nextObject]))
480 [self recordSnapshot:[_snapshots objectForKey:gid] forGlobalID:gid];
483 - (void)recordSnapshot:(NSArray *)_gids
484 forSourceGlobalID:(EOGlobalID *)_gid
485 relationshipName:(NSString *)_name
487 /* to-many snapshot */
488 [self notImplemented:_cmd];
490 - (void)recordToManySnapshots:(NSDictionary *)_snapshots {
494 gids = [_snapshots keyEnumerator];
495 while ((gid = [gids nextObject])) {
497 NSEnumerator *relNames;
500 d = [_snapshots objectForKey:gid];
501 relNames = [d keyEnumerator];
503 while ((relName = [relNames nextObject])) {
504 [self recordSnapshot:[d objectForKey:relName]
505 forSourceGlobalID:gid
506 relationshipName:relName];
511 - (NSDictionary *)snapshotForGlobalID:(EOGlobalID *)_gid
512 after:(NSTimeInterval)_duration
514 NSLog(@"ERROR(%s): subclasses must override this method!",
515 __PRETTY_FUNCTION__);
519 - (NSDictionary *)snapshotForGlobalID:(EOGlobalID *)_gid {
520 return [self snapshotForGlobalID:_gid after:NSDistantPastTimeInterval];
523 @end /* EODatabase(EOF2Additions) */