4 Copyright (C) 1996 Free Software Foundation, Inc.
6 Author: Ovidiu Predescu <ovidiu@bx.logicnet.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.
27 #include "EOAdaptor.h"
28 #include "EOAdaptorChannel.h"
29 #include "EOAdaptorContext.h"
30 #include "EOAttribute.h"
31 #include "EOFExceptions.h"
33 #include "EOSQLExpression.h"
36 @implementation EOAdaptor
38 + (id)adaptorWithModel:(EOModel*)_model {
39 /* Check first to see if the adaptor class exists in the running program
40 by testing the existence of [_model adaptorClassName]. */
41 NSString *adaptorName = [_model adaptorName];
42 Class adaptorClass = NSClassFromString([_model adaptorClassName]);
45 adaptor = adaptorClass
46 ? AUTORELEASE([[adaptorClass alloc] initWithName:adaptorName])
47 : [self adaptorWithName:adaptorName];
49 [adaptor setModel:_model];
53 + (NSArray *)adaptorSearchPathes {
54 // TODO: add support for Cocoa
55 static NSArray *searchPathes = nil;
60 if (searchPathes != nil) return searchPathes;
62 env = [[NSProcessInfo processInfo] environment];
63 ma = [NSMutableArray arrayWithCapacity:8];
65 if ((tmp = [env objectForKey:@"GNUSTEP_PATHPREFIX_LIST"]) == nil)
66 tmp = [env objectForKey:@"GNUSTEP_PATHLIST"];
67 tmp = [tmp componentsSeparatedByString:@":"];
68 if ([tmp count] > 0) {
71 e = [tmp objectEnumerator];
72 while ((tmp = [e nextObject])) {
73 if (![tmp hasSuffix:@"/"]) tmp = [tmp stringByAppendingString:@"/"];
75 tmp = [tmp stringByAppendingFormat:@"Library/GDLAdaptors-%i.%i",
76 GDL_MAJOR_VERSION, GDL_MINOR_VERSION];
78 if ([ma containsObject:tmp] || tmp == nil) continue;
83 tmp = [NSString stringWithFormat:@"/lib/sope-%i.%i/dbadaptors",
84 SOPE_MAJOR_VERSION, SOPE_MINOR_VERSION];
85 [ma addObject:[@"/usr/local" stringByAppendingString:tmp]];
86 [ma addObject:[@"/usr" stringByAppendingString:tmp]];
88 searchPathes = [ma copy];
89 if ([searchPathes count] == 0)
90 NSLog(@"%s: empty library search path !", __PRETTY_FUNCTION__);
94 + (id)adaptorWithName:(NSString *)adaptorName {
97 NSString *adaptorBundlePath = nil;
98 Class adaptorClass = Nil;
100 bundle = [NSBundle mainBundle];
103 if ((adaptorName == nil) || [adaptorName isEqual:@""])
106 /* Look in application bundle */
107 bundle = [NSBundle mainBundle];
109 [bundle pathForResource:adaptorName ofType:@"gdladaptor"];
111 /* Look in standard paths */
112 if (adaptorBundlePath == nil) {
117 fm = [NSFileManager defaultManager];
118 paths = [self adaptorSearchPathes];
119 dirname = [adaptorName stringByAppendingPathExtension:@"gdladaptor"];
121 /* Loop through the paths and check each one */
122 for (i = 0, count = [paths count]; i < count; i++) {
126 p = [[paths objectAtIndex:i] stringByAppendingPathComponent:dirname];
127 if (![fm fileExistsAtPath:p isDirectory:&isDir])
132 adaptorBundlePath = p;
137 /* Make adaptor bundle */
138 bundle = adaptorBundlePath
139 ? [NSBundle bundleWithPath:adaptorBundlePath]
144 NSLog(@"EOAdaptor: cannot find adaptor bundle: '%@'", adaptorName);
146 [[[[CannotFindAdaptorBundleException alloc]
147 initWithFormat:@"Cannot find adaptor bundle '%@'",
148 adaptorName] autorelease] raise];
155 if (![bundle load]) {
156 NSLog(@"Cannot load adaptor bundle '%@'", adaptorName);
158 [[[[InvalidAdaptorBundleException alloc]
159 initWithFormat:@"Cannot load adaptor bundle '%@'",
160 adaptorName] autorelease] raise];
165 /* Get the adaptor bundle "infoDictionary", and pricipal class, ie. the
166 adaptor class. Other info about the adaptor should be put in the
167 bundle's "Info.plist" file (property list format - see NSBundle class
168 documentation for details about reserved keys in this dictionary
169 property list containing one entry whose key is adaptorClassName. It
170 identifies the actual adaptor class from the bundle. */
172 adaptorClass = [bundle principalClass];
173 if (adaptorClass == Nil) {
174 NSLog(@"The adaptor bundle '%@' at '%@' doesn't contain "
175 @"a principal class (infoDict=%@)",
176 adaptorName, [bundle bundlePath], [bundle infoDictionary]);
178 [[[InvalidAdaptorBundleException alloc]
179 initWithFormat:@"The adaptor bundle '%@' doesn't contain "
180 @"a principal class (infoDict=%@)",
181 adaptorName, [bundle infoDictionary]]
184 return AUTORELEASE([[adaptorClass alloc] initWithName:adaptorName]);
187 + (NSString *)adaptorNameForURLScheme:(NSString *)_scheme {
188 // TODO: map scheme to adaptors (eg 'postgresql://' to PostgreSQL
190 // TODO: hack in some known names
191 if ([_scheme isEqualToString:@"postgresql"]) return @"PostgreSQL";
192 if ([_scheme isEqualToString:@"sybase"]) return @"Sybase10";
193 if ([_scheme isEqualToString:@"sqlite"]) return @"SQLite3";
195 if ([_scheme isEqualToString:@"http"]) {
196 NSLog(@"WARNING(%s): asked for 'http' URL, "
197 @"please fix the URLs to 'postgresql'!",
198 __PRETTY_FUNCTION__);
199 return @"PostgreSQL";
205 - (NSDictionary *)connectionDictionaryForNSURL:(NSURL *)_url {
210 postgresql://[user]:[password]@[host]:[port]/[dbname]/[tablename]
212 NSMutableDictionary *md;
215 md = [NSMutableDictionary dictionaryWithCapacity:8];
217 if ((tmp = [_url host]) != nil)
218 [md setObject:tmp forKey:@"hostName"];
219 if ((tmp = [_url port]) != nil)
220 [md setObject:tmp forKey:@"port"];
221 if ((tmp = [_url user]) != nil)
222 [md setObject:tmp forKey:@"userName"];
223 if ((tmp = [_url password]) != nil)
224 [md setObject:tmp forKey:@"password"];
229 if ([tmp hasPrefix:@"/"]) tmp = [tmp substringFromIndex:1];
230 if ([tmp length] > 0) {
233 r = [tmp rangeOfString:@"/"];
234 if (r.length > 0) tmp = [tmp substringToIndex:r.location];
236 if ([tmp length] > 0)
237 [md setObject:tmp forKey:@"databaseName"];
243 + (id)adaptorForNSURL:(NSURL *)_url {
245 NSString *adaptorName;
246 NSDictionary *condict;
251 adaptorName = [self adaptorNameForURLScheme:[_url scheme]];
252 adaptor = [self adaptorWithName:adaptorName];
254 if ((condict = [adaptor connectionDictionaryForNSURL:_url]) != nil)
255 [adaptor setConnectionDictionary:condict];
260 + (id)adaptorForURL:(id)_url {
266 if ([_url isKindOfClass:[NSURL class]])
268 else if ((url = [NSURL URLWithString:[_url stringValue]]) == nil) {
269 NSLog(@"ERROR(%s): could not parse URL: '%@'", __PRETTY_FUNCTION__, _url);
273 return [self adaptorForNSURL:url];
276 - (id)initWithName:(NSString*)_name {
277 ASSIGN(self->name, _name);
278 self->contexts = [[NSMutableArray allocWithZone:[self zone]] init];
283 RELEASE(self->model);
285 RELEASE(self->connectionDictionary);
286 RELEASE(self->pkeyGeneratorDictionary);
287 RELEASE(self->contexts);
293 - (void)setConnectionDictionary:(NSDictionary*)_dictionary {
294 if([self hasOpenChannels]) {
295 [NSException raise:NSInvalidArgumentException
296 format:@"Cannot set the connection dictionary "
297 @"while the adaptor is connected!"];
299 ASSIGN(self->connectionDictionary, _dictionary);
300 [self->model setConnectionDictionary:_dictionary];
302 - (NSDictionary *)connectionDictionary {
303 return self->connectionDictionary;
305 - (BOOL)hasValidConnectionDictionary {
309 - (EOAdaptorContext*)createAdaptorContext {
310 return AUTORELEASE([[[self adaptorContextClass] alloc]
311 initWithAdaptor:self]);
313 - (NSArray *)contexts {
317 if ((count = [self->contexts count]) == 0)
320 ma = [NSMutableArray arrayWithCapacity:count];
321 for (i = 0; i < count; i++)
322 [ma addObject:[[self->contexts objectAtIndex:i] nonretainedObjectValue]];
327 /* Setting pkey generation info */
329 - (void)setPkeyGeneratorDictionary:(NSDictionary*)aDictionary {
330 ASSIGN(self->pkeyGeneratorDictionary, aDictionary);
331 [self->model setPkeyGeneratorDictionary:aDictionary];
333 - (NSDictionary*)pkeyGeneratorDictionary {
334 return self->pkeyGeneratorDictionary;
339 - (void)contextDidInit:aContext {
340 [self->contexts addObject:[NSValue valueWithNonretainedObject:aContext]];
343 - (void)contextWillDealloc:aContext {
346 for (i = [contexts count]-1; i >= 0; i--) {
347 if ([[contexts objectAtIndex:i] nonretainedObjectValue] == aContext) {
348 [contexts removeObjectAtIndex:i];
354 - (BOOL)hasOpenChannels {
357 for (i = [contexts count] - 1; i >= 0; i--) {
358 EOAdaptorContext* ctx = [[contexts objectAtIndex:i]
359 nonretainedObjectValue];
360 if ([ctx hasOpenChannels])
366 - (id)formatAttribute:(EOAttribute *)_attribute {
367 return [_attribute expressionValueForContext:nil];
370 - (id)formatValue:(id)_value forAttribute:(EOAttribute *)attribute {
371 if (_value == nil) _value = [NSNull null];
372 return [_value expressionValueForContext:nil];
375 - (void)reportError:(NSString*)error {
376 if(delegateWillReportError) {
377 if([delegate adaptor:self willReportError:error] == NO)
380 NSLog(@"%@ adaptor error: %@", name, error);
383 - (void)setDelegate:(id)_delegate {
384 self->delegate = _delegate;
385 self->delegateWillReportError
386 = [self->delegate respondsToSelector:
387 @selector(adaptor:willReportError:)];
390 - (void)setModel:(EOModel *)_model {
391 ASSIGN(self->model, _model);
392 [self setConnectionDictionary:[_model connectionDictionary]];
393 [self setPkeyGeneratorDictionary:[_model pkeyGeneratorDictionary]];
403 - (Class)expressionClass {
404 return [EOSQLExpression class];
406 - (Class)adaptorContextClass {
407 return [EOAdaptorContext class];
409 - (Class)adaptorChannelClass {
410 return [EOAdaptorChannel class];
413 - (BOOL)attributeAllowedInDistinctSelects:(EOAttribute *)_attr {
416 - (BOOL)isValidQualifierType:(NSString*)typeName {
420 return self->delegate;
425 - (NSString *)description {
426 return [NSString stringWithFormat:
427 @"<%@[0x%08X]: name=%@ "
428 @"model=%s connection-dict=%s>",
429 NSStringFromClass([self class]), self,
431 [self model] ? "yes" : "no",
432 [self connectionDictionary] ? "yes" : "no"];
437 @implementation EOAdaptor(EOF2Additions)
439 - (BOOL)canServiceModel:(EOModel *)_model {