/* EOAttributeOrdering.m Copyright (C) 1996 Free Software Foundation, Inc. Author: Ovidiu Predescu Date: 1996 This file is part of the GNUstep Database Library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "EOAdaptor.h" #include "EOAdaptorChannel.h" #include "EOAdaptorContext.h" #include "EOAttribute.h" #include "EOFExceptions.h" #include "EOModel.h" #include "EOSQLExpression.h" #include "common.h" @implementation EOAdaptor + (id)adaptorWithModel:(EOModel*)_model { /* Check first to see if the adaptor class exists in the running program by testing the existence of [_model adaptorClassName]. */ NSString *adaptorName = [_model adaptorName]; Class adaptorClass = NSClassFromString([_model adaptorClassName]); id adaptor; adaptor = adaptorClass ? AUTORELEASE([[adaptorClass alloc] initWithName:adaptorName]) : [self adaptorWithName:adaptorName]; [adaptor setModel:_model]; return adaptor; } + (NSArray *)adaptorSearchPathes { // TODO: add support for Cocoa static NSArray *searchPathes = nil; NSDictionary *env; NSMutableArray *ma; id tmp; if (searchPathes != nil) return searchPathes; env = [[NSProcessInfo processInfo] environment]; ma = [NSMutableArray arrayWithCapacity:8]; if ((tmp = [env objectForKey:@"GNUSTEP_PATHPREFIX_LIST"]) == nil) tmp = [env objectForKey:@"GNUSTEP_PATHLIST"]; tmp = [tmp componentsSeparatedByString:@":"]; if ([tmp count] > 0) { NSEnumerator *e; e = [tmp objectEnumerator]; while ((tmp = [e nextObject])) { if (![tmp hasSuffix:@"/"]) tmp = [tmp stringByAppendingString:@"/"]; tmp = [tmp stringByAppendingFormat:@"Library/GDLAdaptors-%i.%i", GDL_MAJOR_VERSION, GDL_MINOR_VERSION]; if ([ma containsObject:tmp] || tmp == nil) continue; [ma addObject:tmp]; } } tmp = [NSString stringWithFormat: #if CONFIGURE_64BIT @"/lib64/sope-%i.%i/dbadaptors", #else @"/lib/sope-%i.%i/dbadaptors", #endif SOPE_MAJOR_VERSION, SOPE_MINOR_VERSION]; #ifdef FHS_INSTALL_ROOT [ma addObject:[FHS_INSTALL_ROOT stringByAppendingPathComponent:tmp]]; #endif [ma addObject:[@"/usr/local" stringByAppendingString:tmp]]; [ma addObject:[@"/usr" stringByAppendingString:tmp]]; searchPathes = [ma copy]; if ([searchPathes count] == 0) NSLog(@"%s: empty library search path !", __PRETTY_FUNCTION__); return searchPathes; } + (id)adaptorWithName:(NSString *)adaptorName { int i, count; NSBundle *bundle; NSString *adaptorBundlePath = nil; Class adaptorClass = Nil; bundle = [NSBundle mainBundle]; /* Check error */ if ((adaptorName == nil) || [adaptorName isEqual:@""]) return nil; /* Look in application bundle */ bundle = [NSBundle mainBundle]; adaptorBundlePath = [bundle pathForResource:adaptorName ofType:@"gdladaptor"]; /* Look in standard paths */ if (adaptorBundlePath == nil) { NSFileManager *fm; NSString *dirname; NSArray *paths; fm = [NSFileManager defaultManager]; paths = [self adaptorSearchPathes]; dirname = [adaptorName stringByAppendingPathExtension:@"gdladaptor"]; /* Loop through the paths and check each one */ for (i = 0, count = [paths count]; i < count; i++) { NSString *p; BOOL isDir; p = [[paths objectAtIndex:i] stringByAppendingPathComponent:dirname]; if (![fm fileExistsAtPath:p isDirectory:&isDir]) continue; if (!isDir) continue; adaptorBundlePath = p; break; } } /* Make adaptor bundle */ bundle = adaptorBundlePath ? [NSBundle bundleWithPath:adaptorBundlePath] : (NSBundle *)nil; /* Check bundle */ if (bundle == nil) { NSLog(@"EOAdaptor: cannot find adaptor bundle: '%@'", adaptorName); #if 0 [[[[CannotFindAdaptorBundleException alloc] initWithFormat:@"Cannot find adaptor bundle '%@'", adaptorName] autorelease] raise]; #endif return nil; } /* load bundle */ if (![bundle load]) { NSLog(@"Cannot load adaptor bundle '%@'", adaptorName); #if 1 [[[[InvalidAdaptorBundleException alloc] initWithFormat:@"Cannot load adaptor bundle '%@'", adaptorName] autorelease] raise]; #endif return nil; } /* Get the adaptor bundle "infoDictionary", and pricipal class, ie. the adaptor class. Other info about the adaptor should be put in the bundle's "Info.plist" file (property list format - see NSBundle class documentation for details about reserved keys in this dictionary property list containing one entry whose key is adaptorClassName. It identifies the actual adaptor class from the bundle. */ adaptorClass = [bundle principalClass]; if (adaptorClass == Nil) { NSLog(@"The adaptor bundle '%@' at '%@' doesn't contain " @"a principal class (infoDict=%@)", adaptorName, [bundle bundlePath], [bundle infoDictionary]); [[[InvalidAdaptorBundleException alloc] initWithFormat:@"The adaptor bundle '%@' doesn't contain " @"a principal class (infoDict=%@)", adaptorName, [bundle infoDictionary]] raise]; } return AUTORELEASE([[adaptorClass alloc] initWithName:adaptorName]); } + (NSString *)adaptorNameForURLScheme:(NSString *)_scheme { // TODO: map scheme to adaptors (eg 'postgresql://' to PostgreSQL // TODO: hack in some known names if ([_scheme isEqualToString:@"postgresql"]) return @"PostgreSQL"; if ([_scheme isEqualToString:@"sybase"]) return @"Sybase10"; if ([_scheme isEqualToString:@"sqlite"]) return @"SQLite3"; if ([_scheme isEqualToString:@"oracle"]) return @"Oracle8"; if ([_scheme isEqualToString:@"mysql"]) return @"MySQL"; if ([_scheme isEqualToString:@"http"]) { NSLog(@"WARNING(%s): asked for 'http' URL, " @"please fix the URLs to 'postgresql'!", __PRETTY_FUNCTION__); return @"PostgreSQL"; } return _scheme; } - (NSDictionary *)connectionDictionaryForNSURL:(NSURL *)_url { /* "Database URLs" We use the schema: postgresql://[user]:[password]@[host]:[port]/[dbname]/[tablename] */ NSMutableDictionary *md; id tmp; md = [NSMutableDictionary dictionaryWithCapacity:8]; if ((tmp = [_url host]) != nil) [md setObject:tmp forKey:@"hostName"]; if ((tmp = [_url port]) != nil) [md setObject:tmp forKey:@"port"]; if ((tmp = [_url user]) != nil) [md setObject:tmp forKey:@"userName"]; if ((tmp = [_url password]) != nil) [md setObject:tmp forKey:@"password"]; /* extract dbname */ tmp = [_url path]; if ([tmp hasPrefix:@"/"]) tmp = [tmp substringFromIndex:1]; if ([tmp length] > 0) { NSRange r; r = [tmp rangeOfString:@"/"]; if (r.length > 0) tmp = [tmp substringToIndex:r.location]; if ([tmp length] > 0) [md setObject:tmp forKey:@"databaseName"]; } return md; } + (id)adaptorForNSURL:(NSURL *)_url { EOAdaptor *adaptor; NSString *adaptorName; NSDictionary *condict; if (_url == nil) return nil; adaptorName = [self adaptorNameForURLScheme:[_url scheme]]; adaptor = [self adaptorWithName:adaptorName]; if ((condict = [adaptor connectionDictionaryForNSURL:_url]) != nil) [adaptor setConnectionDictionary:condict]; return adaptor; } + (id)adaptorForURL:(id)_url { NSURL *url; if (_url == nil) return nil; if ([_url isKindOfClass:[NSURL class]]) url = _url; else if ((url = [NSURL URLWithString:[_url stringValue]]) == nil) { NSLog(@"ERROR(%s): could not parse URL: '%@'", __PRETTY_FUNCTION__, _url); return nil; } return [self adaptorForNSURL:url]; } - (id)initWithName:(NSString*)_name { ASSIGN(self->name, _name); self->contexts = [[NSMutableArray allocWithZone:[self zone]] init]; return self; } - (void)dealloc { RELEASE(self->model); RELEASE(self->name); RELEASE(self->connectionDictionary); RELEASE(self->pkeyGeneratorDictionary); RELEASE(self->contexts); [super dealloc]; } /* accessors */ - (void)setConnectionDictionary:(NSDictionary*)_dictionary { if([self hasOpenChannels]) { [NSException raise:NSInvalidArgumentException format:@"Cannot set the connection dictionary " @"while the adaptor is connected!"]; } ASSIGN(self->connectionDictionary, _dictionary); [self->model setConnectionDictionary:_dictionary]; } - (NSDictionary *)connectionDictionary { return self->connectionDictionary; } - (BOOL)hasValidConnectionDictionary { return NO; } - (EOAdaptorContext*)createAdaptorContext { return AUTORELEASE([[[self adaptorContextClass] alloc] initWithAdaptor:self]); } - (NSArray *)contexts { NSMutableArray *ma; unsigned i, count; if ((count = [self->contexts count]) == 0) return nil; ma = [NSMutableArray arrayWithCapacity:count]; for (i = 0; i < count; i++) [ma addObject:[[self->contexts objectAtIndex:i] nonretainedObjectValue]]; return ma; } /* Setting pkey generation info */ - (void)setPkeyGeneratorDictionary:(NSDictionary*)aDictionary { ASSIGN(self->pkeyGeneratorDictionary, aDictionary); [self->model setPkeyGeneratorDictionary:aDictionary]; } - (NSDictionary*)pkeyGeneratorDictionary { return self->pkeyGeneratorDictionary; } // notifications - (void)contextDidInit:aContext { [self->contexts addObject:[NSValue valueWithNonretainedObject:aContext]]; } - (void)contextWillDealloc:aContext { int i; for (i = [contexts count]-1; i >= 0; i--) { if ([[contexts objectAtIndex:i] nonretainedObjectValue] == aContext) { [contexts removeObjectAtIndex:i]; break; } } } - (BOOL)hasOpenChannels { int i; for (i = [contexts count] - 1; i >= 0; i--) { EOAdaptorContext* ctx = [[contexts objectAtIndex:i] nonretainedObjectValue]; if ([ctx hasOpenChannels]) return YES; } return NO; } - (id)formatAttribute:(EOAttribute *)_attribute { return [_attribute expressionValueForContext:nil]; } - (id)formatValue:(id)_value forAttribute:(EOAttribute *)attribute { if (_value == nil) _value = [NSNull null]; return [_value expressionValueForContext:nil]; } - (void)reportError:(NSString*)error { if(delegateWillReportError) { if([delegate adaptor:self willReportError:error] == NO) return; } NSLog(@"%@ adaptor error: %@", name, error); } - (void)setDelegate:(id)_delegate { self->delegate = _delegate; self->delegateWillReportError = [self->delegate respondsToSelector: @selector(adaptor:willReportError:)]; } - (void)setModel:(EOModel *)_model { ASSIGN(self->model, _model); [self setConnectionDictionary:[_model connectionDictionary]]; [self setPkeyGeneratorDictionary:[_model pkeyGeneratorDictionary]]; } - (EOModel *)model { return self->model; } - (NSString *)name { return self->name; } - (Class)expressionClass { return [EOSQLExpression class]; } - (Class)adaptorContextClass { return [EOAdaptorContext class]; } - (Class)adaptorChannelClass { return [EOAdaptorChannel class]; } - (BOOL)attributeAllowedInDistinctSelects:(EOAttribute *)_attr { return YES; } - (BOOL)isValidQualifierType:(NSString*)typeName { return NO; } - (id)delegate { return self->delegate; } // description - (NSString *)description { return [NSString stringWithFormat: @"<%@[0x%p]: name=%@ " @"model=%s connection-dict=%s>", NSStringFromClass([self class]), self, [self name], [self model] ? "yes" : "no", [self connectionDictionary] ? "yes" : "no"]; } @end /* EOAdaptor */ @implementation EOAdaptor(EOF2Additions) - (BOOL)canServiceModel:(EOModel *)_model { return YES; } @end