]> err.no Git - sope/blob - sope-gdl1/GDLAccess/EODatabase.m
added missing inline pathes
[sope] / sope-gdl1 / GDLAccess / EODatabase.m
1 /* 
2    EODatabase.m
3
4    Copyright (C) 1996 Free Software Foundation, Inc.
5
6    Author: Mircea Oancea <mircea@jupiter.elcom.pub.ro>
7    Date: 1996
8
9    This file is part of the GNUstep Database Library.
10
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.
15
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.
20
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.
25 */
26
27 #import "common.h"
28 #import "EODatabase.h"
29 #import "EOAdaptor.h"
30 #import "EOModel.h"
31 #import "EOEntity.h"
32 #import "EOGenericRecord.h"
33 #import "EODatabaseContext.h"
34 #import "EOObjectUniquer.h"
35 #import "EODatabaseFault.h"
36
37 NSTimeInterval NSDistantPastTimeInterval = 0.0;
38
39 @implementation EODatabase
40
41 // Database Global Methods
42
43 static NSMutableArray  *databaseInstances = nil;
44 static NSRecursiveLock *lock              = nil;
45
46 + (void)initialize {
47   static BOOL isInitialized = NO;
48   // THREAD
49   if (!isInitialized) {
50     isInitialized = YES;
51     databaseInstances = [[NSMutableArray alloc] init];
52     lock              = [[NSRecursiveLock alloc] init];
53   }
54 }
55
56 static inline void _addDatabaseInstance(EODatabase *_db) {
57   [lock lock];
58   [databaseInstances addObject:[NSValue valueWithNonretainedObject:_db]];
59   [lock unlock];
60 }
61 static inline void _removeDatabaseInstance(EODatabase *_db) {
62   [lock lock];
63   {
64     int i;
65     
66     for (i = [databaseInstances count] - 1; i >= 0; i--) {
67       EODatabase *db;
68
69       db = [[databaseInstances objectAtIndex:i] nonretainedObjectValue];
70       if (db == _db) {
71         [databaseInstances removeObjectAtIndex:i];
72         break;
73       }
74     }
75   }
76   [lock unlock];
77 }
78
79 /*
80  * Initializing new instances
81  */
82
83 - (id)initWithAdaptor:(EOAdaptor *)_adaptor {
84   if (_adaptor == nil) {
85     AUTORELEASE(self);
86     return nil;
87   }
88     
89   self->adaptor           = RETAIN(_adaptor);
90   self->objectsDictionary = [[EOObjectUniquer allocWithZone:[self zone]] init];
91   self->contexts          = [[NSMutableArray  allocWithZone:[self zone]] init];
92     
93   self->flags.isUniquingObjects  = YES;
94   self->flags.isKeepingSnapshots = YES;
95   self->flags.isLoggingWarnings  = YES;
96
97   _addDatabaseInstance(self);
98     
99   return self;
100 }
101
102 - (id)initWithModel:(EOModel *)_model {
103   return [self initWithAdaptor:[EOAdaptor adaptorWithModel:_model]];
104 }
105 - (id)init {
106   return [self initWithAdaptor:nil];
107 }
108
109 - (void)dealloc {
110   _removeDatabaseInstance(self);
111   
112   RELEASE(self->adaptor);
113   RELEASE(self->objectsDictionary);
114   RELEASE(self->contexts);
115   [super dealloc];
116 }
117
118 // accessors
119
120 - (EOAdaptor *)adaptor {
121   return self->adaptor;
122 }
123
124 - (EOObjectUniquer *)objectUniquer {
125   return self->objectsDictionary;
126 }
127
128 // Checking connection status
129
130 - (BOOL)hasOpenChannels {
131   int i;
132     
133   for (i = ([self->contexts count] - 1); i >= 0; i--) {
134     if ([[[self->contexts objectAtIndex:i] nonretainedObjectValue]
135                           hasOpenChannels])
136       return YES;
137   }
138   return NO;
139 }
140
141 /*
142  * Getting the database contexts
143  */
144
145 - (id)createContext {
146   return AUTORELEASE([[EODatabaseContext alloc] initWithDatabase:self]);
147 }
148
149 - (NSArray *)contexts {
150   NSMutableArray *array = nil;
151   int i, n;
152
153   n = [self->contexts count];
154   array = [[NSMutableArray alloc] initWithCapacity:n];
155     
156   for (i = 0; i < n; i++) {
157     EODatabaseContext *ctx;
158
159     ctx = [[self->contexts objectAtIndex:i] nonretainedObjectValue];
160     [array addObject:ctx];
161   }
162   return AUTORELEASE(array);
163 }
164
165 - (void)contextDidInit:(id)_context {
166   [self->contexts addObject:[NSValue valueWithNonretainedObject:_context]];
167 }
168
169 - (void)contextWillDealloc:(id)aContext {
170   int i;
171     
172   for (i = [self->contexts count]-1; i >= 0; i--) {
173     if ([[self->contexts objectAtIndex:i] nonretainedObjectValue] == aContext) {
174       [self->contexts removeObjectAtIndex:i];
175       break;
176     }
177   }
178 }
179
180 /*
181  * Uniquing/snapshotting
182  */
183
184 - (void)setUniquesObjects:(BOOL)yn {
185   if ([self hasOpenChannels]) {
186     [NSException raise:NSInvalidArgumentException
187                  format:
188               @"EODatabase:%x: All channels must be closed when changing "
189               @"uniquing mode in the EODatabase, "
190               @"in [EODatabase setUniquesObjects:]",
191                  self];
192   }
193
194   if ((!yn) && (self->flags.isUniquingObjects))
195     [self->objectsDictionary forgetAllObjects];
196   self->flags.isUniquingObjects = yn;
197 }
198 - (BOOL)uniquesObjects {
199   return self->flags.isUniquingObjects;
200 }
201
202 - (void)setKeepsSnapshots:(BOOL)yn {
203   if ([self hasOpenChannels]) {
204     [NSException raise:NSInvalidArgumentException
205                  format:
206               @"EODatabase:%x: All channels must be closed when changing "
207               @"snapshoting mode in the EODatabase, "
208               @"in [EODatabase setKeepsSnapshots:]",
209                  self];
210   }
211
212   if ((yn == NO) && self->flags.isKeepingSnapshots)
213     [self->objectsDictionary forgetAllSnapshots];
214   
215   self->flags.isKeepingSnapshots = yn;
216 }
217 - (BOOL)keepsSnapshots {
218   return self->flags.isKeepingSnapshots;
219 }
220
221 // ******************** Handle Objects ********************
222
223 - (void)forgetAllObjects {
224   [self->objectsDictionary forgetAllObjects];
225 }
226
227 + (void)forgetObject:(id)_object {
228   static Class UniquerClass = Nil;
229   if (UniquerClass == Nil) UniquerClass = [EOObjectUniquer class];
230   [(EOObjectUniquer *)UniquerClass forgetObject:_object];
231
232   [lock lock];
233   {
234     int i;
235     
236     for (i = [databaseInstances count] - 1; i >= 0; i--) {
237       EODatabase *db;
238
239       db = [[databaseInstances objectAtIndex:i] nonretainedObjectValue];
240       [db forgetObject:_object];
241     }
242   }
243   [lock unlock];
244 }
245
246 - (void)forgetObject:(id)_object {
247   /*
248     NSLog(@"DB[0x%08X]: forget object 0x%08X<%s> entity=%@",
249         self, _object, class_get_class_name(*(Class *)_object),
250         [[_object entity] name]);
251   */
252   [self->objectsDictionary forgetObject:_object];
253 }
254
255 - (void)forgetAllSnapshots {
256   [self->objectsDictionary forgetAllSnapshots];
257 }
258
259 - (id)objectForPrimaryKey:(NSDictionary *)_key entity:(EOEntity *)_entity {
260   if (self->flags.isUniquingObjects && (_key != nil) && (_entity != nil)) {
261     _key = [_entity primaryKeyForRow:_key];
262     if (_key == nil)
263       return nil;
264     else {
265       id object = [self->objectsDictionary objectForPrimaryKey:_key entity:_entity];
266       
267 #if 0
268       if (object) {
269         if (![object isKindOfClass:[EOGenericRecord class]])
270           NSLog(@"object 0x%08X pkey=%@ entity=%@", object, _key, _entity);
271       }
272 #endif
273       return object;
274     }
275   }
276   return nil;
277 }
278
279 - (NSDictionary *)snapshotForObject:_object {
280   EOUniquerRecord* rec = [self->objectsDictionary recordForObject:_object];
281     
282   return rec ? rec->snapshot : nil;
283 }
284
285 - (NSDictionary *)primaryKeyForObject:(id)_object {
286   EOUniquerRecord* rec = [self->objectsDictionary recordForObject:_object];
287     
288   return rec ? rec->pkey : nil;
289 }
290
291 - (void)primaryKey:(NSDictionary**)_key
292   andSnapshot:(NSDictionary**)_snapshot
293   forObject:(id)_object {
294
295   EOUniquerRecord *rec = [self->objectsDictionary recordForObject:_object];
296     
297   if (rec) {
298     if (_key)      *_key      = rec->pkey;
299     if (_snapshot) *_snapshot = rec->snapshot;
300   }
301   else {
302     if (_key)      *_key      = nil;
303     if (_snapshot) *_snapshot = nil;
304   }
305 }
306
307 - (void)recordObject:(id)_object
308   primaryKey:(NSDictionary *)_key
309   snapshot:(NSDictionary *)_snapshot {
310
311   EOEntity *entity;
312
313   entity = [_object respondsToSelector:@selector(entity)]
314     ? [_object entity]
315     : [[self->adaptor model] entityForObject:_object];
316     
317   [self recordObject:_object
318         primaryKey:_key
319         entity:entity
320         snapshot:_snapshot];
321 }
322
323 - (void)recordObject:(id)_object
324   primaryKey:(NSDictionary *)_key
325   entity:(EOEntity *)_entity
326   snapshot:(NSDictionary *)_snapshot {
327
328   if (_object == nil) {
329     [NSException raise:NSInvalidArgumentException
330                  format:
331                    @"EODatabase:%x: Cannot record null object, "
332                    @"in [EODatabase recordObject:primaryKey:entity:snapshot:]",
333                    self];
334   }
335   if ((_entity == nil) && self->flags.isUniquingObjects) {
336     [NSException raise:NSInvalidArgumentException
337                  format:
338               @"EODatabase:%x: Cannot record object with null entity "
339               @"when the database is uniquing objects, "
340               @"in [EODatabase recordObject:primaryKey:entity:snapshot:]",
341                  self];
342   }
343   _key = [_entity primaryKeyForRow:_key];
344   if ((_key == nil) && self->flags.isUniquingObjects) {
345     [NSException raise:NSInvalidArgumentException
346                  format:
347               @"EODatabase:%x: Cannot record object with null key "
348               @"when the database is uniquing objects, "
349               @"in [EODatabase recordObject:primaryKey:entity:snapshot:]",
350                  self];
351   }
352   if ((_snapshot == nil) && self->flags.isKeepingSnapshots) {
353     [NSException raise:NSInvalidArgumentException
354                  format:
355               @"EODatabase:%x: Cannot record object with null snapshot "
356               @"when the database is keeping snapshots, "
357               @"in [EODatabase recordObject:primaryKey:entity:snapshot:]",
358                  self];
359   }
360
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];
365 }
366
367 - (BOOL)isObject:(id)_object 
368   updatedOutsideContext:(EODatabaseContext *)_context 
369 {
370   int i;
371     
372   for (i = [contexts count] - 1; i >= 0; i--) {
373     EODatabaseContext *ctx;
374
375     ctx = [[self->contexts objectAtIndex:i] nonretainedObjectValue];
376         
377     if ((ctx != _context) && [ctx isObjectUpdated:_object])
378       return YES;
379   }
380   return NO;
381 }
382
383 // ******************** Error messages ********************
384  
385 - (void)setLogsErrorMessages:(BOOL)yn {
386   self->flags.isLoggingWarnings = yn;
387 }
388 - (BOOL)logsErrorMessages {
389   return self->flags.isLoggingWarnings;
390 }
391
392 - (void)reportErrorFormat:(NSString*)format, ... {
393   va_list va;
394     
395   va_start(va, format);
396   [self reportErrorFormat:format arguments:va];
397   va_end(va);
398 }
399
400 - (void)reportErrorFormat:(NSString*)format arguments:(va_list)arguments {
401   [self reportError:AUTORELEASE([[NSString alloc] initWithFormat:format 
402                                                   arguments:arguments])];
403 }
404
405 - (void)reportError:(NSString*)error {
406   if (self->flags.isLoggingWarnings)
407     NSLog(@"EODatabase:%x:%@", self, error);
408 }
409
410 @end /* EODatabase */
411
412 @implementation EODatabase(EOF2Additions)
413
414 - (NSArray *)models {
415   EOModel *model;
416
417   model = [[self adaptor] model];
418   return model ? [NSArray arrayWithObject:model] : nil;
419 }
420
421 - (void)addModel:(EOModel *)_model {
422   EOModel *model;
423
424   model = [[self adaptor] model];
425
426   if (model == nil)
427     [[self adaptor] setModel:_model];
428   else
429     [self notImplemented:_cmd];
430 }
431
432 - (BOOL)addModelIfCompatible:(EOModel *)_model {
433   NSEnumerator *e;
434   EOModel *m;
435
436   if (![[self adaptor] canServiceModel:_model])
437     return NO;
438
439   e = [[self models] objectEnumerator];
440   while ((m = [e nextObject])) {
441     if (m == _model)
442       return YES;
443
444     if (![[m adaptorName] isEqualToString:[_model adaptorName]])
445       return NO;
446   }
447   
448   [self addModel:_model];
449   return YES;
450 }
451
452 - (EOEntity *)entityForObject:(id)_object {
453   return [[[self adaptor] model] entityForObject:_object];
454 }
455 - (EOEntity *)entityNamed:(NSString *)_name {
456   return [[[self adaptor] model] entityNamed:_name];
457 }
458
459 /* snapshots */
460
461 - (void)forgetSnapshotsForGlobalIDs:(NSArray *)_gids {
462   NSEnumerator *e;
463   EOGlobalID   *gid;
464
465   e = [_gids objectEnumerator];
466   while ((gid = [e nextObject]))
467     [self forgetSnapshotsForGlobalID:gid];
468 }
469 - (void)forgetSnapshotsForGlobalID:(EOGlobalID *)_gid {
470   [self notImplemented:_cmd];
471 }
472
473 - (void)recordSnapshot:(NSDictionary *)_snapshot forGlobalID:(EOGlobalID *)_gid {
474   [self notImplemented:_cmd];
475 }
476 - (void)recordSnapshots:(NSDictionary *)_snapshots {
477   NSEnumerator *gids;
478   EOGlobalID   *gid;
479
480   gids = [_snapshots keyEnumerator];
481   while ((gid = [gids nextObject]))
482     [self recordSnapshot:[_snapshots objectForKey:gid] forGlobalID:gid];
483 }
484
485 - (void)recordSnapshot:(NSArray *)_gids
486   forSourceGlobalID:(EOGlobalID *)_gid
487   relationshipName:(NSString *)_name
488 {
489   /* to-many snapshot */
490   [self notImplemented:_cmd];
491 }
492 - (void)recordToManySnapshots:(NSDictionary *)_snapshots {
493   NSEnumerator *gids;
494   EOGlobalID   *gid;
495
496   gids = [_snapshots keyEnumerator];
497   while ((gid = [gids nextObject])) {
498     NSDictionary *d;
499     NSEnumerator *relNames;
500     NSString     *relName;
501     
502     d = [_snapshots objectForKey:gid];
503     relNames = [d keyEnumerator];
504
505     while ((relName = [relNames nextObject])) {
506       [self recordSnapshot:[d objectForKey:relName]
507             forSourceGlobalID:gid
508             relationshipName:relName];
509     }
510   }
511 }
512
513 - (NSDictionary *)snapshotForGlobalID:(EOGlobalID *)_gid
514   after:(NSTimeInterval)_duration
515 {
516   NSLog(@"ERROR(%s): subclasses must override this method!",
517         __PRETTY_FUNCTION__);
518   return nil;
519 }
520
521 - (NSDictionary *)snapshotForGlobalID:(EOGlobalID *)_gid {
522   return [self snapshotForGlobalID:_gid after:NSDistantPastTimeInterval];
523 }
524
525 @end /* EODatabase(EOF2Additions) */