2 Copyright (C) 2000-2006 SKYRIX Software AG
3 Copyright (C) 2006 Helge Hess
5 This file is part of SOPE.
7 SOPE is free software; you can redistribute it and/or modify it under
8 the terms of the GNU Lesser General Public License as published by the
9 Free Software Foundation; either version 2, or (at your option) any
12 SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
15 License for more details.
17 You should have received a copy of the GNU Lesser General Public
18 License along with SOPE; see the file COPYING. If not, write to the
19 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
23 #include "NGBundleManager.h"
25 #include <NGExtensions/NSObject+Logs.h>
26 #include <NGExtensions/NSNull+misc.h>
27 #import <Foundation/NSFileManager.h>
28 #import <EOControl/EOQualifier.h>
31 #if 0 && GNUSTEP_BASE_LIBRARY /* supported in repository since 19990916-2254-MET */
32 /* hack until GNUstep provides the necessary callbacks */
33 # define NSNonRetainedObjectMapValueCallBacks NSObjectMapValueCallBacks
36 #if NeXT_Foundation_LIBRARY || COCOA_Foundation_LIBRARY
37 # include <NGExtensions/NGPropertyListParser.h>
40 #if LIB_FOUNDATION_LIBRARY
41 @interface NSBundle(UsedPrivates)
42 + (BOOL)isFlattenedDirLayout;
46 #if NeXT_RUNTIME || APPLE_RUNTIME
48 #include <objc/objc-runtime.h>
50 //OBJC_EXPORT void objc_setClassHandler(int (*)(const char *));
52 static BOOL debugClassHook = NO;
53 static BOOL hookDoLookup = YES;
55 static int _getClassHook(const char *className) {
57 if (className == NULL) return 0;
60 printf("lookup class '%s'.\n", className);
62 if (objc_lookUpClass(className))
66 static NGBundleManager *manager = nil;
71 printf("%s: look for class %s\n", __PRETTY_FUNCTION__, className);
73 manager = [NGBundleManager defaultBundleManager];
75 cns = [[NSString alloc] initWithCString:className];
76 bundle = [manager bundleForClassNamed:cns];
77 [cns release]; cns = nil;
81 NSLog(@"%s: found bundle %@", __PRETTY_FUNCTION__,
85 if (![manager loadBundle:bundle]) {
87 "bundleManager couldn't load bundle for class '%s'.\n",
92 Class c = objc_lookUpClass(className);
93 NSLog(@"%s: loaded bundle %@ for className %s class %@",
95 bundle, className, c);
107 #include <objc/objc-api.h>
109 static Class (*oldClassLoadHook)(const char *_name) = NULL;
111 static inline BOOL _isValidClassName(const char *_name) {
114 if (_name == NULL) return NO;
116 for (len = 0; (len < 256) && (*_name != '\0'); len++, _name++) {
118 if (!isalnum((int)*_name))
122 return (len == 256) ? NO : YES;
125 static Class _classLoadHook(const char *_name) {
126 if (_isValidClassName(_name)) {
127 static NGBundleManager *manager = nil;
130 //NSLog(@"%s: look for class %s", __PRETTY_FUNCTION__, _name);
132 manager = [NGBundleManager defaultBundleManager];
134 bundle = [manager bundleForClassNamed:[NSString stringWithCString:_name]];
137 NSLog(@"%s: found bundle %@", __PRETTY_FUNCTION__, [bundle bundlePath]);
140 if ([manager loadBundle:bundle]) {
144 hook = _objc_lookup_class;
145 _objc_lookup_class = NULL;
146 clazz = objc_lookup_class(_name);
147 _objc_lookup_class = hook;
149 if (clazz) return clazz;
153 return (oldClassLoadHook != NULL) ? oldClassLoadHook(_name) : Nil;
155 #endif // GNU_RUNTIME
157 NSString *NGBundleWasLoadedNotificationName = @"NGBundleWasLoadedNotification";
159 @interface NSBundle(NGBundleManagerPrivate)
160 - (BOOL)_loadForBundleManager:(NGBundleManager *)_manager;
163 @interface NGBundleManager(PrivateMethods)
165 - (void)registerBundle:(NSBundle *)_bundle
166 classes:(NSArray *)_classes
167 categories:(NSArray *)_categories;
169 - (NSString *)pathForBundleProvidingResource:(NSString *)_resourceName
170 ofType:(NSString *)_type
171 resourceSelector:(NGBundleResourceSelector)_selector
172 context:(void *)_ctx;
174 - (NSString *)makeBundleInfoPath:(NSString *)_path;
178 static BOOL _selectClassByVersion(NSString *_resourceName,
179 NSString *_resourceType,
181 NSDictionary *_resourceConfig,
182 NGBundleManager *_bundleManager,
188 if (![_resourceType isEqualToString:@"classes"])
191 if (_version == NULL)
193 if ([(id)_version intValue] == -1)
196 if ((tmp = [_resourceConfig objectForKey:@"version"])) {
197 classVersion = [tmp intValue];
199 if (classVersion < [(id)_version intValue]) {
200 NSLog(@"WARNING: class version mismatch for class %@: "
201 @"requested at least version %i, got version %i",
202 _resourceName, [(id)_version intValue], classVersion);
205 if ((tmp = [_resourceConfig objectForKey:@"exact-version"])) {
206 classVersion = [tmp intValue];
208 if (classVersion != [(id)_version intValue]) {
209 NSLog(@"WARNING: class version mismatch for class %@: "
210 @"requested exact version %i, got version %i",
211 _resourceName, [(id)_version intValue], classVersion);
217 @implementation NGBundleManager
220 static NGBundleManager *defaultManager = nil;
221 static BOOL debugOn = NO;
223 #if defined(__MINGW32__)
224 static NSString *NGEnvVarPathSeparator = @";";
226 static NSString *NGEnvVarPathSeparator = @":";
230 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
232 debugOn = [ud boolForKey:@"NGBundleManagerDebugEnabled"];
237 if (_objc_lookup_class != _classLoadHook) {
238 oldClassLoadHook = _objc_lookup_class;
239 _objc_lookup_class = _classLoadHook;
244 + (id)defaultBundleManager {
245 if (defaultManager == nil) {
246 defaultManager = [[NGBundleManager alloc] init];
248 #if NeXT_RUNTIME || APPLE_RUNTIME
250 static BOOL didRegisterCallback = NO;
252 if (!didRegisterCallback) {
253 didRegisterCallback = YES;
254 objc_setClassHandler(_getClassHook);
258 if (_objc_lookup_class != _classLoadHook) {
259 oldClassLoadHook = _objc_lookup_class;
260 _objc_lookup_class = _classLoadHook;
264 return defaultManager;
267 /* setup bundle search path */
269 - (void)_addMainBundlePathToPathArray:(NSMutableArray *)_paths {
273 pi = [NSProcessInfo processInfo];
274 path = [[pi arguments] objectAtIndex:0];
275 path = [path stringByDeletingLastPathComponent];
277 if ([path isEqual:@""])
281 // TODO: to be correct this would need to read the bundle-info
284 The path is the complete path to the executable, including the
285 processor, the OS and the library combo. Strip these directories
286 from the main bundle's path.
288 #if LIB_FOUNDATION_LIBRARY
289 if (![NSBundle isFlattenedDirLayout])
291 path = [[[path stringByDeletingLastPathComponent]
292 stringByDeletingLastPathComponent]
293 stringByDeletingLastPathComponent];
296 [_paths addObject:path];
299 - (void)_addBundlePathDefaultToPathArray:(NSMutableArray *)_paths {
303 if ((ud = [NSUserDefaults standardUserDefaults]) == nil) {
304 // got this with gstep-base during the port, apparently it happens
305 // if the bundle manager is created inside the setup process of
306 // gstep-base (for whatever reason)
307 NSLog(@"ERROR(NGBundleManager): got no system userdefaults object!");
313 if ((paths = [ud arrayForKey:@"NGBundlePath"]) == nil) {
314 if ((paths = [ud stringForKey:@"NGBundlePath"]) != nil)
315 paths = [paths componentsSeparatedByString:NGEnvVarPathSeparator];
318 [_paths addObjectsFromArray:paths];
320 NSLog(@"Note: NGBundlePath default is not configured.");
323 - (void)_addEnvironmentPathToPathArray:(NSMutableArray *)_paths {
327 pi = [NSProcessInfo processInfo];
328 paths = [[pi environment] objectForKey:@"NGBundlePath"];
330 paths = [paths componentsSeparatedByString:NGEnvVarPathSeparator];
331 if (paths) [_paths addObjectsFromArray:paths];
334 - (void)_addGNUstepPathsToPathArray:(NSMutableArray *)_paths {
337 // TODO: whats that supposed to do?
338 // TODO: replace with proper path locator function!
344 env = [[NSProcessInfo processInfo] environment];
346 if ((tmp = [env objectForKey:@"GNUSTEP_PATHPREFIX_LIST"]) == nil)
347 tmp = [env objectForKey:@"GNUSTEP_PATHLIST"];
348 tmp = [tmp componentsSeparatedByString:@":"];
350 for (i = 0, count = [tmp count]; i < count; i++) {
351 p = [tmp objectAtIndex:i];
352 p = [p stringByAppendingPathComponent:@"Library"];
353 p = [p stringByAppendingPathComponent:@"Bundles"];
354 if ([self->bundleSearchPaths containsObject:p]) continue;
356 if (p) [self->bundleSearchPaths addObject:p];
361 - (void)_setupBundleSearchPathes {
364 pi = [NSProcessInfo processInfo];
366 /* setup bundle search path */
368 self->bundleSearchPaths = [[NSMutableArray alloc] initWithCapacity:16];
370 [self _addMainBundlePathToPathArray:self->bundleSearchPaths];
371 [self _addBundlePathDefaultToPathArray:self->bundleSearchPaths];
372 [self _addEnvironmentPathToPathArray:self->bundleSearchPaths];
373 [self _addGNUstepPathsToPathArray:self->bundleSearchPaths];
375 #if DEBUG && NeXT_Foundation_LIBRARY && 0
376 NSLog(@"%s: bundle search pathes:\n%@", __PRETTY_FUNCTION__,
377 self->bundleSearchPaths);
381 - (void)_registerLoadedBundles {
382 NSEnumerator *currentBundles;
383 NSBundle *loadedBundle;
385 currentBundles = [[NSBundle allBundles] objectEnumerator];
386 while ((loadedBundle = [currentBundles nextObject]) != nil)
387 [self registerBundle:loadedBundle classes:nil categories:nil];
390 - (void)_registerForBundleLoadNotification {
391 [[NSNotificationCenter defaultCenter]
393 selector:@selector(_bundleDidLoadNotifcation:)
394 name:@"NSBundleDidLoadNotification"
399 #if GNUSTEP_BASE_LIBRARY
400 if ([NSUserDefaults standardUserDefaults] == nil) {
401 /* called inside setup process, deny creation (HACK) */
407 if ((self = [super init])) {
408 self->classToBundle =
409 NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks,
410 NSNonRetainedObjectMapValueCallBacks,
412 self->classNameToBundle =
413 NSCreateMapTable(NSObjectMapKeyCallBacks,
414 NSNonRetainedObjectMapValueCallBacks,
416 self->categoryNameToBundle =
417 NSCreateMapTable(NSObjectMapKeyCallBacks,
418 NSNonRetainedObjectMapValueCallBacks,
421 NSCreateMapTable(NSObjectMapKeyCallBacks,
422 NSNonRetainedObjectMapValueCallBacks,
424 self->pathToBundleInfo =
425 NSCreateMapTable(NSObjectMapKeyCallBacks,
426 NSObjectMapValueCallBacks,
429 NSCreateMapTable(NSObjectMapKeyCallBacks,
430 NSNonRetainedObjectMapValueCallBacks,
432 self->loadedBundles =
433 NSCreateMapTable(NSNonRetainedObjectMapKeyCallBacks,
434 NSObjectMapValueCallBacks,
437 [self _setupBundleSearchPathes];
438 [self _registerLoadedBundles];
439 [self _registerForBundleLoadNotification];
445 [self->loadingBundles release];
446 if (self->loadedBundles) NSFreeMapTable(self->loadedBundles);
447 if (self->classToBundle) NSFreeMapTable(self->classToBundle);
448 if (self->classNameToBundle) NSFreeMapTable(self->classNameToBundle);
449 if (self->categoryNameToBundle) NSFreeMapTable(self->categoryNameToBundle);
450 if (self->pathToBundle) NSFreeMapTable(self->pathToBundle);
451 if (self->pathToBundleInfo) NSFreeMapTable(self->pathToBundleInfo);
452 if (self->nameToBundle) NSFreeMapTable(self->nameToBundle);
453 [self->bundleSearchPaths release];
459 - (void)setBundleSearchPaths:(NSArray *)_paths {
460 ASSIGNCOPY(self->bundleSearchPaths, _paths);
462 - (NSArray *)bundleSearchPaths {
463 return self->bundleSearchPaths;
466 /* registering bundles */
468 - (void)registerBundle:(NSBundle *)_bundle
469 classes:(NSArray *)_classes
470 categories:(NSArray *)_categories
475 #if NeXT_RUNTIME || APPLE_RUNTIME
476 v = [_bundle bundlePath];
477 if ([v hasSuffix:@"Libraries"] || [v hasSuffix:@"Tools"]) {
479 fprintf(stderr, "INVALID BUNDLE: %s\n", [[_bundle bundlePath] cString]);
485 NSLog(@"NGBundleManager: register loaded bundle %@", [_bundle bundlePath]);
488 e = [_classes objectEnumerator];
489 while ((v = [e nextObject]) != nil) {
490 #if NeXT_RUNTIME || APPLE_RUNTIME
494 NSMapInsert(self->classToBundle, NSClassFromString(v), _bundle);
495 NSMapInsert(self->classNameToBundle, v, _bundle);
497 #if NeXT_RUNTIME || APPLE_RUNTIME
502 e = [_categories objectEnumerator];
503 while ((v = [e nextObject]) != nil)
504 NSMapInsert(self->categoryNameToBundle, v, _bundle);
509 - (NSString *)pathForBundleWithName:(NSString *)_name type:(NSString *)_type {
510 NSFileManager *fm = [NSFileManager defaultManager];
513 NSString *bundlePath;
516 /* first check in table */
519 bundlePath = [_name stringByAppendingPathExtension:_type];
521 if ((bundle = NSMapGet(self->nameToBundle, bundlePath)))
522 return [bundle bundlePath];
524 e = [self->bundleSearchPaths objectEnumerator];
525 while ((path = [e nextObject])) {
528 if ([fm fileExistsAtPath:path isDirectory:&isDir]) {
529 if (!isDir) continue;
531 if ([[path lastPathComponent] isEqualToString:bundlePath]) {
532 // direct match (a bundle was specified in the path)
538 tmp = [path stringByAppendingPathComponent:bundlePath];
539 if ([fm fileExistsAtPath:tmp isDirectory:&isDir]) {
550 /* getting bundles */
552 - (NSBundle *)bundleForClass:(Class)aClass {
553 /* this method never loads a dynamic bundle (since the class is set up) */
559 bundle = NSMapGet(self->classToBundle, aClass);
561 #if NeXT_Foundation_LIBRARY || COCOA_Foundation_LIBRARY
565 bundle = [NSBundle bundleForClass:aClass];
566 if (bundle == [NSBundle mainBundle])
569 p = [bundle bundlePath];
570 if ([p hasSuffix:@"Libraries"]) {
572 fprintf(stderr, "%s: Dylib bundle: 0x%p: %s\n",
574 bundle, [[bundle bundlePath] cString]);
578 else if ([p hasSuffix:@"Tools"]) {
580 fprintf(stderr, "%s: Tool bundle: 0x%p: %s\n",
582 bundle, [[bundle bundlePath] cString]);
591 if the class wasn't loaded from a bundle, it's *either* the main bundle
592 or a bundle loaded before NGExtension was loaded !!!
595 #if !LIB_FOUNDATION_LIBRARY && !GNUSTEP_BASE_LIBRARY
596 // Note: incorrect behaviour if NGExtensions is dynamically loaded !
597 // TODO: can we do anything about this? Can we detect the situation and
598 // print a log instead of the compile warning?
599 // Note: the above refers to the situation when a framework is implicitly
600 // loaded by loading a bundle (the framework is not linked against
603 bundle = [NSBundle mainBundle];
604 NSMapInsert(self->classToBundle, aClass, bundle);
605 NSMapInsert(self->classNameToBundle, NSStringFromClass(aClass), bundle);
609 - (NSBundle *)bundleWithPath:(NSString *)path {
610 NSBundle *bundle = nil;
613 path = [path stringByResolvingSymlinksInPath];
617 if (debugOn) NSLog(@"find bundle for path: '%@'", path);
618 bundle = NSMapGet(self->pathToBundle, path);
621 if (debugOn) NSLog(@" found: %@", bundle);
625 if ((bundle = [(NGBundle *)[NGBundle alloc] initWithPath:path]) == nil) {
626 NSLog(@"ERROR(%s): could not create bundle for path: '%@'",
627 __PRETTY_FUNCTION__, path);
631 bn = [[bundle bundleName]
632 stringByAppendingPathExtension:[bundle bundleType]],
634 NSMapInsert(self->pathToBundle, path, bundle);
635 NSMapInsert(self->nameToBundle, bn, bundle);
639 - (NSBundle *)bundleWithName:(NSString *)_name type:(NSString *)_type {
643 bn = [_name stringByAppendingPathExtension:_type];
644 bundle = NSMapGet(self->nameToBundle, bn);
646 if (![bundle isNotNull]) {
647 bundle = [self bundleWithPath:
648 [self pathForBundleWithName:_name type:_type]];
651 if (![bundle isNotNull]) /* NSNull is used to signal missing bundles */
654 if (![[bundle bundleType] isEqualToString:_type])
660 - (NSBundle *)bundleWithName:(NSString *)_name {
661 return [self bundleWithName:_name type:@"bundle"];
664 - (NSBundle *)bundleForClassNamed:(NSString *)_className {
665 NSString *path = nil;
666 NSBundle *bundle = nil;
668 if (_className == nil)
671 /* first check in table */
673 if ((bundle = NSMapGet(self->classNameToBundle, _className)) != nil)
677 /* then look in runtime, reset load callback to avoid recursion */
683 loadCallback = _objc_lookup_class;
684 _objc_lookup_class = NULL;
685 clazz = NSClassFromString(_className);
686 _objc_lookup_class = loadCallback;
689 /* the class is already loaded */
690 bundle = [self bundleForClass:clazz];
691 NSMapInsert(self->classNameToBundle, _className, bundle);
695 #elif NeXT_RUNTIME || APPLE_RUNTIME
699 hookDoLookup = NO; // THREAD
700 clazz = NSClassFromString(_className);
704 /* the class is already loaded */
706 printf("found class in runtime: %s\n", [_className cString]);
708 bundle = [self bundleForClass:clazz];
709 NSMapInsert(self->classNameToBundle, _className, bundle);
714 printf("did NOT find class in runtime: %s\n", [_className cString]);
719 path = [self pathForBundleProvidingResource:_className
721 resourceSelector:_selectClassByVersion
722 context:NULL /* version */];
724 path = [path stringByResolvingSymlinksInPath];
725 NSAssert(path, @"couldn't resolve symlinks in path ..");
731 if ((bundle = [self bundleWithPath:path]) != nil)
732 NSMapInsert(self->classNameToBundle, _className, bundle);
743 - (NSArray *)bundlesRequiredByBundle:(NSBundle *)_bundle {
744 [self doesNotRecognizeSelector:_cmd];
748 - (NSArray *)classesProvidedByBundle:(NSBundle *)_bundle {
749 return [[_bundle providedResourcesOfType:@"classes"] valueForKey:@"name"];
751 - (NSArray *)classesRequiredByBundle:(NSBundle *)_bundle {
752 [self doesNotRecognizeSelector:_cmd];
758 - (NSString *)makeBundleInfoPath:(NSString *)_path {
759 #if (NeXT_Foundation_LIBRARY || APPLE_Foundation_LIBRARY) && !defined(GSWARN)
760 return [[[_path stringByAppendingPathComponent:@"Contents"]
761 stringByAppendingPathComponent:@"Resources"]
762 stringByAppendingPathComponent:@"bundle-info.plist"];
764 return [_path stringByAppendingPathComponent:@"bundle-info.plist"];
768 - (id)_initializeLoadedBundle:(NSBundle *)_bundle
769 info:(NSDictionary *)_bundleInfo
773 /* check whether a handler was specified */
775 if ((handler = [_bundleInfo objectForKey:@"bundleHandler"]) != nil) {
776 [self debugWithFormat:@"lookup bundle handler %@ of bundle: %@",
779 if ((handler = NSClassFromString(handler)) == nil) {
780 NSLog(@"ERROR: did not find handler class %@ of bundle %@.",
781 [_bundleInfo objectForKey:@"bundleHandler"], [_bundle bundlePath]);
782 handler = [_bundle principalClass];
785 handler = [handler alloc];
787 if ([handler respondsToSelector:@selector(initForBundle:bundleManager:)])
788 handler = [handler initForBundle:_bundle bundleManager:self];
790 handler = [handler init];
791 handler = [handler autorelease];
793 if (handler == nil) {
794 NSLog(@"ERROR: could not instantiate handler class %@ of bundle %@.",
795 [_bundleInfo objectForKey:@"bundleHandler"], [_bundle bundlePath]);
796 handler = [_bundle principalClass];
800 [self debugWithFormat:
801 @"no bundle handler, lookup principal class of bundle: %@",
803 if ((handler = [_bundle principalClass]) == nil) {
804 /* use NGBundle class as default bundle handler */
805 #if !(NeXT_Foundation_LIBRARY || COCOA_Foundation_LIBRARY)
806 [self warnWithFormat:@"bundle has no principal class: %@", _bundle];
808 handler = [NGBundle class];
811 [self debugWithFormat:@" => %@", handler];
819 - (NSDictionary *)_loadBundleInfoAtExistingPath:(NSString *)_path {
820 NSDictionary *bundleInfo;
823 #if NeXT_Foundation_LIBRARY || COCOA_Foundation_LIBRARY
824 bundleInfo = NGParsePropertyListFromFile(_path);
826 bundleInfo = [NSDictionary dictionaryWithContentsOfFile:_path];
828 if (bundleInfo == nil) {
829 NSLog(@"could not load bundle-info at path '%@' !", _path);
833 /* check required bundle manager version */
834 info = [bundleInfo objectForKey:@"requires"];
835 if ((info = [(NSDictionary *)info objectForKey:@"bundleManagerVersion"])) {
836 if ([info intValue] > [[self class] version]) {
837 /* bundle manager version does not match ... */
841 NSMapInsert(self->pathToBundleInfo, _path, bundleInfo);
845 - (NSBundle *)_locateBundleForClassInfo:(NSDictionary *)_classInfo {
849 if (_classInfo == nil)
851 if ((className = [_classInfo objectForKey:@"name"]) == nil) {
852 NSLog(@"ERROR: missing classname in bundle-info.plist class section !");
856 // TODO: do we need to check the runtime for already loaded classes?
857 // Yes, I think so. But avoid recursions
859 #if APPLE_Foundation_LIBRARY || COCOA_Foundation_LIBRARY
860 // TODO: HACK, see above. w/o this, we get issues.
861 if ([className hasPrefix:@"NS"])
866 if ((bundle = [self bundleForClassNamed:className]) == nil) {
867 #if 0 // class might be already loaded
868 NSLog(@"ERROR: did not find class %@ required by bundle %@.",
869 className, [_bundle bundlePath]);
874 NSLog(@"CLASS %@ => BUNDLE %@", className, bundle);
878 - (NSArray *)_locateBundlesForClassInfos:(NSEnumerator *)_classInfos {
879 NSMutableArray *requiredBundles;
882 requiredBundles = [NSMutableArray arrayWithCapacity:16];
883 while ((i = [_classInfos nextObject]) != nil) {
886 if ((bundle = [self _locateBundleForClassInfo:i]) == nil)
889 [requiredBundles addObject:bundle];
891 return requiredBundles;
894 - (BOOL)_preLoadBundle:(NSBundle *)_bundle info:(NSDictionary *)_bundleInfo {
895 /* TODO: split up this huge method */
896 NSDictionary *requires;
897 NSMutableArray *requiredBundles = nil;
898 NSBundle *requiredBundle = nil;
900 if (debugOn) NSLog(@"NGBundleManager: preload bundle: %@", _bundle);
902 requires = [_bundleInfo objectForKey:@"requires"];
905 /* invalid bundle info specified */
908 /* load required bundles */
913 /* locate required bundles */
915 e = [[requires objectForKey:@"bundles"] objectEnumerator];
916 while ((i = [e nextObject]) != nil) {
917 NSString *bundleName;
919 if (![i respondsToSelector:@selector(objectForKey:)]) {
920 NSLog(@"ERROR(%s): invalid bundle-info of bundle %@ !!!\n"
921 @" requires-entry is not a dictionary: %@",
922 __PRETTY_FUNCTION__, _bundle, i);
926 if ((bundleName = [i objectForKey:@"name"])) {
929 type = [i objectForKey:@"type"];
930 if (type == nil) type = @"bundle";
932 if ((requiredBundle = [self bundleWithName:bundleName type:type])) {
933 if (requiredBundles == nil)
934 requiredBundles = [NSMutableArray arrayWithCapacity:16];
936 [requiredBundles addObject:requiredBundle];
939 NSLog(@"ERROR(NGBundleManager): did not find bundle '%@' (type=%@) "
940 @"required by bundle %@.",
941 bundleName, type, [_bundle bundlePath]);
946 NSLog(@"ERROR: error in bundle-info.plist of bundle %@", _bundle);
950 /* load located bundles */
955 NSLog(@"NGBundleManager: preload required bundles: %@",
959 e = [requiredBundles objectEnumerator];
960 while ((requiredBundle = [e nextObject]) != nil) {
963 if ((bundleMaster = [self loadBundle:requiredBundle]) == Nil) {
964 NSLog(@"ERROR: could not load bundle %@ (%@) required by bundle %@.",
965 [requiredBundle bundlePath], requiredBundle,
966 [_bundle bundlePath]);
972 /* load required classes */
977 reqClasses = [requires objectForKey:@"classes"];
979 bundles = [self _locateBundlesForClassInfos:[reqClasses objectEnumerator]];
980 if (requiredBundles == nil)
981 requiredBundles = [NSMutableArray arrayWithCapacity:16];
982 [requiredBundles addObjectsFromArray:bundles];
985 /* load located bundles */
989 e = [requiredBundles objectEnumerator];
990 while ((requiredBundle = [e nextObject]) != nil) {
993 if ((bundleMaster = [self loadBundle:requiredBundle]) == Nil) {
994 NSLog(@"ERROR: could not load bundle %@ (%@) required by bundle %@.",
995 [requiredBundle bundlePath], requiredBundle,
996 [_bundle bundlePath]);
1002 /* check whether versions of classes match */
1007 e = [[requires objectForKey:@"classes"] objectEnumerator];
1008 while ((i = [e nextObject]) != nil) {
1009 NSString *className;
1012 if ((className = [i objectForKey:@"name"]) == nil)
1015 if ((clazz = NSClassFromString(className)) == Nil)
1018 if ([i objectForKey:@"exact-version"]) {
1021 v = [[i objectForKey:@"exact-version"] intValue];
1023 if (v != [clazz version]) {
1024 NSLog(@"ERROR: required exact class match failed:\n"
1026 @" required version: %i\n"
1027 @" loaded version: %i\n"
1031 [_bundle bundlePath]);
1034 else if ([i objectForKey:@"version"]) {
1037 v = [[i objectForKey:@"version"] intValue];
1039 if (v > [clazz version]) {
1040 NSLog(@"ERROR: provided class does not match required version:\n"
1042 @" least required version: %i\n"
1043 @" loaded version: %i\n"
1047 [_bundle bundlePath]);
1055 - (BOOL)_postLoadBundle:(NSBundle *)_bundle info:(NSDictionary *)_bundleInfo {
1059 - (id)loadBundle:(NSBundle *)_bundle {
1060 NSString *path = nil;
1061 NSDictionary *bundleInfo = nil;
1062 id bundleManager = nil;
1065 NSAssert(self->loadedBundles, @"missing loadedBundles hashmap ..");
1068 if ((bundleManager = NSMapGet(self->loadedBundles, _bundle)))
1069 return bundleManager;
1071 if (_bundle == [NSBundle mainBundle])
1072 return [NSBundle mainBundle];
1074 if ([self->loadingBundles containsObject:_bundle])
1078 if (self->loadingBundles == nil)
1079 self->loadingBundles = [[NSMutableSet allocWithZone:[self zone]] init];
1080 [self->loadingBundles addObject:_bundle];
1082 path = [_bundle bundlePath];
1083 path = [self makeBundleInfoPath:path];
1085 if ((bundleInfo = NSMapGet(self->pathToBundleInfo, path)) == nil) {
1086 if ([[NSFileManager defaultManager] fileExistsAtPath:path])
1087 bundleInfo = [self _loadBundleInfoAtExistingPath:path];
1090 if (![self _preLoadBundle:_bundle info:bundleInfo])
1093 if (debugOn) NSLog(@"NGBundleManager: will load bundle: %@", _bundle);
1094 if (![_bundle _loadForBundleManager:self])
1096 if (debugOn) NSLog(@"NGBundleManager: did load bundle: %@", _bundle);
1098 if (![self _postLoadBundle:_bundle info:bundleInfo])
1101 if ((bundleManager =
1102 [self _initializeLoadedBundle:_bundle info:bundleInfo])) {
1103 NSMapInsert(self->loadedBundles, _bundle, bundleManager);
1105 if ([bundleManager respondsToSelector:
1106 @selector(bundleManager:didLoadBundle:)])
1107 [bundleManager bundleManager:self didLoadBundle:_bundle];
1111 NSLog(@"ERROR(%s): couldn't initialize loaded bundle '%@'",
1112 __PRETTY_FUNCTION__, [_bundle bundlePath]);
1116 [self->loadingBundles removeObject:_bundle];
1118 if (bundleManager) {
1119 if (bundleInfo == nil)
1120 bundleInfo = [NSDictionary dictionary];
1122 [[NSNotificationCenter defaultCenter]
1123 postNotificationName:
1124 NGBundleWasLoadedNotificationName
1126 userInfo:[NSDictionary dictionaryWithObjectsAndKeys:
1127 self, @"NGBundleManager",
1128 bundleManager, @"NGBundleHandler",
1129 bundleInfo, @"NGBundleInfo",
1132 return bundleManager;
1137 - (id)principalObjectOfBundle:(NSBundle *)_bundle {
1138 return (id)NSMapGet(self->loadedBundles, _bundle);
1143 static BOOL _doesInfoMatch(NSArray *keys, NSDictionary *dict, NSDictionary *info)
1147 for (i = 0, count = [keys count]; i < count; i++) {
1151 key = [keys objectAtIndex:i];
1152 vv = [info objectForKey:key];
1155 /* info has no matching key */
1159 kv = [dict objectForKey:key];
1160 if (![kv isEqual:vv])
1166 - (NSDictionary *)configForResource:(id)_resource ofType:(NSString *)_type
1167 providedByBundle:(NSBundle *)_bundle
1169 NSDictionary *bundleInfo = nil;
1171 NSEnumerator *providedResources;
1172 NSArray *rnKeys = nil;
1176 if ([_resource respondsToSelector:@selector(objectForKey:)]) {
1177 rnKeys = [_resource allKeys];
1178 rnKeyCount = [rnKeys count];
1181 infoPath = [self makeBundleInfoPath:[_bundle bundlePath]];
1183 /* check whether info is in cache */
1184 if ((bundleInfo = NSMapGet(self->pathToBundleInfo, infoPath)) == nil) {
1185 if (![[NSFileManager defaultManager] fileExistsAtPath:infoPath])
1186 /* no bundle-info.plist available .. */
1190 bundleInfo = [self _loadBundleInfoAtExistingPath:infoPath];
1193 /* get provided resources config */
1196 [[(NSDictionary *)[bundleInfo objectForKey:@"provides"] objectForKey:_type]
1198 if (providedResources == nil) return nil;
1200 /* scan provided resources */
1202 while ((info = [providedResources nextObject])) {
1204 if (!_doesInfoMatch(rnKeys, _resource, info))
1210 name = [[(NSDictionary *)info objectForKey:@"name"] stringValue];
1211 if (name == nil) continue;
1212 if (![name isEqualToString:_resource]) continue;
1219 - (void)_processInfoForProvidedResources:(NSDictionary *)info
1220 ofType:(NSString *)_type path:(NSString *)path
1221 resourceName:(NSString *)_resourceName
1222 resourceSelector:(NGBundleResourceSelector)_selector
1223 context:(void *)_context
1224 andAddToResultArray:(NSMutableArray *)result
1226 NSEnumerator *providedResources = nil;
1227 if (info == nil) return;
1229 /* direct match (a bundle was specified in the path) */
1231 providedResources = [[(NSDictionary *)[info objectForKey:@"provides"]
1235 if (providedResources == nil) return;
1237 /* scan provide array */
1238 while ((info = [providedResources nextObject])) {
1241 if ((name = [[info objectForKey:@"name"] stringValue]) == nil)
1244 if (_resourceName) {
1245 if (![name isEqualToString:_resourceName])
1249 if (!_selector(_resourceName, _type, path, info, self, _context))
1253 [result addObject:path];
1257 - (NSArray *)pathsForBundlesProvidingResource:(NSString *)_resourceName
1258 ofType:(NSString *)_type
1259 resourceSelector:(NGBundleResourceSelector)_selector
1260 context:(void *)_context
1262 /* TODO: split up method */
1263 NSMutableArray *result = nil;
1269 NSLog(@"BM LOOKUP pathes (%d bundles loaded): %@ / %@",
1270 NSCountMapTable(self->loadedBundles), _resourceName, _type);
1273 fm = [NSFileManager defaultManager];
1274 result = [NSMutableArray arrayWithCapacity:64];
1276 // TODO: look in loaded bundles
1278 /* check physical pathes */
1280 e = [self->bundleSearchPaths objectEnumerator];
1281 while ((path = [e nextObject]) != nil) {
1284 NSString *tmp, *bundleDirPath;
1287 if (![fm fileExistsAtPath:path isDirectory:&isDir])
1290 if (!isDir) continue;
1292 /* check whether an appropriate bundle is contained in 'path' */
1294 dir = [[fm directoryContentsAtPath:path] objectEnumerator];
1295 while ((bundleDirPath = [dir nextObject]) != nil) {
1296 NSDictionary *bundleInfo = nil;
1297 NSEnumerator *providedResources = nil;
1301 bundleDirPath = [path stringByAppendingPathComponent:bundleDirPath];
1302 infoPath = [self makeBundleInfoPath:bundleDirPath];
1304 // TODO: can we use _doesBundleInfo:path:providedResource:... ?
1305 if ((bundleInfo = NSMapGet(self->pathToBundleInfo, infoPath))==nil) {
1306 if (![fm fileExistsAtPath:infoPath])
1309 bundleInfo = [self _loadBundleInfoAtExistingPath:infoPath];
1313 [[(NSDictionary *)[bundleInfo objectForKey:@"provides"]
1316 if (providedResources == nil) continue;
1318 /* scan 'provides' array */
1319 while ((info = [providedResources nextObject])) {
1322 name = [[(NSDictionary *)info objectForKey:@"name"] stringValue];
1323 if (name == nil) continue;
1325 if (_resourceName != nil) {
1326 if (![name isEqualToString:_resourceName])
1329 if (_selector != NULL) {
1330 if (!_selector(name, _type, bundleDirPath, info, self, _context))
1334 [result addObject:bundleDirPath];
1339 /* check for direct match (NGBundlePath element is a bundle) */
1341 tmp = [self makeBundleInfoPath:path];
1343 if ((info = NSMapGet(self->pathToBundleInfo, tmp)) == nil) {
1344 if ([fm fileExistsAtPath:tmp])
1345 info = [self _loadBundleInfoAtExistingPath:tmp];
1348 [self _processInfoForProvidedResources:info ofType:_type path:path
1349 resourceName:_resourceName resourceSelector:_selector
1351 andAddToResultArray:result];
1354 if ([result count] == 0) {
1355 [self logWithFormat:
1356 @"Note(%s): method does not search in loaded bundles for "
1357 @"resources of type '%@'",
1358 __PRETTY_FUNCTION__, _type];
1361 return [[result copy] autorelease];
1364 - (BOOL)_doesBundleInfo:(NSDictionary *)_bundleInfo path:(NSString *)_path
1365 provideResource:(id)_resourceName ofType:(NSString *)_type
1366 rnKeys:(NSArray *)_rnKeys
1367 resourceSelector:(NGBundleResourceSelector)_selector context:(void *)_context
1369 NSEnumerator *providedResources;
1373 [[(NSDictionary *)[_bundleInfo objectForKey:@"provides"]
1374 objectForKey:_type] objectEnumerator];
1375 if (providedResources == nil) return NO;
1377 /* scan provide array */
1378 while ((info = [providedResources nextObject])) {
1379 if (_rnKeys != nil) {
1380 if (!_doesInfoMatch(_rnKeys, _resourceName, info))
1386 name = [[(NSDictionary *)info objectForKey:@"name"] stringValue];
1387 if (name == nil) continue;
1388 if (![name isEqualToString:_resourceName]) continue;
1391 if (_selector != NULL) {
1392 if (!_selector(_resourceName, _type, _path, info, self, _context))
1396 /* all conditions applied (found) */
1402 - (NSString *)pathOfLoadedBundleProvidingResource:(id)_resourceName
1403 ofType:(NSString *)_type
1404 resourceSelector:(NGBundleResourceSelector)_selector context:(void *)_context
1406 NSMapEnumerator menum;
1408 NSDictionary *bundleInfo;
1411 rnKeys = ([_resourceName respondsToSelector:@selector(objectForKey:)])
1412 ? [_resourceName allKeys]
1415 menum = NSEnumerateMapTable(self->pathToBundleInfo);
1416 while (NSNextMapEnumeratorPair(&menum, (void *)&path, (void *)&bundleInfo)) {
1418 NSLog(@"check loaded bundle for resource %@: %@", _resourceName,
1422 if ([self _doesBundleInfo:bundleInfo path:path
1423 provideResource:_resourceName ofType:_type rnKeys:rnKeys
1424 resourceSelector:_selector context:_context])
1425 /* strip bundle-info.plist name */
1426 return [path stringByDeletingLastPathComponent];
1432 - (NSString *)pathForBundleProvidingResource:(id)_resourceName
1433 ofType:(NSString *)_type
1434 resourceSelector:(NGBundleResourceSelector)_selector
1435 context:(void *)_context
1437 /* main path lookup method */
1438 // TODO: this method seriously needs some refactoring
1442 NSArray *rnKeys = nil;
1446 NSLog(@"BM LOOKUP path (%d bundles loaded): %@ / %@",
1447 NSCountMapTable(self->loadedBundles), _resourceName, _type);
1450 /* look in loaded bundles */
1452 path = [self pathOfLoadedBundleProvidingResource:_resourceName ofType:_type
1453 resourceSelector:_selector context:_context];
1454 if (path != nil) return path;
1456 /* look in filesystem */
1458 if ([_resourceName respondsToSelector:@selector(objectForKey:)]) {
1459 rnKeys = [_resourceName allKeys];
1460 rnKeyCount = [rnKeys count];
1463 fm = [NSFileManager defaultManager];
1464 e = [self->bundleSearchPaths objectEnumerator];
1465 while ((path = [e nextObject]) != nil) {
1471 if (![fm fileExistsAtPath:path isDirectory:&isDir])
1474 if (!isDir) continue;
1476 /* check whether an appropriate bundle is contained in 'path' */
1478 dir = [[fm directoryContentsAtPath:path] objectEnumerator];
1479 while ((tmp = [dir nextObject]) != nil) {
1480 NSDictionary *bundleInfo = nil;
1483 tmp = [path stringByAppendingPathComponent:tmp];
1484 infoPath = [self makeBundleInfoPath:tmp];
1487 NSLog(@"check path path=%@ info=%@", tmp, infoPath);
1489 if ((bundleInfo = NSMapGet(self->pathToBundleInfo, infoPath)) == nil) {
1490 if (![fm fileExistsAtPath:infoPath])
1493 bundleInfo = [self _loadBundleInfoAtExistingPath:infoPath];
1496 NSLog(@"found info for path=%@ info=%@: %@", tmp,infoPath,bundleInfo);
1498 if ([self _doesBundleInfo:bundleInfo path:tmp
1499 provideResource:_resourceName ofType:_type rnKeys:rnKeys
1500 resourceSelector:_selector context:_context])
1504 /* check for direct match */
1506 tmp = [self makeBundleInfoPath:path];
1508 if ((info = NSMapGet(self->pathToBundleInfo, tmp)) == nil) {
1509 if ([fm fileExistsAtPath:tmp])
1510 info = [self _loadBundleInfoAtExistingPath:tmp];
1512 NSLog(@"WARNING(%s): did not find direct path '%@'",
1513 __PRETTY_FUNCTION__, tmp);
1518 // direct match (a bundle was specified in the path)
1519 NSEnumerator *providedResources;
1520 NSDictionary *provides;
1522 provides = [(NSDictionary *)info objectForKey:@"provides"];
1523 providedResources = [[provides objectForKey:_type] objectEnumerator];
1525 if (providedResources == nil) continue;
1527 // scan provide array
1528 while ((info = [providedResources nextObject])) {
1530 if (!_doesInfoMatch(rnKeys, _resourceName, info))
1536 name = [[(NSDictionary *)info objectForKey:@"name"] stringValue];
1537 if (name == nil) continue;
1538 if (![name isEqualToString:_resourceName]) continue;
1542 if (!_selector(_resourceName, _type, tmp, info, self, _context))
1545 /* all conditions applied */
1553 - (NSBundle *)bundleProvidingResource:(id)_name ofType:(NSString *)_type {
1556 if (debugOn) NSLog(@"BM LOOKUP: %@ / %@", _name, _type);
1558 bp = [self pathForBundleProvidingResource:_name
1560 resourceSelector:NULL context:nil];
1561 if ([bp length] == 0) {
1562 #if (NeXT_Foundation_LIBRARY || APPLE_Foundation_LIBRARY) && HEAVY_DEBUG
1563 NSLog(@"%s: found no resource '%@' of type '%@' ...",
1564 __PRETTY_FUNCTION__, _resourceName, _resourceType);
1566 if (debugOn) NSLog(@" did not find: %@ / %@", _name, _type);
1570 if (debugOn) NSLog(@" FOUND: %@", bp);
1571 return [self bundleWithPath:bp];
1574 - (NSArray *)bundlesProvidingResource:(id)_resourceName
1575 ofType:(NSString *)_type
1578 NSMutableArray *bundles;
1581 paths = [self pathsForBundlesProvidingResource:_resourceName
1583 resourceSelector:NULL context:nil];
1585 count = [paths count];
1586 if (paths == nil) return nil;
1587 if (count == 0) return paths;
1589 bundles = [NSMutableArray arrayWithCapacity:count];
1590 for (i = 0; i < count; i++) {
1593 if ((bundle = [self bundleWithPath:[paths objectAtIndex:i]]))
1594 [bundles addObject:bundle];
1596 return [[bundles copy] autorelease];
1599 - (NSArray *)providedResourcesOfType:(NSString *)_resourceType
1600 inBundle:(NSBundle *)_bundle
1603 NSDictionary *bundleInfo;
1605 path = [self makeBundleInfoPath:[_bundle bundlePath]];
1606 if (path == nil) return nil;
1608 /* retrieve bundle info dictionary */
1609 if ((bundleInfo = NSMapGet(self->pathToBundleInfo, path)) == nil)
1610 bundleInfo = [self _loadBundleInfoAtExistingPath:path];
1612 return [(NSDictionary *)[bundleInfo objectForKey:@"provides"]
1613 objectForKey:_resourceType];
1616 - (void)_addRegisteredProvidedResourcesOfType:(NSString *)_type
1617 toSet:(NSMutableSet *)_result
1619 NSMapEnumerator menum;
1621 NSDictionary *bundleInfo;
1623 menum = NSEnumerateMapTable(self->pathToBundleInfo);
1624 while (NSNextMapEnumeratorPair(&menum, (void *)&path, (void *)&bundleInfo)) {
1625 NSArray *providedResources;
1628 NSLog(@"check loaded bundle for resource types %@: %@", _type, path);
1631 [(NSDictionary *)[bundleInfo objectForKey:@"provides"]
1632 objectForKey:_type];
1633 if (providedResources == nil) continue;
1635 [_result addObjectsFromArray:providedResources];
1639 - (NSArray *)providedResourcesOfType:(NSString *)_resourceType {
1640 NSMutableSet *result = nil;
1641 NSFileManager *fm = [NSFileManager defaultManager];
1645 result = [NSMutableSet setWithCapacity:128];
1647 /* scan loaded bundles */
1649 [self _addRegisteredProvidedResourcesOfType:_resourceType toSet:result];
1651 /* scan all bundle search paths */
1653 e = [self->bundleSearchPaths objectEnumerator];
1654 while ((path = [e nextObject]) != nil) {
1660 if (![fm fileExistsAtPath:path isDirectory:&isDir])
1662 if (!isDir) continue;
1664 /* check whether an appropriate bundle is contained in 'path' */
1666 // TODO: move to own method
1667 dir = [[fm directoryContentsAtPath:path] objectEnumerator];
1668 while ((tmp = [dir nextObject]) != nil) {
1669 NSDictionary *bundleInfo = nil;
1670 NSArray *providedResources = nil;
1673 tmp = [path stringByAppendingPathComponent:tmp];
1674 infoPath = [self makeBundleInfoPath:tmp];
1677 NSLog(@" info path: %@", tmp);
1680 if ((bundleInfo = NSMapGet(self->pathToBundleInfo, infoPath)) == nil) {
1681 if (![fm fileExistsAtPath:infoPath])
1684 bundleInfo = [self _loadBundleInfoAtExistingPath:infoPath];
1688 [(NSDictionary *)[bundleInfo objectForKey:@"provides"]
1689 objectForKey:_resourceType];
1690 if (providedResources == nil) continue;
1692 [result addObjectsFromArray:providedResources];
1695 /* check for direct match */
1697 tmp = [self makeBundleInfoPath:path];
1699 if ((info = NSMapGet(self->pathToBundleInfo, tmp)) == nil) {
1700 if ([fm fileExistsAtPath:tmp])
1701 info = [self _loadBundleInfoAtExistingPath:tmp];
1705 // direct match (a bundle was specified in the path)
1706 NSArray *providedResources;
1707 NSDictionary *provides;
1709 provides = [(NSDictionary *)info objectForKey:@"provides"];
1710 providedResources = [provides objectForKey:_resourceType];
1712 if (providedResources == nil) continue;
1714 [result addObjectsFromArray:providedResources];
1717 return [result allObjects];
1720 - (NSBundle *)bundleProvidingResourceOfType:(NSString *)_resourceType
1721 matchingQualifier:(EOQualifier *)_qual
1723 NSFileManager *fm = [NSFileManager defaultManager];
1727 /* foreach search path entry */
1729 e = [self->bundleSearchPaths objectEnumerator];
1730 while ((path = [e nextObject])) {
1733 if ([fm fileExistsAtPath:path isDirectory:&isDir]) {
1736 if (!isDir) continue;
1738 /* check whether an appropriate bundle is contained in 'path' */
1742 dir = [[fm directoryContentsAtPath:path] objectEnumerator];
1743 while ((tmp = [dir nextObject])) {
1744 NSDictionary *bundleInfo;
1745 NSArray *providedResources;
1748 tmp = [path stringByAppendingPathComponent:tmp];
1749 infoPath = [self makeBundleInfoPath:tmp];
1751 if ((bundleInfo=NSMapGet(self->pathToBundleInfo, infoPath)) == nil) {
1752 if (![fm fileExistsAtPath:infoPath])
1755 bundleInfo = [self _loadBundleInfoAtExistingPath:infoPath];
1758 bundleInfo = [bundleInfo objectForKey:@"provides"];
1759 providedResources = [bundleInfo objectForKey:_resourceType];
1761 if (providedResources == nil) continue;
1764 [providedResources filteredArrayUsingQualifier:_qual];
1766 if ([providedResources count] > 0)
1767 return [self bundleWithPath:tmp];
1771 /* check for direct match */
1773 tmp = [self makeBundleInfoPath:path];
1775 if ((info = NSMapGet(self->pathToBundleInfo, tmp)) == nil) {
1776 if ([fm fileExistsAtPath:tmp])
1777 info = [self _loadBundleInfoAtExistingPath:tmp];
1781 // direct match (a bundle was specified in the path)
1782 NSArray *providedResources;
1783 NSDictionary *provides;
1785 provides = [(NSDictionary *)info objectForKey:@"provides"];
1786 providedResources = [provides objectForKey:_resourceType];
1788 if (providedResources == nil) continue;
1791 [providedResources filteredArrayUsingQualifier:_qual];
1793 if ([providedResources count] > 0)
1794 return [self bundleWithPath:path];
1801 - (NSBundle *)bundlesProvidingResourcesOfType:(NSString *)_resourceType
1802 matchingQualifier:(EOQualifier *)_qual
1804 NSMutableArray *bundles = nil;
1805 NSFileManager *fm = [NSFileManager defaultManager];
1809 bundles = [NSMutableArray arrayWithCapacity:128];
1811 /* foreach search path entry */
1813 e = [self->bundleSearchPaths objectEnumerator];
1814 while ((path = [e nextObject])) {
1817 if ([fm fileExistsAtPath:path isDirectory:&isDir]) {
1820 if (!isDir) continue;
1822 /* check whether an appropriate bundle is contained in 'path' */
1826 dir = [[fm directoryContentsAtPath:path] objectEnumerator];
1827 while ((tmp = [dir nextObject])) {
1828 NSDictionary *bundleInfo = nil;
1829 NSArray *providedResources = nil;
1832 tmp = [path stringByAppendingPathComponent:tmp];
1833 infoPath = [self makeBundleInfoPath:tmp];
1835 if ((bundleInfo=NSMapGet(self->pathToBundleInfo, infoPath)) == nil) {
1836 if (![fm fileExistsAtPath:infoPath])
1839 bundleInfo = [self _loadBundleInfoAtExistingPath:infoPath];
1842 bundleInfo = [bundleInfo objectForKey:@"provides"];
1843 providedResources = [bundleInfo objectForKey:_resourceType];
1845 if (providedResources == nil) continue;
1848 [providedResources filteredArrayUsingQualifier:_qual];
1850 if ([providedResources count] > 0)
1851 [bundles addObject:[self bundleWithPath:tmp]];
1855 /* check for direct match */
1857 tmp = [self makeBundleInfoPath:path];
1859 if ((info = NSMapGet(self->pathToBundleInfo, tmp)) == nil) {
1860 if ([fm fileExistsAtPath:tmp])
1861 info = [self _loadBundleInfoAtExistingPath:tmp];
1865 // direct match (a bundle was specified in the path)
1866 NSArray *providedResources;
1867 NSDictionary *provides;
1869 provides = [(NSDictionary *)info objectForKey:@"provides"];
1870 providedResources = [provides objectForKey:_resourceType];
1872 if (providedResources == nil) continue;
1875 [providedResources filteredArrayUsingQualifier:_qual];
1877 if ([providedResources count] > 0)
1878 [bundles addObject:[self bundleWithPath:path]];
1882 return [[bundles copy] autorelease];
1887 - (void)_bundleDidLoadNotifcation:(NSNotification *)_notification {
1888 NSDictionary *ui = [_notification userInfo];
1891 NSLog(@"bundle %@ did load with classes %@",
1892 [[_notification object] bundlePath],
1893 [ui objectForKey:@"NSLoadedClasses"]);
1896 [self registerBundle:[_notification object]
1897 classes:[ui objectForKey:@"NSLoadedClasses"]
1898 categories:[ui objectForKey:@"NSLoadedCategories"]];
1903 - (BOOL)isDebuggingEnabled {
1907 @end /* NGBundleManager */
1909 @implementation NSBundle(BundleManagerSupport)
1912 return [NGBundle alloc];
1914 + (id)allocWithZone:(NSZone *)zone {
1915 return [NGBundle allocWithZone:zone];
1918 #if !(NeXT_Foundation_LIBRARY || APPLE_Foundation_LIBRARY)
1919 //#warning remember, bundleForClass is not overridden !
1921 + (NSBundle *)bundleForClass:(Class)aClass {
1922 return [[NGBundleManager defaultBundleManager] bundleForClass:aClass];
1925 + (NSBundle *)bundleWithPath:(NSString*)path {
1926 return [[NGBundleManager defaultBundleManager] bundleWithPath:path];
1930 @end /* NSBundle(BundleManagerSupport) */
1932 @implementation NSBundle(NGBundleManagerExtensions)
1934 - (id)principalObject {
1935 return [[NGBundleManager defaultBundleManager]
1936 principalObjectOfBundle:self];
1939 - (NSArray *)providedResourcesOfType:(NSString *)_resourceType {
1940 return [[NGBundleManager defaultBundleManager]
1941 providedResourcesOfType:_resourceType
1945 - (NSString *)bundleName {
1946 return [[[self bundlePath] lastPathComponent] stringByDeletingPathExtension];
1949 - (NSString *)bundleType {
1950 return [[self bundlePath] pathExtension];
1953 - (NSArray *)providedClasses {
1954 return [[NGBundleManager defaultBundleManager] classesProvidedByBundle:self];
1957 - (NSArray *)requiredClasses {
1958 return [[NGBundleManager defaultBundleManager] classesRequiredByBundle:self];
1961 - (NSArray *)requiredBundles {
1962 return [[NGBundleManager defaultBundleManager] bundlesRequiredByBundle:self];
1965 - (NSDictionary *)configForResource:(id)_resource ofType:(NSString *)_type {
1966 return [[NGBundleManager defaultBundleManager]
1967 configForResource:_resource ofType:_type
1968 providedByBundle:self];
1973 - (BOOL)_loadForBundleManager:(NGBundleManager *)_manager {
1977 @end /* NSBundle(NGBundleManagerExtensions) */
1979 @implementation NSBundle(NGLanguageResourceExtensions)
1981 static BOOL debugLanguageLookup = NO;
1983 // locating resources
1985 - (NSString *)pathForResource:(NSString *)_name ofType:(NSString *)_ext
1986 inDirectory:(NSString *)_directory
1987 languages:(NSArray *)_languages
1990 NSString *path = nil;
1992 id (*objAtIdx)(id,SEL,int);
1994 if (debugLanguageLookup) {
1995 NSLog(@"LOOKUP(%s): %@ | %@ | %@ | %@", __PRETTY_FUNCTION__,
1996 _name, _ext, _directory, [_languages componentsJoinedByString:@","]);
1999 path = [self bundlePath];
2000 if ([_directory isNotNull]) {
2001 // TODO: should we change that?
2002 path = [path stringByAppendingPathComponent:_directory];
2005 #if (NeXT_Foundation_LIBRARY || APPLE_Foundation_LIBRARY)
2006 path = [path stringByAppendingPathComponent:@"Contents"];
2008 path = [path stringByAppendingPathComponent:@"Resources"];
2011 if (debugLanguageLookup) NSLog(@" BASE: %@", path);
2013 fm = [NSFileManager defaultManager];
2014 if (![fm fileExistsAtPath:path])
2017 if (_ext != nil) _name = [_name stringByAppendingPathExtension:_ext];
2019 langCount = [_languages count];
2020 objAtIdx = (langCount > 0)
2021 ? (void*)[_languages methodForSelector:@selector(objectAtIndex:)]
2024 for (i = 0; i < langCount; i++) {
2029 ? objAtIdx(_languages, @selector(objectAtIndex:), i)
2030 : [_languages objectAtIndex:i];
2032 language = [language stringByAppendingPathExtension:@"lproj"];
2033 lpath = [path stringByAppendingPathComponent:language];
2034 lpath = [lpath stringByAppendingPathComponent:_name];
2036 if ([fm fileExistsAtPath:lpath])
2040 if (debugLanguageLookup)
2041 NSLog(@" no language matched, check base: %@", path);
2043 /* now look into x.bundle/Resources/name.type */
2044 if ([fm fileExistsAtPath:[path stringByAppendingPathComponent:_name]])
2045 return [path stringByAppendingPathComponent:_name];
2050 - (NSString *)pathForResource:(NSString *)_name ofType:(NSString *)_ext
2051 languages:(NSArray *)_languages
2055 path = [self pathForResource:_name ofType:_ext
2056 inDirectory:@"Resources"
2057 languages:_languages];
2058 if (path) return path;
2060 path = [self pathForResource:_name ofType:_ext
2062 languages:_languages];
2066 @end /* NSBundle(NGLanguageResourceExtensions) */
2068 @implementation NGBundle
2071 return [self allocWithZone:NULL];
2073 + (id)allocWithZone:(NSZone*)zone {
2074 return NSAllocateObject(self, 0, zone);
2077 - (id)initWithPath:(NSString *)__path {
2078 return [super initWithPath:__path];
2083 - (BOOL)_loadForBundleManager:(NGBundleManager *)_manager {
2084 return [super load];
2088 NGBundleManager *bm;
2090 bm = [NGBundleManager defaultBundleManager];
2092 return [bm loadBundle:self] ? YES : NO;
2095 + (NSBundle *)bundleForClass:(Class)aClass {
2096 return [[NGBundleManager defaultBundleManager] bundleForClass:aClass];
2098 + (NSBundle *)bundleWithPath:(NSString*)path {
2099 return [[NGBundleManager defaultBundleManager] bundleWithPath:path];
2102 #if GNUSTEP_BASE_LIBRARY
2104 - (Class)principalClass {
2108 if ((c = [super principalClass]) != Nil)
2111 if ((cname = [[self infoDictionary] objectForKey:@"NSPrincipalClass"]) ==nil)
2114 if ((c = NSClassFromString(cname)) != Nil)
2117 NSLog(@"%s: did not find principal class named '%@' of bundle %@, dict: %@",
2118 __PRETTY_FUNCTION__, cname, self, [self infoDictionary]);
2124 - (NSString *)description {
2128 "<%s %p fullPath: %s infoDictionary: %p loaded=%s>",
2129 (char*)object_get_class_name(self),
2131 [[self bundlePath] cString],
2132 [self infoDictionary],
2133 self->_codeLoaded ? "yes" : "no");
2135 return [NSString stringWithCString:buffer];