/*
- Copyright (C) 2000-2004 SKYRIX Software AG
+ Copyright (C) 2000-2005 SKYRIX Software AG
- 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 <NGExtensions/NSObject+Logs.h>
+#include <NGExtensions/NSNull+misc.h>
#import <Foundation/NSFileManager.h>
#import <EOControl/EOQualifier.h>
#include <ctype.h>
//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)
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]);
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
objc_setClassHandler(_getClassHook);
}
}
-#endif
-
-#if GNU_RUNTIME
+#elif GNU_RUNTIME
if (_objc_lookup_class != _classLoadHook) {
oldClassLoadHook = _objc_lookup_class;
_objc_lookup_class = _classLoadHook;
- (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];
}
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);
}
- (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__,
+ (unsigned int )bundle, [[bundle bundlePath] cString]);
+ }
+ bundle = nil;
+ }
+ else if ([p hasSuffix:@"Tools"]) {
+ if (debugOn) {
+ fprintf(stderr, "%s: Tool bundle: 0x%p: %s\n",
+ __PRETTY_FUNCTION__,
+ (unsigned int )bundle, [[bundle bundlePath] cString]);
+ }
+ bundle = nil;
+ }
+ }
}
#endif
if (bundle == nil) {
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 {
- (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
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
ofType:@"classes"
resourceSelector:_selectClassByVersion
context:NULL /* version */];
- if (path) {
+ if (path != nil) {
path = [path stringByResolvingSymlinksInPath];
NSAssert(path, @"couldn't resolve symlinks in path ..");
}
if (path == nil)
return nil;
- if ((bundle = [self bundleWithPath:path]))
+ if ((bundle = [self bundleWithPath:path]) != nil)
NSMapInsert(self->classNameToBundle, _className, bundle);
return bundle;
}
- (NSArray *)classesProvidedByBundle:(NSBundle *)_bundle {
- [self doesNotRecognizeSelector:_cmd];
- return nil;
+ return [[_bundle providedResourcesOfType:@"classes"] valueForKey:@"name"];
}
- (NSArray *)classesRequiredByBundle:(NSBundle *)_bundle {
[self doesNotRecognizeSelector:_cmd];
[self debugWithFormat:
@"no bundle handler, lookup principal class of bundle: %@",
_bundle];
- if ((handler = [_bundle principalClass]) == nil)
+ 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;
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;
}
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 %@.",
#endif
}
+ if (debugOn)
+ NSLog(@"CLASS %@ => BUNDLE %@", className, bundle);
+
return bundle;
}
- (NSArray *)_locateBundlesForClassInfos:(NSEnumerator *)_classInfos {
NSDictionary *i;
requiredBundles = [NSMutableArray arrayWithCapacity:16];
- while ((i = [_classInfos nextObject])) {
+ while ((i = [_classInfos nextObject]) != nil) {
NSBundle *bundle;
if ((bundle = [self _locateBundleForClassInfo:i]) == nil)
- (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"];
/* locate required bundles */
e = [[requires objectForKey:@"bundles"] objectEnumerator];
- while ((i = [e nextObject])) {
+ while ((i = [e nextObject]) != nil) {
NSString *bundleName;
if (![i respondsToSelector:@selector(objectForKey:)]) {
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;
}
}
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;
}
}
NSDictionary *i;
e = [[requires objectForKey:@"classes"] objectEnumerator];
- while ((i = [e nextObject])) {
+ while ((i = [e nextObject]) != nil) {
NSString *className;
Class clazz;
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;
rnKeys = ([_resourceName respondsToSelector:@selector(objectForKey:)])
? [_resourceName allKeys]
- : nil;
+ : (NSArray *)nil;
menum = NSEnumerateMapTable(self->pathToBundleInfo);
while (NSNextMapEnumeratorPair(&menum, (void *)&path, (void *)&bundleInfo)) {
/* check whether an appropriate bundle is contained in 'path' */
dir = [[fm directoryContentsAtPath:path] objectEnumerator];
- while ((tmp = [dir nextObject])) {
+ while ((tmp = [dir nextObject]) != nil) {
NSDictionary *bundleInfo = nil;
NSString *infoPath;
/* scan all bundle search paths */
e = [self->bundleSearchPaths objectEnumerator];
- while ((path = [e nextObject])) {
+ while ((path = [e nextObject]) != nil) {
NSEnumerator *dir;
BOOL isDir = NO;
NSString *tmp;
if (!isDir) continue;
/* check whether an appropriate bundle is contained in 'path' */
-
+
+ // TODO: move to own method
dir = [[fm directoryContentsAtPath:path] objectEnumerator];
- while ((tmp = [dir nextObject])) {
+ while ((tmp = [dir nextObject]) != nil) {
NSDictionary *bundleInfo = nil;
NSArray *providedResources = nil;
NSString *infoPath;
providedByBundle:self];
}
-// loading
+/* loading */
- (BOOL)_loadForBundleManager:(NGBundleManager *)_manager {
return [self load];
@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)
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]])
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;
}