X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=blobdiff_plain;ds=sidebyside;f=sope-core%2FNGExtensions%2FNGBundleManager.m;h=61113ff3eb53d6d691c8766d7e279aa61bd91514;hb=da4fee939ad67e8da731714f06358d6bdd3ee617;hp=cf04d95004219432b27da47bc3282ad6b667e6e5;hpb=b572f22aee813b0c74127bd7046db4b30cef7158;p=sope diff --git a/sope-core/NGExtensions/NGBundleManager.m b/sope-core/NGExtensions/NGBundleManager.m index cf04d950..61113ff3 100644 --- a/sope-core/NGExtensions/NGBundleManager.m +++ b/sope-core/NGExtensions/NGBundleManager.m @@ -1,26 +1,29 @@ /* - Copyright (C) 2000-2004 SKYRIX Software AG + Copyright (C) 2000-2006 SKYRIX Software AG + Copyright (C) 2006 Helge Hess - This file is part of OpenGroupware.org. + This file is part of SOPE. - OGo is free software; you can redistribute it and/or modify it under + SOPE is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. - OGo is distributed in the hope that it will be useful, but WITHOUT ANY + SOPE 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public - License along with OGo; see the file COPYING. If not, write to the + License along with SOPE; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "NGBundleManager.h" #include "common.h" +#include +#include #import #import #include @@ -47,8 +50,10 @@ //OBJC_EXPORT void objc_setClassHandler(int (*)(const char *)); static BOOL debugClassHook = NO; +static BOOL hookDoLookup = YES; static int _getClassHook(const char *className) { + // Cocoa variant if (className == NULL) return 0; if (debugClassHook) @@ -57,18 +62,21 @@ static int _getClassHook(const char *className) { if (objc_lookUpClass(className)) return 1; - { + if (hookDoLookup) { static NGBundleManager *manager = nil; NSBundle *bundle; + NSString *cns; if (debugClassHook) - NSLog(@"%s: look for class %s", __PRETTY_FUNCTION__, className); + printf("%s: look for class %s\n", __PRETTY_FUNCTION__, className); if (manager == nil) manager = [NGBundleManager defaultBundleManager]; - bundle = [manager bundleForClassNamed: - [NSString stringWithCString:className]]; - if (bundle) { + cns = [[NSString alloc] initWithCString:className]; + bundle = [manager bundleForClassNamed:cns]; + [cns release]; cns = nil; + + if (bundle != nil) { if (debugClassHook) { NSLog(@"%s: found bundle %@", __PRETTY_FUNCTION__, [bundle bundlePath]); @@ -124,7 +132,7 @@ static Class _classLoadHook(const char *_name) { manager = [NGBundleManager defaultBundleManager]; bundle = [manager bundleForClassNamed:[NSString stringWithCString:_name]]; - if (bundle) { + if (bundle != nil) { #if 0 NSLog(@"%s: found bundle %@", __PRETTY_FUNCTION__, [bundle bundlePath]); #endif @@ -246,9 +254,7 @@ static NSString *NGEnvVarPathSeparator = @":"; objc_setClassHandler(_getClassHook); } } -#endif - -#if GNU_RUNTIME +#elif GNU_RUNTIME if (_objc_lookup_class != _classLoadHook) { oldClassLoadHook = _objc_lookup_class; _objc_lookup_class = _classLoadHook; @@ -375,9 +381,9 @@ static NSString *NGEnvVarPathSeparator = @":"; - (void)_registerLoadedBundles { NSEnumerator *currentBundles; NSBundle *loadedBundle; - + currentBundles = [[NSBundle allBundles] objectEnumerator]; - while ((loadedBundle = [currentBundles nextObject])) + while ((loadedBundle = [currentBundles nextObject]) != nil) [self registerBundle:loadedBundle classes:nil categories:nil]; } @@ -466,16 +472,35 @@ static NSString *NGEnvVarPathSeparator = @":"; NSEnumerator *e; id v; - //NSLog(@"NGBundleManager: register loaded bundle %@", [_bundle bundlePath]); - +#if NeXT_RUNTIME || APPLE_RUNTIME + v = [_bundle bundlePath]; + if ([v hasSuffix:@"Libraries"] || [v hasSuffix:@"Tools"]) { + if (debugOn) + fprintf(stderr, "INVALID BUNDLE: %s\n", [[_bundle bundlePath] cString]); + return; + } +#endif + +#if 0 + NSLog(@"NGBundleManager: register loaded bundle %@", [_bundle bundlePath]); +#endif + e = [_classes objectEnumerator]; - while ((v = [e nextObject])) { + while ((v = [e nextObject]) != nil) { +#if NeXT_RUNTIME || APPLE_RUNTIME + hookDoLookup = NO; +#endif + NSMapInsert(self->classToBundle, NSClassFromString(v), _bundle); NSMapInsert(self->classNameToBundle, v, _bundle); + +#if NeXT_RUNTIME || APPLE_RUNTIME + hookDoLookup = YES; +#endif } - + e = [_categories objectEnumerator]; - while ((v = [e nextObject])) + while ((v = [e nextObject]) != nil) NSMapInsert(self->categoryNameToBundle, v, _bundle); } @@ -527,14 +552,38 @@ static NSString *NGEnvVarPathSeparator = @":"; - (NSBundle *)bundleForClass:(Class)aClass { /* this method never loads a dynamic bundle (since the class is set up) */ NSBundle *bundle; - + + if (aClass == Nil) + return nil; + bundle = NSMapGet(self->classToBundle, aClass); #if NeXT_Foundation_LIBRARY || COCOA_Foundation_LIBRARY - if (bundle == nil){ + if (bundle == nil) { + NSString *p; + bundle = [NSBundle bundleForClass:aClass]; if (bundle == [NSBundle mainBundle]) bundle = nil; + else { + p = [bundle bundlePath]; + if ([p hasSuffix:@"Libraries"]) { + if (debugOn) { + fprintf(stderr, "%s: Dylib bundle: 0x%p: %s\n", + __PRETTY_FUNCTION__, + bundle, [[bundle bundlePath] cString]); + } + bundle = nil; + } + else if ([p hasSuffix:@"Tools"]) { + if (debugOn) { + fprintf(stderr, "%s: Tool bundle: 0x%p: %s\n", + __PRETTY_FUNCTION__, + bundle, [[bundle bundlePath] cString]); + } + bundle = nil; + } + } } #endif if (bundle == nil) { @@ -544,9 +593,12 @@ static NSString *NGEnvVarPathSeparator = @":"; */ #if !LIB_FOUNDATION_LIBRARY && !GNUSTEP_BASE_LIBRARY -# warning incorrect behaviour if NGExtensions is dynamically loaded ! + // Note: incorrect behaviour if NGExtensions is dynamically loaded ! // TODO: can we do anything about this? Can we detect the situation and // print a log instead of the compile warning? + // Note: the above refers to the situation when a framework is implicitly + // loaded by loading a bundle (the framework is not linked against + // the main tool) #endif bundle = [NSBundle mainBundle]; NSMapInsert(self->classToBundle, aClass, bundle); @@ -591,12 +643,18 @@ static NSString *NGEnvVarPathSeparator = @":"; bn = [_name stringByAppendingPathExtension:_type]; bundle = NSMapGet(self->nameToBundle, bn); - if (bundle == nil) - bundle = [self bundleWithPath:[self pathForBundleWithName:_name type:_type]]; - + if (![bundle isNotNull]) { + bundle = [self bundleWithPath: + [self pathForBundleWithName:_name type:_type]]; + } + + if (![bundle isNotNull]) /* NSNull is used to signal missing bundles */ + return nil; + if (![[bundle bundleType] isEqualToString:_type]) - bundle = nil; - + return nil; + + /* bundle matches */ return bundle; } - (NSBundle *)bundleWithName:(NSString *)_name { @@ -606,13 +664,13 @@ static NSString *NGEnvVarPathSeparator = @":"; - (NSBundle *)bundleForClassNamed:(NSString *)_className { NSString *path = nil; NSBundle *bundle = nil; - + if (_className == nil) return nil; /* first check in table */ - if ((bundle = NSMapGet(self->classNameToBundle, _className))) + if ((bundle = NSMapGet(self->classNameToBundle, _className)) != nil) return bundle; #if GNU_RUNTIME @@ -622,18 +680,39 @@ static NSString *NGEnvVarPathSeparator = @":"; void *loadCallback; Class clazz; - loadCallback = _objc_lookup_class; _objc_lookup_class = NULL; clazz = NSClassFromString(_className); _objc_lookup_class = loadCallback; - if (clazz) { + if (clazz != Nil) { + /* the class is already loaded */ + bundle = [self bundleForClass:clazz]; + NSMapInsert(self->classNameToBundle, _className, bundle); + return bundle; + } + } +#elif NeXT_RUNTIME || APPLE_RUNTIME + { + Class clazz; + + hookDoLookup = NO; // THREAD + clazz = NSClassFromString(_className); + hookDoLookup = YES; + + if (clazz != Nil) { /* the class is already loaded */ +#if 0 + printf("found class in runtime: %s\n", [_className cString]); +#endif bundle = [self bundleForClass:clazz]; NSMapInsert(self->classNameToBundle, _className, bundle); return bundle; } +#if 0 + else + printf("did NOT find class in runtime: %s\n", [_className cString]); +#endif } #endif @@ -641,7 +720,7 @@ static NSString *NGEnvVarPathSeparator = @":"; ofType:@"classes" resourceSelector:_selectClassByVersion context:NULL /* version */]; - if (path) { + if (path != nil) { path = [path stringByResolvingSymlinksInPath]; NSAssert(path, @"couldn't resolve symlinks in path .."); } @@ -649,7 +728,7 @@ static NSString *NGEnvVarPathSeparator = @":"; if (path == nil) return nil; - if ((bundle = [self bundleWithPath:path])) + if ((bundle = [self bundleWithPath:path]) != nil) NSMapInsert(self->classNameToBundle, _className, bundle); return bundle; @@ -667,15 +746,14 @@ static NSString *NGEnvVarPathSeparator = @":"; } - (NSArray *)classesProvidedByBundle:(NSBundle *)_bundle { - [self doesNotRecognizeSelector:_cmd]; - return nil; + return [[_bundle providedResourcesOfType:@"classes"] valueForKey:@"name"]; } - (NSArray *)classesRequiredByBundle:(NSBundle *)_bundle { [self doesNotRecognizeSelector:_cmd]; return nil; } -// initialization +/* initialization */ - (NSString *)makeBundleInfoPath:(NSString *)_path { #if (NeXT_Foundation_LIBRARY || APPLE_Foundation_LIBRARY) && !defined(GSWARN) @@ -690,17 +768,20 @@ static NSString *NGEnvVarPathSeparator = @":"; - (id)_initializeLoadedBundle:(NSBundle *)_bundle info:(NSDictionary *)_bundleInfo { - id handler = nil; + id handler; /* check whether a handler was specified */ - - if ((handler = [_bundleInfo objectForKey:@"bundleHandler"])) { + + if ((handler = [_bundleInfo objectForKey:@"bundleHandler"]) != nil) { + [self debugWithFormat:@"lookup bundle handler %@ of bundle: %@", + handler, _bundle]; + if ((handler = NSClassFromString(handler)) == nil) { NSLog(@"ERROR: did not find handler class %@ of bundle %@.", [_bundleInfo objectForKey:@"bundleHandler"], [_bundle bundlePath]); handler = [_bundle principalClass]; } - + handler = [handler alloc]; if ([handler respondsToSelector:@selector(initForBundle:bundleManager:)]) @@ -716,9 +797,18 @@ static NSString *NGEnvVarPathSeparator = @":"; } } else { - if ((handler = [_bundle principalClass]) == nil) + [self debugWithFormat: + @"no bundle handler, lookup principal class of bundle: %@", + _bundle]; + if ((handler = [_bundle principalClass]) == nil) { /* use NGBundle class as default bundle handler */ +#if !(NeXT_Foundation_LIBRARY || COCOA_Foundation_LIBRARY) + [self warnWithFormat:@"bundle has no principal class: %@", _bundle]; +#endif handler = [NGBundle class]; + } + else + [self debugWithFormat:@" => %@", handler]; } return handler; @@ -736,7 +826,7 @@ static NSString *NGEnvVarPathSeparator = @":"; bundleInfo = [NSDictionary dictionaryWithContentsOfFile:_path]; #endif if (bundleInfo == nil) { - NSLog(@"couldn't load bundle-info at path '%@' !", _path); + NSLog(@"could not load bundle-info at path '%@' !", _path); return nil; } @@ -763,6 +853,16 @@ static NSString *NGEnvVarPathSeparator = @":"; return nil; } + // TODO: do we need to check the runtime for already loaded classes? + // Yes, I think so. But avoid recursions +#if 0 +#if APPLE_Foundation_LIBRARY || COCOA_Foundation_LIBRARY + // TODO: HACK, see above. w/o this, we get issues. + if ([className hasPrefix:@"NS"]) + return nil; +#endif +#endif + if ((bundle = [self bundleForClassNamed:className]) == nil) { #if 0 // class might be already loaded NSLog(@"ERROR: did not find class %@ required by bundle %@.", @@ -770,6 +870,9 @@ static NSString *NGEnvVarPathSeparator = @":"; #endif } + if (debugOn) + NSLog(@"CLASS %@ => BUNDLE %@", className, bundle); + return bundle; } - (NSArray *)_locateBundlesForClassInfos:(NSEnumerator *)_classInfos { @@ -777,7 +880,7 @@ static NSString *NGEnvVarPathSeparator = @":"; NSDictionary *i; requiredBundles = [NSMutableArray arrayWithCapacity:16]; - while ((i = [_classInfos nextObject])) { + while ((i = [_classInfos nextObject]) != nil) { NSBundle *bundle; if ((bundle = [self _locateBundleForClassInfo:i]) == nil) @@ -790,9 +893,11 @@ static NSString *NGEnvVarPathSeparator = @":"; - (BOOL)_preLoadBundle:(NSBundle *)_bundle info:(NSDictionary *)_bundleInfo { /* TODO: split up this huge method */ - NSDictionary *requires = nil; + NSDictionary *requires; NSMutableArray *requiredBundles = nil; NSBundle *requiredBundle = nil; + + if (debugOn) NSLog(@"NGBundleManager: preload bundle: %@", _bundle); requires = [_bundleInfo objectForKey:@"requires"]; @@ -808,7 +913,7 @@ static NSString *NGEnvVarPathSeparator = @":"; /* locate required bundles */ e = [[requires objectForKey:@"bundles"] objectEnumerator]; - while ((i = [e nextObject])) { + while ((i = [e nextObject]) != nil) { NSString *bundleName; if (![i respondsToSelector:@selector(objectForKey:)]) { @@ -841,18 +946,24 @@ static NSString *NGEnvVarPathSeparator = @":"; NSLog(@"ERROR: error in bundle-info.plist of bundle %@", _bundle); } } - + /* load located bundles */ { NSEnumerator *e; + if (debugOn) { + NSLog(@"NGBundleManager: preload required bundles: %@", + requiredBundles); + } + e = [requiredBundles objectEnumerator]; - while ((requiredBundle = [e nextObject])) { + while ((requiredBundle = [e nextObject]) != nil) { Class bundleMaster; if ((bundleMaster = [self loadBundle:requiredBundle]) == Nil) { NSLog(@"ERROR: could not load bundle %@ (%@) required by bundle %@.", - [requiredBundle bundlePath], requiredBundle, [_bundle bundlePath]); + [requiredBundle bundlePath], requiredBundle, + [_bundle bundlePath]); continue; } } @@ -864,23 +975,25 @@ static NSString *NGEnvVarPathSeparator = @":"; NSArray *reqClasses; reqClasses = [requires objectForKey:@"classes"]; + bundles = [self _locateBundlesForClassInfos:[reqClasses objectEnumerator]]; if (requiredBundles == nil) requiredBundles = [NSMutableArray arrayWithCapacity:16]; [requiredBundles addObjectsFromArray:bundles]; } - + /* load located bundles */ { NSEnumerator *e; e = [requiredBundles objectEnumerator]; - while ((requiredBundle = [e nextObject])) { + while ((requiredBundle = [e nextObject]) != nil) { Class bundleMaster; if ((bundleMaster = [self loadBundle:requiredBundle]) == Nil) { NSLog(@"ERROR: could not load bundle %@ (%@) required by bundle %@.", - [requiredBundle bundlePath], requiredBundle, [_bundle bundlePath]); + [requiredBundle bundlePath], requiredBundle, + [_bundle bundlePath]); continue; } } @@ -892,7 +1005,7 @@ static NSString *NGEnvVarPathSeparator = @":"; NSDictionary *i; e = [[requires objectForKey:@"classes"] objectEnumerator]; - while ((i = [e nextObject])) { + while ((i = [e nextObject]) != nil) { NSString *className; Class clazz; @@ -976,9 +1089,11 @@ static NSString *NGEnvVarPathSeparator = @":"; if (![self _preLoadBundle:_bundle info:bundleInfo]) goto done; - + + if (debugOn) NSLog(@"NGBundleManager: will load bundle: %@", _bundle); if (![_bundle _loadForBundleManager:self]) goto done; + if (debugOn) NSLog(@"NGBundleManager: did load bundle: %@", _bundle); if (![self _postLoadBundle:_bundle info:bundleInfo]) goto done; @@ -1149,84 +1264,169 @@ static BOOL _doesInfoMatch(NSArray *keys, NSDictionary *dict, NSDictionary *info NSFileManager *fm; NSEnumerator *e; NSString *path; + + if (debugOn) { + NSLog(@"BM LOOKUP pathes (%d bundles loaded): %@ / %@", + NSCountMapTable(self->loadedBundles), _resourceName, _type); + } fm = [NSFileManager defaultManager]; result = [NSMutableArray arrayWithCapacity:64]; + // TODO: look in loaded bundles + + /* check physical pathes */ + e = [self->bundleSearchPaths objectEnumerator]; - while ((path = [e nextObject])) { - BOOL isDir = NO; + while ((path = [e nextObject]) != nil) { + NSEnumerator *dir; + BOOL isDir = NO; + NSString *tmp, *bundleDirPath; + id info = nil; - if ([fm fileExistsAtPath:path isDirectory:&isDir]) { - NSString *tmp; - id info = nil; - if (!isDir) continue; - - /* check whether an appropriate bundle is contained in 'path' */ - { - NSEnumerator *dir; - - dir = [[fm directoryContentsAtPath:path] objectEnumerator]; - while ((tmp = [dir nextObject])) { - NSDictionary *bundleInfo = nil; - NSEnumerator *providedResources = nil; - NSString *infoPath; - id info; + if (![fm fileExistsAtPath:path isDirectory:&isDir]) + continue; + + if (!isDir) continue; + + /* check whether an appropriate bundle is contained in 'path' */ + + dir = [[fm directoryContentsAtPath:path] objectEnumerator]; + while ((bundleDirPath = [dir nextObject]) != nil) { + NSDictionary *bundleInfo = nil; + NSEnumerator *providedResources = nil; + NSString *infoPath; + id info; - tmp = [path stringByAppendingPathComponent:tmp]; - infoPath = [self makeBundleInfoPath:tmp]; - - if ((bundleInfo = NSMapGet(self->pathToBundleInfo, infoPath)) == nil) { - if (![fm fileExistsAtPath:infoPath]) - continue; - - bundleInfo = [self _loadBundleInfoAtExistingPath:infoPath]; - } - - providedResources = - [[(NSDictionary *)[bundleInfo objectForKey:@"provides"] - objectForKey:_type] - objectEnumerator]; - if (providedResources == nil) continue; + bundleDirPath = [path stringByAppendingPathComponent:bundleDirPath]; + infoPath = [self makeBundleInfoPath:bundleDirPath]; + + // TODO: can we use _doesBundleInfo:path:providedResource:... ? + if ((bundleInfo = NSMapGet(self->pathToBundleInfo, infoPath))==nil) { + if (![fm fileExistsAtPath:infoPath]) + continue; - // scan provide array - while ((info = [providedResources nextObject])) { - NSString *name; + bundleInfo = [self _loadBundleInfoAtExistingPath:infoPath]; + } + + providedResources = + [[(NSDictionary *)[bundleInfo objectForKey:@"provides"] + objectForKey:_type] + objectEnumerator]; + if (providedResources == nil) continue; + + /* scan 'provides' array */ + while ((info = [providedResources nextObject])) { + NSString *name; - name = [[(NSDictionary *)info objectForKey:@"name"] stringValue]; - if (name == nil) continue; - - if (_resourceName) { - if (![name isEqualToString:_resourceName]) - continue; - } - if (_selector) { - if (!_selector(name, _type, tmp, info, self, _context)) - continue; - } - - [result addObject:tmp]; - break; - } - } + name = [[(NSDictionary *)info objectForKey:@"name"] stringValue]; + if (name == nil) continue; + + if (_resourceName != nil) { + if (![name isEqualToString:_resourceName]) + continue; + } + if (_selector != NULL) { + if (!_selector(name, _type, bundleDirPath, info, self, _context)) + continue; + } + + [result addObject:bundleDirPath]; + break; } + } + + /* check for direct match (NGBundlePath element is a bundle) */ + + tmp = [self makeBundleInfoPath:path]; - /* check for direct match */ + if ((info = NSMapGet(self->pathToBundleInfo, tmp)) == nil) { + if ([fm fileExistsAtPath:tmp]) + info = [self _loadBundleInfoAtExistingPath:tmp]; + } + + [self _processInfoForProvidedResources:info ofType:_type path:path + resourceName:_resourceName resourceSelector:_selector + context:_context + andAddToResultArray:result]; + } + + if ([result count] == 0) { + [self logWithFormat: + @"Note(%s): method does not search in loaded bundles for " + @"resources of type '%@'", + __PRETTY_FUNCTION__, _type]; + } + + return [[result copy] autorelease]; +} - tmp = [self makeBundleInfoPath:path]; +- (BOOL)_doesBundleInfo:(NSDictionary *)_bundleInfo path:(NSString *)_path + provideResource:(id)_resourceName ofType:(NSString *)_type + rnKeys:(NSArray *)_rnKeys + resourceSelector:(NGBundleResourceSelector)_selector context:(void *)_context +{ + NSEnumerator *providedResources; + NSDictionary *info; + + providedResources = + [[(NSDictionary *)[_bundleInfo objectForKey:@"provides"] + objectForKey:_type] objectEnumerator]; + if (providedResources == nil) return NO; + + /* scan provide array */ + while ((info = [providedResources nextObject])) { + if (_rnKeys != nil) { + if (!_doesInfoMatch(_rnKeys, _resourceName, info)) + continue; + } + else { + NSString *name; - if ((info = NSMapGet(self->pathToBundleInfo, tmp)) == nil) { - if ([fm fileExistsAtPath:tmp]) - info = [self _loadBundleInfoAtExistingPath:tmp]; - } - - [self _processInfoForProvidedResources:info ofType:_type path:path - resourceName:_resourceName resourceSelector:_selector - context:_context - andAddToResultArray:result]; + name = [[(NSDictionary *)info objectForKey:@"name"] stringValue]; + if (name == nil) continue; + if (![name isEqualToString:_resourceName]) continue; + } + + if (_selector != NULL) { + if (!_selector(_resourceName, _type, _path, info, self, _context)) + continue; } + + /* all conditions applied (found) */ + return YES; } - return [[result copy] autorelease]; + return NO; +} + +- (NSString *)pathOfLoadedBundleProvidingResource:(id)_resourceName + ofType:(NSString *)_type + resourceSelector:(NGBundleResourceSelector)_selector context:(void *)_context +{ + NSMapEnumerator menum; + NSString *path; + NSDictionary *bundleInfo; + NSArray *rnKeys; + + rnKeys = ([_resourceName respondsToSelector:@selector(objectForKey:)]) + ? [_resourceName allKeys] + : (NSArray *)nil; + + menum = NSEnumerateMapTable(self->pathToBundleInfo); + while (NSNextMapEnumeratorPair(&menum, (void *)&path, (void *)&bundleInfo)) { + if (debugOn) { + NSLog(@"check loaded bundle for resource %@: %@", _resourceName, + path); + } + + if ([self _doesBundleInfo:bundleInfo path:path + provideResource:_resourceName ofType:_type rnKeys:rnKeys + resourceSelector:_selector context:_context]) + /* strip bundle-info.plist name */ + return [path stringByDeletingLastPathComponent]; + } + + return nil; } - (NSString *)pathForBundleProvidingResource:(id)_resourceName @@ -1234,99 +1434,87 @@ static BOOL _doesInfoMatch(NSArray *keys, NSDictionary *dict, NSDictionary *info resourceSelector:(NGBundleResourceSelector)_selector context:(void *)_context { - NSFileManager *fm = [NSFileManager defaultManager]; + /* main path lookup method */ + // TODO: this method seriously needs some refactoring + NSFileManager *fm; NSEnumerator *e; NSString *path; NSArray *rnKeys = nil; int rnKeyCount = 0; - + + if (debugOn) { + NSLog(@"BM LOOKUP path (%d bundles loaded): %@ / %@", + NSCountMapTable(self->loadedBundles), _resourceName, _type); + } + + /* look in loaded bundles */ + + path = [self pathOfLoadedBundleProvidingResource:_resourceName ofType:_type + resourceSelector:_selector context:_context]; + if (path != nil) return path; + + /* look in filesystem */ + if ([_resourceName respondsToSelector:@selector(objectForKey:)]) { rnKeys = [_resourceName allKeys]; rnKeyCount = [rnKeys count]; } + fm = [NSFileManager defaultManager]; e = [self->bundleSearchPaths objectEnumerator]; - while ((path = [e nextObject])) { - BOOL isDir = NO; + while ((path = [e nextObject]) != nil) { + NSEnumerator *dir; + BOOL isDir = NO; + NSString *tmp; + id info = nil; - if ([fm fileExistsAtPath:path isDirectory:&isDir]) { - NSString *tmp; - id info = nil; + if (![fm fileExistsAtPath:path isDirectory:&isDir]) + continue; + + if (!isDir) continue; + + /* check whether an appropriate bundle is contained in 'path' */ + + dir = [[fm directoryContentsAtPath:path] objectEnumerator]; + while ((tmp = [dir nextObject]) != nil) { + NSDictionary *bundleInfo = nil; + NSString *infoPath; - if (!isDir) continue; + tmp = [path stringByAppendingPathComponent:tmp]; + infoPath = [self makeBundleInfoPath:tmp]; - /* check whether an appropriate bundle is contained in 'path' */ - { - NSEnumerator *dir; - - dir = [[fm directoryContentsAtPath:path] objectEnumerator]; - while ((tmp = [dir nextObject])) { - NSDictionary *bundleInfo = nil; - NSEnumerator *providedResources = nil; - NSString *infoPath; - id info; - - tmp = [path stringByAppendingPathComponent:tmp]; - infoPath = [self makeBundleInfoPath:tmp]; - - if (debugOn) - NSLog(@"check path path=%@ info=%@", tmp, infoPath); - - if ((bundleInfo=NSMapGet(self->pathToBundleInfo, infoPath)) == nil) { - if (![fm fileExistsAtPath:infoPath]) - continue; - - bundleInfo = [self _loadBundleInfoAtExistingPath:infoPath]; - } - if (debugOn) { - NSLog(@"found info for path=%@ info=%@: %@", - tmp, infoPath, bundleInfo); - } + if (debugOn) + NSLog(@"check path path=%@ info=%@", tmp, infoPath); - providedResources = - [[(NSDictionary *)[bundleInfo objectForKey:@"provides"] - objectForKey:_type] - objectEnumerator]; - if (providedResources == nil) continue; + if ((bundleInfo = NSMapGet(self->pathToBundleInfo, infoPath)) == nil) { + if (![fm fileExistsAtPath:infoPath]) + continue; - // scan provide array - while ((info = [providedResources nextObject])) { - if (rnKeys) { - if (!_doesInfoMatch(rnKeys, _resourceName, info)) - continue; - } - else { - NSString *name; - - name = [[(NSDictionary *)info objectForKey:@"name"] stringValue]; - if (name == nil) continue; - if (![name isEqualToString:_resourceName]) continue; - } - - if (_selector) { - if (!_selector(_resourceName, _type, tmp, info, self, _context)) - continue; - } - /* all conditions applied */ - return tmp; - } - } + bundleInfo = [self _loadBundleInfoAtExistingPath:infoPath]; } - - /* check for direct match */ + if (debugOn) + NSLog(@"found info for path=%@ info=%@: %@", tmp,infoPath,bundleInfo); - tmp = [self makeBundleInfoPath:path]; + if ([self _doesBundleInfo:bundleInfo path:tmp + provideResource:_resourceName ofType:_type rnKeys:rnKeys + resourceSelector:_selector context:_context]) + return tmp; + } + + /* check for direct match */ + + tmp = [self makeBundleInfoPath:path]; - if ((info = NSMapGet(self->pathToBundleInfo, tmp)) == nil) { + if ((info = NSMapGet(self->pathToBundleInfo, tmp)) == nil) { if ([fm fileExistsAtPath:tmp]) info = [self _loadBundleInfoAtExistingPath:tmp]; else if (debugOn) { NSLog(@"WARNING(%s): did not find direct path '%@'", __PRETTY_FUNCTION__, tmp); } - } + } - if (info) { + if (info != nil) { // direct match (a bundle was specified in the path) NSEnumerator *providedResources; NSDictionary *provides; @@ -1357,28 +1545,29 @@ static BOOL _doesInfoMatch(NSArray *keys, NSDictionary *dict, NSDictionary *info /* all conditions applied */ return tmp; } - } } } return nil; } -- (NSBundle *)bundleProvidingResource:(id)_resourceName - ofType:(NSString *)_resourceType -{ +- (NSBundle *)bundleProvidingResource:(id)_name ofType:(NSString *)_type { NSString *bp; - bp = [self pathForBundleProvidingResource:_resourceName - ofType:_resourceType + if (debugOn) NSLog(@"BM LOOKUP: %@ / %@", _name, _type); + + bp = [self pathForBundleProvidingResource:_name + ofType:_type resourceSelector:NULL context:nil]; if ([bp length] == 0) { #if (NeXT_Foundation_LIBRARY || APPLE_Foundation_LIBRARY) && HEAVY_DEBUG NSLog(@"%s: found no resource '%@' of type '%@' ...", __PRETTY_FUNCTION__, _resourceName, _resourceType); #endif + if (debugOn) NSLog(@" did not find: %@ / %@", _name, _type); return nil; } + if (debugOn) NSLog(@" FOUND: %@", bp); return [self bundleWithPath:bp]; } @@ -1424,6 +1613,29 @@ static BOOL _doesInfoMatch(NSArray *keys, NSDictionary *dict, NSDictionary *info objectForKey:_resourceType]; } +- (void)_addRegisteredProvidedResourcesOfType:(NSString *)_type + toSet:(NSMutableSet *)_result +{ + NSMapEnumerator menum; + NSString *path; + NSDictionary *bundleInfo; + + menum = NSEnumerateMapTable(self->pathToBundleInfo); + while (NSNextMapEnumeratorPair(&menum, (void *)&path, (void *)&bundleInfo)) { + NSArray *providedResources; + + if (debugOn) + NSLog(@"check loaded bundle for resource types %@: %@", _type, path); + + providedResources = + [(NSDictionary *)[bundleInfo objectForKey:@"provides"] + objectForKey:_type]; + if (providedResources == nil) continue; + + [_result addObjectsFromArray:providedResources]; + } +} + - (NSArray *)providedResourcesOfType:(NSString *)_resourceType { NSMutableSet *result = nil; NSFileManager *fm = [NSFileManager defaultManager]; @@ -1432,67 +1644,74 @@ static BOOL _doesInfoMatch(NSArray *keys, NSDictionary *dict, NSDictionary *info result = [NSMutableSet setWithCapacity:128]; + /* scan loaded bundles */ + + [self _addRegisteredProvidedResourcesOfType:_resourceType toSet:result]; + + /* scan all bundle search paths */ + e = [self->bundleSearchPaths objectEnumerator]; - while ((path = [e nextObject])) { - BOOL isDir = NO; - - if ([fm fileExistsAtPath:path isDirectory:&isDir]) { - NSString *tmp; - id info = nil; - if (!isDir) continue; + while ((path = [e nextObject]) != nil) { + NSEnumerator *dir; + BOOL isDir = NO; + NSString *tmp; + id info = nil; - /* check whether an appropriate bundle is contained in 'path' */ - { - NSEnumerator *dir; + if (![fm fileExistsAtPath:path isDirectory:&isDir]) + continue; + if (!isDir) continue; - dir = [[fm directoryContentsAtPath:path] objectEnumerator]; - while ((tmp = [dir nextObject])) { - NSDictionary *bundleInfo = nil; - NSArray *providedResources = nil; - NSString *infoPath; + /* check whether an appropriate bundle is contained in 'path' */ + + // TODO: move to own method + dir = [[fm directoryContentsAtPath:path] objectEnumerator]; + while ((tmp = [dir nextObject]) != nil) { + NSDictionary *bundleInfo = nil; + NSArray *providedResources = nil; + NSString *infoPath; - tmp = [path stringByAppendingPathComponent:tmp]; - infoPath = [self makeBundleInfoPath:tmp]; - - //NSLog(@" info path: %@", tmp); - - if ((bundleInfo = NSMapGet(self->pathToBundleInfo, infoPath)) == nil) { - if (![fm fileExistsAtPath:infoPath]) - continue; - - bundleInfo = [self _loadBundleInfoAtExistingPath:infoPath]; - } + tmp = [path stringByAppendingPathComponent:tmp]; + infoPath = [self makeBundleInfoPath:tmp]; + +#if 0 + NSLog(@" info path: %@", tmp); +#endif - providedResources = - [(NSDictionary *)[bundleInfo objectForKey:@"provides"] - objectForKey:_resourceType]; - if (providedResources == nil) continue; + if ((bundleInfo = NSMapGet(self->pathToBundleInfo, infoPath)) == nil) { + if (![fm fileExistsAtPath:infoPath]) + continue; - [result addObjectsFromArray:providedResources]; - } + bundleInfo = [self _loadBundleInfoAtExistingPath:infoPath]; } - /* check for direct match */ + providedResources = + [(NSDictionary *)[bundleInfo objectForKey:@"provides"] + objectForKey:_resourceType]; + if (providedResources == nil) continue; + + [result addObjectsFromArray:providedResources]; + } + + /* check for direct match */ - tmp = [self makeBundleInfoPath:path]; + tmp = [self makeBundleInfoPath:path]; - if ((info = NSMapGet(self->pathToBundleInfo, tmp)) == nil) { - if ([fm fileExistsAtPath:tmp]) - info = [self _loadBundleInfoAtExistingPath:tmp]; - } + if ((info = NSMapGet(self->pathToBundleInfo, tmp)) == nil) { + if ([fm fileExistsAtPath:tmp]) + info = [self _loadBundleInfoAtExistingPath:tmp]; + } - if (info) { - // direct match (a bundle was specified in the path) - NSArray *providedResources; - NSDictionary *provides; + if (info != nil) { + // direct match (a bundle was specified in the path) + NSArray *providedResources; + NSDictionary *provides; - provides = [(NSDictionary *)info objectForKey:@"provides"]; - providedResources = [provides objectForKey:_resourceType]; - info = nil; - if (providedResources == nil) continue; + provides = [(NSDictionary *)info objectForKey:@"provides"]; + providedResources = [provides objectForKey:_resourceType]; + info = nil; + if (providedResources == nil) continue; - [result addObjectsFromArray:providedResources]; - } + [result addObjectsFromArray:providedResources]; } } return [result allObjects]; @@ -1663,7 +1882,7 @@ static BOOL _doesInfoMatch(NSArray *keys, NSDictionary *dict, NSDictionary *info return [[bundles copy] autorelease]; } -// notifications +/* notifications */ - (void)_bundleDidLoadNotifcation:(NSNotification *)_notification { NSDictionary *ui = [_notification userInfo]; @@ -1679,6 +1898,12 @@ static BOOL _doesInfoMatch(NSArray *keys, NSDictionary *dict, NSDictionary *info categories:[ui objectForKey:@"NSLoadedCategories"]]; } +/* debugging */ + +- (BOOL)isDebuggingEnabled { + return debugOn; +} + @end /* NGBundleManager */ @implementation NSBundle(BundleManagerSupport) @@ -1743,7 +1968,7 @@ static BOOL _doesInfoMatch(NSArray *keys, NSDictionary *dict, NSDictionary *info providedByBundle:self]; } -// loading +/* loading */ - (BOOL)_loadForBundleManager:(NGBundleManager *)_manager { return [self load]; @@ -1753,25 +1978,43 @@ static BOOL _doesInfoMatch(NSArray *keys, NSDictionary *dict, NSDictionary *info @implementation NSBundle(NGLanguageResourceExtensions) +static BOOL debugLanguageLookup = NO; + // locating resources - (NSString *)pathForResource:(NSString *)_name ofType:(NSString *)_ext inDirectory:(NSString *)_directory languages:(NSArray *)_languages { - NSFileManager *fm = [NSFileManager defaultManager]; + NSFileManager *fm; NSString *path = nil; int i, langCount; id (*objAtIdx)(id,SEL,int); - - path = _directory - ? [[self bundlePath] stringByAppendingPathComponent:_directory] - : [self bundlePath]; - + + if (debugLanguageLookup) { + NSLog(@"LOOKUP(%s): %@ | %@ | %@ | %@", __PRETTY_FUNCTION__, + _name, _ext, _directory, [_languages componentsJoinedByString:@","]); + } + + path = [self bundlePath]; + if ([_directory isNotNull]) { + // TODO: should we change that? + path = [path stringByAppendingPathComponent:_directory]; + } + else { +#if (NeXT_Foundation_LIBRARY || APPLE_Foundation_LIBRARY) + path = [path stringByAppendingPathComponent:@"Contents"]; +#endif + path = [path stringByAppendingPathComponent:@"Resources"]; + } + + if (debugLanguageLookup) NSLog(@" BASE: %@", path); + + fm = [NSFileManager defaultManager]; if (![fm fileExistsAtPath:path]) return nil; - if (_ext) _name = [_name stringByAppendingPathExtension:_ext]; + if (_ext != nil) _name = [_name stringByAppendingPathExtension:_ext]; langCount = [_languages count]; objAtIdx = (langCount > 0) @@ -1793,6 +2036,9 @@ static BOOL _doesInfoMatch(NSArray *keys, NSDictionary *dict, NSDictionary *info if ([fm fileExistsAtPath:lpath]) return lpath; } + + if (debugLanguageLookup) + NSLog(@" no language matched, check base: %@", path); /* now look into x.bundle/Resources/name.type */ if ([fm fileExistsAtPath:[path stringByAppendingPathComponent:_name]]) @@ -1859,17 +2105,17 @@ static BOOL _doesInfoMatch(NSArray *keys, NSDictionary *dict, NSDictionary *info Class c; NSString *cname; - if ((c = [super principalClass])) + if ((c = [super principalClass]) != Nil) return c; if ((cname = [[self infoDictionary] objectForKey:@"NSPrincipalClass"]) ==nil) return Nil; - if ((c = NSClassFromString(cname))) + if ((c = NSClassFromString(cname)) != Nil) return c; - NSLog(@"%s: did not find principal class named '%@' of bundle %@", - __PRETTY_FUNCTION__, cname, self); + NSLog(@"%s: did not find principal class named '%@' of bundle %@, dict: %@", + __PRETTY_FUNCTION__, cname, self, [self infoDictionary]); return Nil; } @@ -1877,7 +2123,7 @@ static BOOL _doesInfoMatch(NSArray *keys, NSDictionary *dict, NSDictionary *info - (NSString *)description { char buffer[1024]; - + sprintf (buffer, "<%s %p fullPath: %s infoDictionary: %p loaded=%s>", (char*)object_get_class_name(self),