2 Copyright (C) 2000-2004 SKYRIX Software AG
4 This file is part of OpenGroupware.org.
6 OGo is free software; you can redistribute it and/or modify it under
7 the terms of the GNU Lesser General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
11 OGo is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14 License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with OGo; see the file COPYING. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
22 #include "NGBundleManager.h"
24 #include <NGExtensions/NSObject+Logs.h>
25 #import <Foundation/NSFileManager.h>
26 #import <EOControl/EOQualifier.h>
29 #if 0 && GNUSTEP_BASE_LIBRARY /* supported in repository since 19990916-2254-MET */
30 /* hack until GNUstep provides the necessary callbacks */
31 # define NSNonRetainedObjectMapValueCallBacks NSObjectMapValueCallBacks
34 #if NeXT_Foundation_LIBRARY || COCOA_Foundation_LIBRARY
35 # include <NGExtensions/NGPropertyListParser.h>
38 #if LIB_FOUNDATION_LIBRARY
39 @interface NSBundle(UsedPrivates)
40 + (BOOL)isFlattenedDirLayout;
44 #if NeXT_RUNTIME || APPLE_RUNTIME
46 #include <objc/objc-runtime.h>
48 //OBJC_EXPORT void objc_setClassHandler(int (*)(const char *));
50 static BOOL debugClassHook = NO;
52 static int _getClassHook(const char *className) {
53 if (className == NULL) return 0;
56 printf("lookup class '%s'.\n", className);
58 if (objc_lookUpClass(className))
62 static NGBundleManager *manager = nil;
66 NSLog(@"%s: look for class %s", __PRETTY_FUNCTION__, className);
68 manager = [NGBundleManager defaultBundleManager];
70 bundle = [manager bundleForClassNamed:
71 [NSString stringWithCString:className]];
74 NSLog(@"%s: found bundle %@", __PRETTY_FUNCTION__,
78 if (![manager loadBundle:bundle]) {
80 "bundleManager couldn't load bundle for class '%s'.\n",
85 Class c = objc_lookUpClass(className);
86 NSLog(@"%s: loaded bundle %@ for className %s class %@",
88 bundle, className, c);
100 #include <objc/objc-api.h>
102 static Class (*oldClassLoadHook)(const char *_name) = NULL;
104 static inline BOOL _isValidClassName(const char *_name) {
107 if (_name == NULL) return NO;
109 for (len = 0; (len < 256) && (*_name != '\0'); len++, _name++) {
111 if (!isalnum((int)*_name))
115 return (len == 256) ? NO : YES;
118 static Class _classLoadHook(const char *_name) {
119 if (_isValidClassName(_name)) {
120 static NGBundleManager *manager = nil;
123 //NSLog(@"%s: look for class %s", __PRETTY_FUNCTION__, _name);
125 manager = [NGBundleManager defaultBundleManager];
127 bundle = [manager bundleForClassNamed:[NSString stringWithCString:_name]];
130 NSLog(@"%s: found bundle %@", __PRETTY_FUNCTION__, [bundle bundlePath]);
133 if ([manager loadBundle:bundle]) {
137 hook = _objc_lookup_class;
138 _objc_lookup_class = NULL;
139 clazz = objc_lookup_class(_name);
140 _objc_lookup_class = hook;
142 if (clazz) return clazz;
146 return (oldClassLoadHook != NULL) ? oldClassLoadHook(_name) : Nil;
148 #endif // GNU_RUNTIME
150 NSString *NGBundleWasLoadedNotificationName = @"NGBundleWasLoadedNotification";
152 @interface NSBundle(NGBundleManagerPrivate)
153 - (BOOL)_loadForBundleManager:(NGBundleManager *)_manager;
156 @interface NGBundleManager(PrivateMethods)
158 - (void)registerBundle:(NSBundle *)_bundle
159 classes:(NSArray *)_classes
160 categories:(NSArray *)_categories;
162 - (NSString *)pathForBundleProvidingResource:(NSString *)_resourceName
163 ofType:(NSString *)_type
164 resourceSelector:(NGBundleResourceSelector)_selector
165 context:(void *)_ctx;
167 - (NSString *)makeBundleInfoPath:(NSString *)_path;
171 static BOOL _selectClassByVersion(NSString *_resourceName,
172 NSString *_resourceType,
174 NSDictionary *_resourceConfig,
175 NGBundleManager *_bundleManager,
181 if (![_resourceType isEqualToString:@"classes"])
184 if (_version == NULL)
186 if ([(id)_version intValue] == -1)
189 if ((tmp = [_resourceConfig objectForKey:@"version"])) {
190 classVersion = [tmp intValue];
192 if (classVersion < [(id)_version intValue]) {
193 NSLog(@"WARNING: class version mismatch for class %@: "
194 @"requested at least version %i, got version %i",
195 _resourceName, [(id)_version intValue], classVersion);
198 if ((tmp = [_resourceConfig objectForKey:@"exact-version"])) {
199 classVersion = [tmp intValue];
201 if (classVersion != [(id)_version intValue]) {
202 NSLog(@"WARNING: class version mismatch for class %@: "
203 @"requested exact version %i, got version %i",
204 _resourceName, [(id)_version intValue], classVersion);
210 @implementation NGBundleManager
213 static NGBundleManager *defaultManager = nil;
214 static BOOL debugOn = NO;
216 #if defined(__MINGW32__)
217 static NSString *NGEnvVarPathSeparator = @";";
219 static NSString *NGEnvVarPathSeparator = @":";
223 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
225 debugOn = [ud boolForKey:@"NGBundleManagerDebugEnabled"];
230 if (_objc_lookup_class != _classLoadHook) {
231 oldClassLoadHook = _objc_lookup_class;
232 _objc_lookup_class = _classLoadHook;
237 + (id)defaultBundleManager {
238 if (defaultManager == nil) {
239 defaultManager = [[NGBundleManager alloc] init];
241 #if NeXT_RUNTIME || APPLE_RUNTIME
243 static BOOL didRegisterCallback = NO;
245 if (!didRegisterCallback) {
246 didRegisterCallback = YES;
247 objc_setClassHandler(_getClassHook);
253 if (_objc_lookup_class != _classLoadHook) {
254 oldClassLoadHook = _objc_lookup_class;
255 _objc_lookup_class = _classLoadHook;
259 return defaultManager;
262 /* setup bundle search path */
264 - (void)_addMainBundlePathToPathArray:(NSMutableArray *)_paths {
268 pi = [NSProcessInfo processInfo];
269 path = [[pi arguments] objectAtIndex:0];
270 path = [path stringByDeletingLastPathComponent];
272 if ([path isEqual:@""])
276 // TODO: to be correct this would need to read the bundle-info
279 The path is the complete path to the executable, including the
280 processor, the OS and the library combo. Strip these directories
281 from the main bundle's path.
283 #if LIB_FOUNDATION_LIBRARY
284 if (![NSBundle isFlattenedDirLayout])
286 path = [[[path stringByDeletingLastPathComponent]
287 stringByDeletingLastPathComponent]
288 stringByDeletingLastPathComponent];
291 [_paths addObject:path];
294 - (void)_addBundlePathDefaultToPathArray:(NSMutableArray *)_paths {
298 if ((ud = [NSUserDefaults standardUserDefaults]) == nil) {
299 // got this with gstep-base during the port, apparently it happens
300 // if the bundle manager is created inside the setup process of
301 // gstep-base (for whatever reason)
302 NSLog(@"ERROR(NGBundleManager): got no system userdefaults object!");
308 if ((paths = [ud arrayForKey:@"NGBundlePath"]) == nil) {
309 if ((paths = [ud stringForKey:@"NGBundlePath"]) != nil)
310 paths = [paths componentsSeparatedByString:NGEnvVarPathSeparator];
313 [_paths addObjectsFromArray:paths];
315 NSLog(@"Note: NGBundlePath default is not configured.");
318 - (void)_addEnvironmentPathToPathArray:(NSMutableArray *)_paths {
322 pi = [NSProcessInfo processInfo];
323 paths = [[pi environment] objectForKey:@"NGBundlePath"];
325 paths = [paths componentsSeparatedByString:NGEnvVarPathSeparator];
326 if (paths) [_paths addObjectsFromArray:paths];
329 - (void)_addGNUstepPathsToPathArray:(NSMutableArray *)_paths {
332 // TODO: whats that supposed to do?
333 // TODO: replace with proper path locator function!
339 env = [[NSProcessInfo processInfo] environment];
341 if ((tmp = [env objectForKey:@"GNUSTEP_PATHPREFIX_LIST"]) == nil)
342 tmp = [env objectForKey:@"GNUSTEP_PATHLIST"];
343 tmp = [tmp componentsSeparatedByString:@":"];
345 for (i = 0, count = [tmp count]; i < count; i++) {
346 p = [tmp objectAtIndex:i];
347 p = [p stringByAppendingPathComponent:@"Library"];
348 p = [p stringByAppendingPathComponent:@"Bundles"];
349 if ([self->bundleSearchPaths containsObject:p]) continue;
351 if (p) [self->bundleSearchPaths addObject:p];
356 - (void)_setupBundleSearchPathes {
359 pi = [NSProcessInfo processInfo];
361 /* setup bundle search path */
363 self->bundleSearchPaths = [[NSMutableArray alloc] initWithCapacity:16];
365 [self _addMainBundlePathToPathArray:self->bundleSearchPaths];
366 [self _addBundlePathDefaultToPathArray:self->bundleSearchPaths];
367 [self _addEnvironmentPathToPathArray:self->bundleSearchPaths];
368 [self _addGNUstepPathsToPathArray:self->bundleSearchPaths];
370 #if DEBUG && NeXT_Foundation_LIBRARY && 0
371 NSLog(@"%s: bundle search pathes:\n%@", __PRETTY_FUNCTION__,
372 self->bundleSearchPaths);
376 - (void)_registerLoadedBundles {
377 NSEnumerator *currentBundles;
378 NSBundle *loadedBundle;
380 currentBundles = [[NSBundle allBundles] objectEnumerator];
381 while ((loadedBundle = [currentBundles nextObject]))
382 [self registerBundle:loadedBundle classes:nil categories:nil];
385 - (void)_registerForBundleLoadNotification {
386 [[NSNotificationCenter defaultCenter]
388 selector:@selector(_bundleDidLoadNotifcation:)
389 name:@"NSBundleDidLoadNotification"
394 #if GNUSTEP_BASE_LIBRARY
395 if ([NSUserDefaults standardUserDefaults] == nil) {
396 /* called inside setup process, deny creation (HACK) */
402 if ((self = [super init])) {
403 self->classToBundle =
404 NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks,
405 NSNonRetainedObjectMapValueCallBacks,
407 self->classNameToBundle =
408 NSCreateMapTable(NSObjectMapKeyCallBacks,
409 NSNonRetainedObjectMapValueCallBacks,
411 self->categoryNameToBundle =
412 NSCreateMapTable(NSObjectMapKeyCallBacks,
413 NSNonRetainedObjectMapValueCallBacks,
416 NSCreateMapTable(NSObjectMapKeyCallBacks,
417 NSNonRetainedObjectMapValueCallBacks,
419 self->pathToBundleInfo =
420 NSCreateMapTable(NSObjectMapKeyCallBacks,
421 NSObjectMapValueCallBacks,
424 NSCreateMapTable(NSObjectMapKeyCallBacks,
425 NSNonRetainedObjectMapValueCallBacks,
427 self->loadedBundles =
428 NSCreateMapTable(NSNonRetainedObjectMapKeyCallBacks,
429 NSObjectMapValueCallBacks,
432 [self _setupBundleSearchPathes];
433 [self _registerLoadedBundles];
434 [self _registerForBundleLoadNotification];
440 [self->loadingBundles release];
441 if (self->loadedBundles) NSFreeMapTable(self->loadedBundles);
442 if (self->classToBundle) NSFreeMapTable(self->classToBundle);
443 if (self->classNameToBundle) NSFreeMapTable(self->classNameToBundle);
444 if (self->categoryNameToBundle) NSFreeMapTable(self->categoryNameToBundle);
445 if (self->pathToBundle) NSFreeMapTable(self->pathToBundle);
446 if (self->pathToBundleInfo) NSFreeMapTable(self->pathToBundleInfo);
447 if (self->nameToBundle) NSFreeMapTable(self->nameToBundle);
448 [self->bundleSearchPaths release];
454 - (void)setBundleSearchPaths:(NSArray *)_paths {
455 ASSIGNCOPY(self->bundleSearchPaths, _paths);
457 - (NSArray *)bundleSearchPaths {
458 return self->bundleSearchPaths;
461 /* registering bundles */
463 - (void)registerBundle:(NSBundle *)_bundle
464 classes:(NSArray *)_classes
465 categories:(NSArray *)_categories
470 //NSLog(@"NGBundleManager: register loaded bundle %@", [_bundle bundlePath]);
472 e = [_classes objectEnumerator];
473 while ((v = [e nextObject])) {
474 NSMapInsert(self->classToBundle, NSClassFromString(v), _bundle);
475 NSMapInsert(self->classNameToBundle, v, _bundle);
478 e = [_categories objectEnumerator];
479 while ((v = [e nextObject]))
480 NSMapInsert(self->categoryNameToBundle, v, _bundle);
485 - (NSString *)pathForBundleWithName:(NSString *)_name type:(NSString *)_type {
486 NSFileManager *fm = [NSFileManager defaultManager];
489 NSString *bundlePath;
492 /* first check in table */
495 bundlePath = [_name stringByAppendingPathExtension:_type];
497 if ((bundle = NSMapGet(self->nameToBundle, bundlePath)))
498 return [bundle bundlePath];
500 e = [self->bundleSearchPaths objectEnumerator];
501 while ((path = [e nextObject])) {
504 if ([fm fileExistsAtPath:path isDirectory:&isDir]) {
505 if (!isDir) continue;
507 if ([[path lastPathComponent] isEqualToString:bundlePath]) {
508 // direct match (a bundle was specified in the path)
514 tmp = [path stringByAppendingPathComponent:bundlePath];
515 if ([fm fileExistsAtPath:tmp isDirectory:&isDir]) {
526 /* getting bundles */
528 - (NSBundle *)bundleForClass:(Class)aClass {
529 /* this method never loads a dynamic bundle (since the class is set up) */
532 bundle = NSMapGet(self->classToBundle, aClass);
534 #if NeXT_Foundation_LIBRARY || COCOA_Foundation_LIBRARY
536 bundle = [NSBundle bundleForClass:aClass];
537 if (bundle == [NSBundle mainBundle])
543 if the class wasn't loaded from a bundle, it's *either* the main bundle
544 or a bundle loaded before NGExtension was loaded !!!
547 #if !LIB_FOUNDATION_LIBRARY && !GNUSTEP_BASE_LIBRARY
548 // Note: incorrect behaviour if NGExtensions is dynamically loaded !
549 // TODO: can we do anything about this? Can we detect the situation and
550 // print a log instead of the compile warning?
551 // Note: the above refers to the situation when a framework is implicitly
552 // loaded by loading a bundle (the framework is not linked against
555 bundle = [NSBundle mainBundle];
556 NSMapInsert(self->classToBundle, aClass, bundle);
557 NSMapInsert(self->classNameToBundle, NSStringFromClass(aClass), bundle);
561 - (NSBundle *)bundleWithPath:(NSString *)path {
562 NSBundle *bundle = nil;
565 path = [path stringByResolvingSymlinksInPath];
569 if (debugOn) NSLog(@"find bundle for path: '%@'", path);
570 bundle = NSMapGet(self->pathToBundle, path);
573 if (debugOn) NSLog(@" found: %@", bundle);
577 if ((bundle = [(NGBundle *)[NGBundle alloc] initWithPath:path]) == nil) {
578 NSLog(@"ERROR(%s): could not create bundle for path: '%@'",
579 __PRETTY_FUNCTION__, path);
583 bn = [[bundle bundleName]
584 stringByAppendingPathExtension:[bundle bundleType]],
586 NSMapInsert(self->pathToBundle, path, bundle);
587 NSMapInsert(self->nameToBundle, bn, bundle);
591 - (NSBundle *)bundleWithName:(NSString *)_name type:(NSString *)_type {
595 bn = [_name stringByAppendingPathExtension:_type];
596 bundle = NSMapGet(self->nameToBundle, bn);
599 bundle = [self bundleWithPath:[self pathForBundleWithName:_name type:_type]];
601 if (![[bundle bundleType] isEqualToString:_type])
606 - (NSBundle *)bundleWithName:(NSString *)_name {
607 return [self bundleWithName:_name type:@"bundle"];
610 - (NSBundle *)bundleForClassNamed:(NSString *)_className {
611 NSString *path = nil;
612 NSBundle *bundle = nil;
614 if (_className == nil)
617 /* first check in table */
619 if ((bundle = NSMapGet(self->classNameToBundle, _className)))
623 /* then look in runtime, reset load callback to avoid recursion */
630 loadCallback = _objc_lookup_class;
631 _objc_lookup_class = NULL;
632 clazz = NSClassFromString(_className);
633 _objc_lookup_class = loadCallback;
636 /* the class is already loaded */
637 bundle = [self bundleForClass:clazz];
638 NSMapInsert(self->classNameToBundle, _className, bundle);
644 path = [self pathForBundleProvidingResource:_className
646 resourceSelector:_selectClassByVersion
647 context:NULL /* version */];
649 path = [path stringByResolvingSymlinksInPath];
650 NSAssert(path, @"couldn't resolve symlinks in path ..");
656 if ((bundle = [self bundleWithPath:path]))
657 NSMapInsert(self->classNameToBundle, _className, bundle);
668 - (NSArray *)bundlesRequiredByBundle:(NSBundle *)_bundle {
669 [self doesNotRecognizeSelector:_cmd];
673 - (NSArray *)classesProvidedByBundle:(NSBundle *)_bundle {
674 [self doesNotRecognizeSelector:_cmd];
677 - (NSArray *)classesRequiredByBundle:(NSBundle *)_bundle {
678 [self doesNotRecognizeSelector:_cmd];
684 - (NSString *)makeBundleInfoPath:(NSString *)_path {
685 #if (NeXT_Foundation_LIBRARY || APPLE_Foundation_LIBRARY) && !defined(GSWARN)
686 return [[[_path stringByAppendingPathComponent:@"Contents"]
687 stringByAppendingPathComponent:@"Resources"]
688 stringByAppendingPathComponent:@"bundle-info.plist"];
690 return [_path stringByAppendingPathComponent:@"bundle-info.plist"];
694 - (id)_initializeLoadedBundle:(NSBundle *)_bundle
695 info:(NSDictionary *)_bundleInfo
699 /* check whether a handler was specified */
701 if ((handler = [_bundleInfo objectForKey:@"bundleHandler"]) != nil) {
702 [self debugWithFormat:@"lookup bundle handler %@ of bundle: %@",
705 if ((handler = NSClassFromString(handler)) == nil) {
706 NSLog(@"ERROR: did not find handler class %@ of bundle %@.",
707 [_bundleInfo objectForKey:@"bundleHandler"], [_bundle bundlePath]);
708 handler = [_bundle principalClass];
711 handler = [handler alloc];
713 if ([handler respondsToSelector:@selector(initForBundle:bundleManager:)])
714 handler = [handler initForBundle:_bundle bundleManager:self];
716 handler = [handler init];
717 handler = [handler autorelease];
719 if (handler == nil) {
720 NSLog(@"ERROR: could not instantiate handler class %@ of bundle %@.",
721 [_bundleInfo objectForKey:@"bundleHandler"], [_bundle bundlePath]);
722 handler = [_bundle principalClass];
726 [self debugWithFormat:
727 @"no bundle handler, lookup principal class of bundle: %@",
729 if ((handler = [_bundle principalClass]) == nil)
730 /* use NGBundle class as default bundle handler */
731 handler = [NGBundle class];
739 - (NSDictionary *)_loadBundleInfoAtExistingPath:(NSString *)_path {
740 NSDictionary *bundleInfo;
743 #if NeXT_Foundation_LIBRARY || COCOA_Foundation_LIBRARY
744 bundleInfo = NGParsePropertyListFromFile(_path);
746 bundleInfo = [NSDictionary dictionaryWithContentsOfFile:_path];
748 if (bundleInfo == nil) {
749 NSLog(@"couldn't load bundle-info at path '%@' !", _path);
753 /* check required bundle manager version */
754 info = [bundleInfo objectForKey:@"requires"];
755 if ((info = [(NSDictionary *)info objectForKey:@"bundleManagerVersion"])) {
756 if ([info intValue] > [[self class] version]) {
757 /* bundle manager version does not match ... */
761 NSMapInsert(self->pathToBundleInfo, _path, bundleInfo);
765 - (NSBundle *)_locateBundleForClassInfo:(NSDictionary *)_classInfo {
769 if (_classInfo == nil)
771 if ((className = [_classInfo objectForKey:@"name"]) == nil) {
772 NSLog(@"ERROR: missing classname in bundle-info.plist class section !");
776 if ((bundle = [self bundleForClassNamed:className]) == nil) {
777 #if 0 // class might be already loaded
778 NSLog(@"ERROR: did not find class %@ required by bundle %@.",
779 className, [_bundle bundlePath]);
785 - (NSArray *)_locateBundlesForClassInfos:(NSEnumerator *)_classInfos {
786 NSMutableArray *requiredBundles;
789 requiredBundles = [NSMutableArray arrayWithCapacity:16];
790 while ((i = [_classInfos nextObject])) {
793 if ((bundle = [self _locateBundleForClassInfo:i]) == nil)
796 [requiredBundles addObject:bundle];
798 return requiredBundles;
801 - (BOOL)_preLoadBundle:(NSBundle *)_bundle info:(NSDictionary *)_bundleInfo {
802 /* TODO: split up this huge method */
803 NSDictionary *requires = nil;
804 NSMutableArray *requiredBundles = nil;
805 NSBundle *requiredBundle = nil;
807 requires = [_bundleInfo objectForKey:@"requires"];
810 /* invalid bundle info specified */
813 /* load required bundles */
818 /* locate required bundles */
820 e = [[requires objectForKey:@"bundles"] objectEnumerator];
821 while ((i = [e nextObject])) {
822 NSString *bundleName;
824 if (![i respondsToSelector:@selector(objectForKey:)]) {
825 NSLog(@"ERROR(%s): invalid bundle-info of bundle %@ !!!\n"
826 @" requires-entry is not a dictionary: %@",
827 __PRETTY_FUNCTION__, _bundle, i);
831 if ((bundleName = [i objectForKey:@"name"])) {
834 type = [i objectForKey:@"type"];
835 if (type == nil) type = @"bundle";
837 if ((requiredBundle = [self bundleWithName:bundleName type:type])) {
838 if (requiredBundles == nil)
839 requiredBundles = [NSMutableArray arrayWithCapacity:16];
841 [requiredBundles addObject:requiredBundle];
844 NSLog(@"ERROR(NGBundleManager): did not find bundle '%@' (type=%@) "
845 @"required by bundle %@.",
846 bundleName, type, [_bundle bundlePath]);
851 NSLog(@"ERROR: error in bundle-info.plist of bundle %@", _bundle);
855 /* load located bundles */
859 e = [requiredBundles objectEnumerator];
860 while ((requiredBundle = [e nextObject])) {
863 if ((bundleMaster = [self loadBundle:requiredBundle]) == Nil) {
864 NSLog(@"ERROR: could not load bundle %@ (%@) required by bundle %@.",
865 [requiredBundle bundlePath], requiredBundle, [_bundle bundlePath]);
871 /* load required classes */
876 reqClasses = [requires objectForKey:@"classes"];
877 bundles = [self _locateBundlesForClassInfos:[reqClasses objectEnumerator]];
878 if (requiredBundles == nil)
879 requiredBundles = [NSMutableArray arrayWithCapacity:16];
880 [requiredBundles addObjectsFromArray:bundles];
883 /* load located bundles */
887 e = [requiredBundles objectEnumerator];
888 while ((requiredBundle = [e nextObject])) {
891 if ((bundleMaster = [self loadBundle:requiredBundle]) == Nil) {
892 NSLog(@"ERROR: could not load bundle %@ (%@) required by bundle %@.",
893 [requiredBundle bundlePath], requiredBundle, [_bundle bundlePath]);
899 /* check whether versions of classes match */
904 e = [[requires objectForKey:@"classes"] objectEnumerator];
905 while ((i = [e nextObject])) {
909 if ((className = [i objectForKey:@"name"]) == nil)
912 if ((clazz = NSClassFromString(className)) == Nil)
915 if ([i objectForKey:@"exact-version"]) {
918 v = [[i objectForKey:@"exact-version"] intValue];
920 if (v != [clazz version]) {
921 NSLog(@"ERROR: required exact class match failed:\n"
923 @" required version: %i\n"
924 @" loaded version: %i\n"
928 [_bundle bundlePath]);
931 else if ([i objectForKey:@"version"]) {
934 v = [[i objectForKey:@"version"] intValue];
936 if (v > [clazz version]) {
937 NSLog(@"ERROR: provided class does not match required version:\n"
939 @" least required version: %i\n"
940 @" loaded version: %i\n"
944 [_bundle bundlePath]);
952 - (BOOL)_postLoadBundle:(NSBundle *)_bundle info:(NSDictionary *)_bundleInfo {
956 - (id)loadBundle:(NSBundle *)_bundle {
957 NSString *path = nil;
958 NSDictionary *bundleInfo = nil;
959 id bundleManager = nil;
962 NSAssert(self->loadedBundles, @"missing loadedBundles hashmap ..");
965 if ((bundleManager = NSMapGet(self->loadedBundles, _bundle)))
966 return bundleManager;
968 if (_bundle == [NSBundle mainBundle])
969 return [NSBundle mainBundle];
971 if ([self->loadingBundles containsObject:_bundle])
975 if (self->loadingBundles == nil)
976 self->loadingBundles = [[NSMutableSet allocWithZone:[self zone]] init];
977 [self->loadingBundles addObject:_bundle];
979 path = [_bundle bundlePath];
980 path = [self makeBundleInfoPath:path];
982 if ((bundleInfo = NSMapGet(self->pathToBundleInfo, path)) == nil) {
983 if ([[NSFileManager defaultManager] fileExistsAtPath:path])
984 bundleInfo = [self _loadBundleInfoAtExistingPath:path];
987 if (![self _preLoadBundle:_bundle info:bundleInfo])
990 if (![_bundle _loadForBundleManager:self])
993 if (![self _postLoadBundle:_bundle info:bundleInfo])
997 [self _initializeLoadedBundle:_bundle info:bundleInfo])) {
998 NSMapInsert(self->loadedBundles, _bundle, bundleManager);
1000 if ([bundleManager respondsToSelector:
1001 @selector(bundleManager:didLoadBundle:)])
1002 [bundleManager bundleManager:self didLoadBundle:_bundle];
1006 NSLog(@"ERROR(%s): couldn't initialize loaded bundle '%@'",
1007 __PRETTY_FUNCTION__, [_bundle bundlePath]);
1011 [self->loadingBundles removeObject:_bundle];
1013 if (bundleManager) {
1014 if (bundleInfo == nil)
1015 bundleInfo = [NSDictionary dictionary];
1017 [[NSNotificationCenter defaultCenter]
1018 postNotificationName:
1019 NGBundleWasLoadedNotificationName
1021 userInfo:[NSDictionary dictionaryWithObjectsAndKeys:
1022 self, @"NGBundleManager",
1023 bundleManager, @"NGBundleHandler",
1024 bundleInfo, @"NGBundleInfo",
1027 return bundleManager;
1032 - (id)principalObjectOfBundle:(NSBundle *)_bundle {
1033 return (id)NSMapGet(self->loadedBundles, _bundle);
1038 static BOOL _doesInfoMatch(NSArray *keys, NSDictionary *dict, NSDictionary *info)
1042 for (i = 0, count = [keys count]; i < count; i++) {
1046 key = [keys objectAtIndex:i];
1047 vv = [info objectForKey:key];
1050 /* info has no matching key */
1054 kv = [dict objectForKey:key];
1055 if (![kv isEqual:vv])
1061 - (NSDictionary *)configForResource:(id)_resource ofType:(NSString *)_type
1062 providedByBundle:(NSBundle *)_bundle
1064 NSDictionary *bundleInfo = nil;
1066 NSEnumerator *providedResources;
1067 NSArray *rnKeys = nil;
1071 if ([_resource respondsToSelector:@selector(objectForKey:)]) {
1072 rnKeys = [_resource allKeys];
1073 rnKeyCount = [rnKeys count];
1076 infoPath = [self makeBundleInfoPath:[_bundle bundlePath]];
1078 /* check whether info is in cache */
1079 if ((bundleInfo = NSMapGet(self->pathToBundleInfo, infoPath)) == nil) {
1080 if (![[NSFileManager defaultManager] fileExistsAtPath:infoPath])
1081 /* no bundle-info.plist available .. */
1085 bundleInfo = [self _loadBundleInfoAtExistingPath:infoPath];
1088 /* get provided resources config */
1091 [[(NSDictionary *)[bundleInfo objectForKey:@"provides"] objectForKey:_type]
1093 if (providedResources == nil) return nil;
1095 /* scan provided resources */
1097 while ((info = [providedResources nextObject])) {
1099 if (!_doesInfoMatch(rnKeys, _resource, info))
1105 name = [[(NSDictionary *)info objectForKey:@"name"] stringValue];
1106 if (name == nil) continue;
1107 if (![name isEqualToString:_resource]) continue;
1114 - (void)_processInfoForProvidedResources:(NSDictionary *)info
1115 ofType:(NSString *)_type path:(NSString *)path
1116 resourceName:(NSString *)_resourceName
1117 resourceSelector:(NGBundleResourceSelector)_selector
1118 context:(void *)_context
1119 andAddToResultArray:(NSMutableArray *)result
1121 NSEnumerator *providedResources = nil;
1122 if (info == nil) return;
1124 /* direct match (a bundle was specified in the path) */
1126 providedResources = [[(NSDictionary *)[info objectForKey:@"provides"]
1130 if (providedResources == nil) return;
1132 /* scan provide array */
1133 while ((info = [providedResources nextObject])) {
1136 if ((name = [[info objectForKey:@"name"] stringValue]) == nil)
1139 if (_resourceName) {
1140 if (![name isEqualToString:_resourceName])
1144 if (!_selector(_resourceName, _type, path, info, self, _context))
1148 [result addObject:path];
1152 - (NSArray *)pathsForBundlesProvidingResource:(NSString *)_resourceName
1153 ofType:(NSString *)_type
1154 resourceSelector:(NGBundleResourceSelector)_selector
1155 context:(void *)_context
1157 /* TODO: split up method */
1158 NSMutableArray *result = nil;
1164 NSLog(@"BM LOOKUP pathes (%d bundles loaded): %@ / %@",
1165 NSCountMapTable(self->loadedBundles), _resourceName, _type);
1168 fm = [NSFileManager defaultManager];
1169 result = [NSMutableArray arrayWithCapacity:64];
1171 // TODO: look in loaded bundles
1173 /* check physical pathes */
1175 e = [self->bundleSearchPaths objectEnumerator];
1176 while ((path = [e nextObject]) != nil) {
1179 NSString *tmp, *bundleDirPath;
1182 if (![fm fileExistsAtPath:path isDirectory:&isDir])
1185 if (!isDir) continue;
1187 /* check whether an appropriate bundle is contained in 'path' */
1189 dir = [[fm directoryContentsAtPath:path] objectEnumerator];
1190 while ((bundleDirPath = [dir nextObject]) != nil) {
1191 NSDictionary *bundleInfo = nil;
1192 NSEnumerator *providedResources = nil;
1196 bundleDirPath = [path stringByAppendingPathComponent:bundleDirPath];
1197 infoPath = [self makeBundleInfoPath:bundleDirPath];
1199 // TODO: can we use _doesBundleInfo:path:providedResource:... ?
1200 if ((bundleInfo = NSMapGet(self->pathToBundleInfo, infoPath))==nil) {
1201 if (![fm fileExistsAtPath:infoPath])
1204 bundleInfo = [self _loadBundleInfoAtExistingPath:infoPath];
1208 [[(NSDictionary *)[bundleInfo objectForKey:@"provides"]
1211 if (providedResources == nil) continue;
1213 /* scan 'provides' array */
1214 while ((info = [providedResources nextObject])) {
1217 name = [[(NSDictionary *)info objectForKey:@"name"] stringValue];
1218 if (name == nil) continue;
1220 if (_resourceName != nil) {
1221 if (![name isEqualToString:_resourceName])
1224 if (_selector != NULL) {
1225 if (!_selector(name, _type, bundleDirPath, info, self, _context))
1229 [result addObject:bundleDirPath];
1234 /* check for direct match (NGBundlePath element is a bundle) */
1236 tmp = [self makeBundleInfoPath:path];
1238 if ((info = NSMapGet(self->pathToBundleInfo, tmp)) == nil) {
1239 if ([fm fileExistsAtPath:tmp])
1240 info = [self _loadBundleInfoAtExistingPath:tmp];
1243 [self _processInfoForProvidedResources:info ofType:_type path:path
1244 resourceName:_resourceName resourceSelector:_selector
1246 andAddToResultArray:result];
1249 if ([result count] == 0) {
1250 [self logWithFormat:
1251 @"Note(%s): method does not search in loaded bundles for "
1252 @"resources of type '%@'",
1253 __PRETTY_FUNCTION__, _type];
1256 return [[result copy] autorelease];
1259 - (BOOL)_doesBundleInfo:(NSDictionary *)_bundleInfo path:(NSString *)_path
1260 provideResource:(id)_resourceName ofType:(NSString *)_type
1261 rnKeys:(NSArray *)_rnKeys
1262 resourceSelector:(NGBundleResourceSelector)_selector context:(void *)_context
1264 NSEnumerator *providedResources;
1268 [[(NSDictionary *)[_bundleInfo objectForKey:@"provides"]
1269 objectForKey:_type] objectEnumerator];
1270 if (providedResources == nil) return NO;
1272 /* scan provide array */
1273 while ((info = [providedResources nextObject])) {
1274 if (_rnKeys != nil) {
1275 if (!_doesInfoMatch(_rnKeys, _resourceName, info))
1281 name = [[(NSDictionary *)info objectForKey:@"name"] stringValue];
1282 if (name == nil) continue;
1283 if (![name isEqualToString:_resourceName]) continue;
1286 if (_selector != NULL) {
1287 if (!_selector(_resourceName, _type, _path, info, self, _context))
1291 /* all conditions applied (found) */
1297 - (NSString *)pathOfLoadedBundleProvidingResource:(id)_resourceName
1298 ofType:(NSString *)_type
1299 resourceSelector:(NGBundleResourceSelector)_selector context:(void *)_context
1301 NSMapEnumerator menum;
1303 NSDictionary *bundleInfo;
1306 rnKeys = ([_resourceName respondsToSelector:@selector(objectForKey:)])
1307 ? [_resourceName allKeys]
1310 menum = NSEnumerateMapTable(self->pathToBundleInfo);
1311 while (NSNextMapEnumeratorPair(&menum, (void *)&path, (void *)&bundleInfo)) {
1313 NSLog(@"check loaded bundle for resource %@: %@", _resourceName,
1317 if ([self _doesBundleInfo:bundleInfo path:path
1318 provideResource:_resourceName ofType:_type rnKeys:rnKeys
1319 resourceSelector:_selector context:_context])
1320 /* strip bundle-info.plist name */
1321 return [path stringByDeletingLastPathComponent];
1327 - (NSString *)pathForBundleProvidingResource:(id)_resourceName
1328 ofType:(NSString *)_type
1329 resourceSelector:(NGBundleResourceSelector)_selector
1330 context:(void *)_context
1332 /* main path lookup method */
1333 // TODO: this method seriously needs some refactoring
1337 NSArray *rnKeys = nil;
1341 NSLog(@"BM LOOKUP path (%d bundles loaded): %@ / %@",
1342 NSCountMapTable(self->loadedBundles), _resourceName, _type);
1345 /* look in loaded bundles */
1347 path = [self pathOfLoadedBundleProvidingResource:_resourceName ofType:_type
1348 resourceSelector:_selector context:_context];
1349 if (path != nil) return path;
1351 /* look in filesystem */
1353 if ([_resourceName respondsToSelector:@selector(objectForKey:)]) {
1354 rnKeys = [_resourceName allKeys];
1355 rnKeyCount = [rnKeys count];
1358 fm = [NSFileManager defaultManager];
1359 e = [self->bundleSearchPaths objectEnumerator];
1360 while ((path = [e nextObject]) != nil) {
1366 if (![fm fileExistsAtPath:path isDirectory:&isDir])
1369 if (!isDir) continue;
1371 /* check whether an appropriate bundle is contained in 'path' */
1373 dir = [[fm directoryContentsAtPath:path] objectEnumerator];
1374 while ((tmp = [dir nextObject])) {
1375 NSDictionary *bundleInfo = nil;
1378 tmp = [path stringByAppendingPathComponent:tmp];
1379 infoPath = [self makeBundleInfoPath:tmp];
1382 NSLog(@"check path path=%@ info=%@", tmp, infoPath);
1384 if ((bundleInfo = NSMapGet(self->pathToBundleInfo, infoPath)) == nil) {
1385 if (![fm fileExistsAtPath:infoPath])
1388 bundleInfo = [self _loadBundleInfoAtExistingPath:infoPath];
1391 NSLog(@"found info for path=%@ info=%@: %@", tmp,infoPath,bundleInfo);
1393 if ([self _doesBundleInfo:bundleInfo path:tmp
1394 provideResource:_resourceName ofType:_type rnKeys:rnKeys
1395 resourceSelector:_selector context:_context])
1399 /* check for direct match */
1401 tmp = [self makeBundleInfoPath:path];
1403 if ((info = NSMapGet(self->pathToBundleInfo, tmp)) == nil) {
1404 if ([fm fileExistsAtPath:tmp])
1405 info = [self _loadBundleInfoAtExistingPath:tmp];
1407 NSLog(@"WARNING(%s): did not find direct path '%@'",
1408 __PRETTY_FUNCTION__, tmp);
1413 // direct match (a bundle was specified in the path)
1414 NSEnumerator *providedResources;
1415 NSDictionary *provides;
1417 provides = [(NSDictionary *)info objectForKey:@"provides"];
1418 providedResources = [[provides objectForKey:_type] objectEnumerator];
1420 if (providedResources == nil) continue;
1422 // scan provide array
1423 while ((info = [providedResources nextObject])) {
1425 if (!_doesInfoMatch(rnKeys, _resourceName, info))
1431 name = [[(NSDictionary *)info objectForKey:@"name"] stringValue];
1432 if (name == nil) continue;
1433 if (![name isEqualToString:_resourceName]) continue;
1437 if (!_selector(_resourceName, _type, tmp, info, self, _context))
1440 /* all conditions applied */
1448 - (NSBundle *)bundleProvidingResource:(id)_name ofType:(NSString *)_type {
1451 if (debugOn) NSLog(@"BM LOOKUP: %@ / %@", _name, _type);
1453 bp = [self pathForBundleProvidingResource:_name
1455 resourceSelector:NULL context:nil];
1456 if ([bp length] == 0) {
1457 #if (NeXT_Foundation_LIBRARY || APPLE_Foundation_LIBRARY) && HEAVY_DEBUG
1458 NSLog(@"%s: found no resource '%@' of type '%@' ...",
1459 __PRETTY_FUNCTION__, _resourceName, _resourceType);
1461 if (debugOn) NSLog(@" did not find: %@ / %@", _name, _type);
1465 if (debugOn) NSLog(@" FOUND: %@", bp);
1466 return [self bundleWithPath:bp];
1469 - (NSArray *)bundlesProvidingResource:(id)_resourceName
1470 ofType:(NSString *)_type
1473 NSMutableArray *bundles;
1476 paths = [self pathsForBundlesProvidingResource:_resourceName
1478 resourceSelector:NULL context:nil];
1480 count = [paths count];
1481 if (paths == nil) return nil;
1482 if (count == 0) return paths;
1484 bundles = [NSMutableArray arrayWithCapacity:count];
1485 for (i = 0; i < count; i++) {
1488 if ((bundle = [self bundleWithPath:[paths objectAtIndex:i]]))
1489 [bundles addObject:bundle];
1491 return [[bundles copy] autorelease];
1494 - (NSArray *)providedResourcesOfType:(NSString *)_resourceType
1495 inBundle:(NSBundle *)_bundle
1498 NSDictionary *bundleInfo;
1500 path = [self makeBundleInfoPath:[_bundle bundlePath]];
1501 if (path == nil) return nil;
1503 /* retrieve bundle info dictionary */
1504 if ((bundleInfo = NSMapGet(self->pathToBundleInfo, path)) == nil)
1505 bundleInfo = [self _loadBundleInfoAtExistingPath:path];
1507 return [(NSDictionary *)[bundleInfo objectForKey:@"provides"]
1508 objectForKey:_resourceType];
1511 - (void)_addRegisteredProvidedResourcesOfType:(NSString *)_type
1512 toSet:(NSMutableSet *)_result
1514 NSMapEnumerator menum;
1516 NSDictionary *bundleInfo;
1518 menum = NSEnumerateMapTable(self->pathToBundleInfo);
1519 while (NSNextMapEnumeratorPair(&menum, (void *)&path, (void *)&bundleInfo)) {
1520 NSArray *providedResources;
1523 NSLog(@"check loaded bundle for resource types %@: %@", _type, path);
1526 [(NSDictionary *)[bundleInfo objectForKey:@"provides"]
1527 objectForKey:_type];
1528 if (providedResources == nil) continue;
1530 [_result addObjectsFromArray:providedResources];
1534 - (NSArray *)providedResourcesOfType:(NSString *)_resourceType {
1535 NSMutableSet *result = nil;
1536 NSFileManager *fm = [NSFileManager defaultManager];
1540 result = [NSMutableSet setWithCapacity:128];
1542 /* scan loaded bundles */
1544 [self _addRegisteredProvidedResourcesOfType:_resourceType toSet:result];
1546 /* scan all bundle search paths */
1548 e = [self->bundleSearchPaths objectEnumerator];
1549 while ((path = [e nextObject])) {
1555 if (![fm fileExistsAtPath:path isDirectory:&isDir])
1557 if (!isDir) continue;
1559 /* check whether an appropriate bundle is contained in 'path' */
1561 dir = [[fm directoryContentsAtPath:path] objectEnumerator];
1562 while ((tmp = [dir nextObject])) {
1563 NSDictionary *bundleInfo = nil;
1564 NSArray *providedResources = nil;
1567 tmp = [path stringByAppendingPathComponent:tmp];
1568 infoPath = [self makeBundleInfoPath:tmp];
1571 NSLog(@" info path: %@", tmp);
1574 if ((bundleInfo = NSMapGet(self->pathToBundleInfo, infoPath)) == nil) {
1575 if (![fm fileExistsAtPath:infoPath])
1578 bundleInfo = [self _loadBundleInfoAtExistingPath:infoPath];
1582 [(NSDictionary *)[bundleInfo objectForKey:@"provides"]
1583 objectForKey:_resourceType];
1584 if (providedResources == nil) continue;
1586 [result addObjectsFromArray:providedResources];
1589 /* check for direct match */
1591 tmp = [self makeBundleInfoPath:path];
1593 if ((info = NSMapGet(self->pathToBundleInfo, tmp)) == nil) {
1594 if ([fm fileExistsAtPath:tmp])
1595 info = [self _loadBundleInfoAtExistingPath:tmp];
1599 // direct match (a bundle was specified in the path)
1600 NSArray *providedResources;
1601 NSDictionary *provides;
1603 provides = [(NSDictionary *)info objectForKey:@"provides"];
1604 providedResources = [provides objectForKey:_resourceType];
1606 if (providedResources == nil) continue;
1608 [result addObjectsFromArray:providedResources];
1611 return [result allObjects];
1614 - (NSBundle *)bundleProvidingResourceOfType:(NSString *)_resourceType
1615 matchingQualifier:(EOQualifier *)_qual
1617 NSFileManager *fm = [NSFileManager defaultManager];
1621 /* foreach search path entry */
1623 e = [self->bundleSearchPaths objectEnumerator];
1624 while ((path = [e nextObject])) {
1627 if ([fm fileExistsAtPath:path isDirectory:&isDir]) {
1630 if (!isDir) continue;
1632 /* check whether an appropriate bundle is contained in 'path' */
1636 dir = [[fm directoryContentsAtPath:path] objectEnumerator];
1637 while ((tmp = [dir nextObject])) {
1638 NSDictionary *bundleInfo;
1639 NSArray *providedResources;
1642 tmp = [path stringByAppendingPathComponent:tmp];
1643 infoPath = [self makeBundleInfoPath:tmp];
1645 if ((bundleInfo=NSMapGet(self->pathToBundleInfo, infoPath)) == nil) {
1646 if (![fm fileExistsAtPath:infoPath])
1649 bundleInfo = [self _loadBundleInfoAtExistingPath:infoPath];
1652 bundleInfo = [bundleInfo objectForKey:@"provides"];
1653 providedResources = [bundleInfo objectForKey:_resourceType];
1655 if (providedResources == nil) continue;
1658 [providedResources filteredArrayUsingQualifier:_qual];
1660 if ([providedResources count] > 0)
1661 return [self bundleWithPath:tmp];
1665 /* check for direct match */
1667 tmp = [self makeBundleInfoPath:path];
1669 if ((info = NSMapGet(self->pathToBundleInfo, tmp)) == nil) {
1670 if ([fm fileExistsAtPath:tmp])
1671 info = [self _loadBundleInfoAtExistingPath:tmp];
1675 // direct match (a bundle was specified in the path)
1676 NSArray *providedResources;
1677 NSDictionary *provides;
1679 provides = [(NSDictionary *)info objectForKey:@"provides"];
1680 providedResources = [provides objectForKey:_resourceType];
1682 if (providedResources == nil) continue;
1685 [providedResources filteredArrayUsingQualifier:_qual];
1687 if ([providedResources count] > 0)
1688 return [self bundleWithPath:path];
1695 - (NSBundle *)bundlesProvidingResourcesOfType:(NSString *)_resourceType
1696 matchingQualifier:(EOQualifier *)_qual
1698 NSMutableArray *bundles = nil;
1699 NSFileManager *fm = [NSFileManager defaultManager];
1703 bundles = [NSMutableArray arrayWithCapacity:128];
1705 /* foreach search path entry */
1707 e = [self->bundleSearchPaths objectEnumerator];
1708 while ((path = [e nextObject])) {
1711 if ([fm fileExistsAtPath:path isDirectory:&isDir]) {
1714 if (!isDir) continue;
1716 /* check whether an appropriate bundle is contained in 'path' */
1720 dir = [[fm directoryContentsAtPath:path] objectEnumerator];
1721 while ((tmp = [dir nextObject])) {
1722 NSDictionary *bundleInfo = nil;
1723 NSArray *providedResources = nil;
1726 tmp = [path stringByAppendingPathComponent:tmp];
1727 infoPath = [self makeBundleInfoPath:tmp];
1729 if ((bundleInfo=NSMapGet(self->pathToBundleInfo, infoPath)) == nil) {
1730 if (![fm fileExistsAtPath:infoPath])
1733 bundleInfo = [self _loadBundleInfoAtExistingPath:infoPath];
1736 bundleInfo = [bundleInfo objectForKey:@"provides"];
1737 providedResources = [bundleInfo objectForKey:_resourceType];
1739 if (providedResources == nil) continue;
1742 [providedResources filteredArrayUsingQualifier:_qual];
1744 if ([providedResources count] > 0)
1745 [bundles addObject:[self bundleWithPath:tmp]];
1749 /* check for direct match */
1751 tmp = [self makeBundleInfoPath:path];
1753 if ((info = NSMapGet(self->pathToBundleInfo, tmp)) == nil) {
1754 if ([fm fileExistsAtPath:tmp])
1755 info = [self _loadBundleInfoAtExistingPath:tmp];
1759 // direct match (a bundle was specified in the path)
1760 NSArray *providedResources;
1761 NSDictionary *provides;
1763 provides = [(NSDictionary *)info objectForKey:@"provides"];
1764 providedResources = [provides objectForKey:_resourceType];
1766 if (providedResources == nil) continue;
1769 [providedResources filteredArrayUsingQualifier:_qual];
1771 if ([providedResources count] > 0)
1772 [bundles addObject:[self bundleWithPath:path]];
1776 return [[bundles copy] autorelease];
1781 - (void)_bundleDidLoadNotifcation:(NSNotification *)_notification {
1782 NSDictionary *ui = [_notification userInfo];
1785 NSLog(@"bundle %@ did load with classes %@",
1786 [[_notification object] bundlePath],
1787 [ui objectForKey:@"NSLoadedClasses"]);
1790 [self registerBundle:[_notification object]
1791 classes:[ui objectForKey:@"NSLoadedClasses"]
1792 categories:[ui objectForKey:@"NSLoadedCategories"]];
1797 - (BOOL)isDebuggingEnabled {
1801 @end /* NGBundleManager */
1803 @implementation NSBundle(BundleManagerSupport)
1806 return [NGBundle alloc];
1808 + (id)allocWithZone:(NSZone *)zone {
1809 return [NGBundle allocWithZone:zone];
1812 #if !(NeXT_Foundation_LIBRARY || APPLE_Foundation_LIBRARY)
1813 //#warning remember, bundleForClass is not overridden !
1815 + (NSBundle *)bundleForClass:(Class)aClass {
1816 return [[NGBundleManager defaultBundleManager] bundleForClass:aClass];
1819 + (NSBundle *)bundleWithPath:(NSString*)path {
1820 return [[NGBundleManager defaultBundleManager] bundleWithPath:path];
1824 @end /* NSBundle(BundleManagerSupport) */
1826 @implementation NSBundle(NGBundleManagerExtensions)
1828 - (id)principalObject {
1829 return [[NGBundleManager defaultBundleManager]
1830 principalObjectOfBundle:self];
1833 - (NSArray *)providedResourcesOfType:(NSString *)_resourceType {
1834 return [[NGBundleManager defaultBundleManager]
1835 providedResourcesOfType:_resourceType
1839 - (NSString *)bundleName {
1840 return [[[self bundlePath] lastPathComponent] stringByDeletingPathExtension];
1843 - (NSString *)bundleType {
1844 return [[self bundlePath] pathExtension];
1847 - (NSArray *)providedClasses {
1848 return [[NGBundleManager defaultBundleManager] classesProvidedByBundle:self];
1851 - (NSArray *)requiredClasses {
1852 return [[NGBundleManager defaultBundleManager] classesRequiredByBundle:self];
1855 - (NSArray *)requiredBundles {
1856 return [[NGBundleManager defaultBundleManager] bundlesRequiredByBundle:self];
1859 - (NSDictionary *)configForResource:(id)_resource ofType:(NSString *)_type {
1860 return [[NGBundleManager defaultBundleManager]
1861 configForResource:_resource ofType:_type
1862 providedByBundle:self];
1867 - (BOOL)_loadForBundleManager:(NGBundleManager *)_manager {
1871 @end /* NSBundle(NGBundleManagerExtensions) */
1873 @implementation NSBundle(NGLanguageResourceExtensions)
1875 // locating resources
1877 - (NSString *)pathForResource:(NSString *)_name ofType:(NSString *)_ext
1878 inDirectory:(NSString *)_directory
1879 languages:(NSArray *)_languages
1881 NSFileManager *fm = [NSFileManager defaultManager];
1882 NSString *path = nil;
1884 id (*objAtIdx)(id,SEL,int);
1887 ? [[self bundlePath] stringByAppendingPathComponent:_directory]
1888 : [self bundlePath];
1890 if (![fm fileExistsAtPath:path])
1893 if (_ext) _name = [_name stringByAppendingPathExtension:_ext];
1895 langCount = [_languages count];
1896 objAtIdx = (langCount > 0)
1897 ? (void*)[_languages methodForSelector:@selector(objectAtIndex:)]
1900 for (i = 0; i < langCount; i++) {
1905 ? objAtIdx(_languages, @selector(objectAtIndex:), i)
1906 : [_languages objectAtIndex:i];
1908 language = [language stringByAppendingPathExtension:@"lproj"];
1909 lpath = [path stringByAppendingPathComponent:language];
1910 lpath = [lpath stringByAppendingPathComponent:_name];
1912 if ([fm fileExistsAtPath:lpath])
1916 /* now look into x.bundle/Resources/name.type */
1917 if ([fm fileExistsAtPath:[path stringByAppendingPathComponent:_name]])
1918 return [path stringByAppendingPathComponent:_name];
1923 - (NSString *)pathForResource:(NSString *)_name ofType:(NSString *)_ext
1924 languages:(NSArray *)_languages
1928 path = [self pathForResource:_name ofType:_ext
1929 inDirectory:@"Resources"
1930 languages:_languages];
1931 if (path) return path;
1933 path = [self pathForResource:_name ofType:_ext
1935 languages:_languages];
1939 @end /* NSBundle(NGLanguageResourceExtensions) */
1941 @implementation NGBundle
1944 return [self allocWithZone:NULL];
1946 + (id)allocWithZone:(NSZone*)zone {
1947 return NSAllocateObject(self, 0, zone);
1950 - (id)initWithPath:(NSString *)__path {
1951 return [super initWithPath:__path];
1956 - (BOOL)_loadForBundleManager:(NGBundleManager *)_manager {
1957 return [super load];
1961 NGBundleManager *bm;
1963 bm = [NGBundleManager defaultBundleManager];
1965 return [bm loadBundle:self] ? YES : NO;
1968 + (NSBundle *)bundleForClass:(Class)aClass {
1969 return [[NGBundleManager defaultBundleManager] bundleForClass:aClass];
1971 + (NSBundle *)bundleWithPath:(NSString*)path {
1972 return [[NGBundleManager defaultBundleManager] bundleWithPath:path];
1975 #if GNUSTEP_BASE_LIBRARY
1977 - (Class)principalClass {
1981 if ((c = [super principalClass]))
1984 if ((cname = [[self infoDictionary] objectForKey:@"NSPrincipalClass"]) ==nil)
1987 if ((c = NSClassFromString(cname)))
1990 NSLog(@"%s: did not find principal class named '%@' of bundle %@",
1991 __PRETTY_FUNCTION__, cname, self);
1997 - (NSString *)description {
2001 "<%s %p fullPath: %s infoDictionary: %p loaded=%s>",
2002 (char*)object_get_class_name(self),
2004 [[self bundlePath] cString],
2005 [self infoDictionary],
2006 self->_codeLoaded ? "yes" : "no");
2008 return [NSString stringWithCString:buffer];