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) */