2 Copyright (C) 2000-2005 SKYRIX Software AG
4 This file is part of SOPE.
6 SOPE 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 SOPE 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 SOPE; 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 #include <NGExtensions/NSNull+misc.h>
26 #import <Foundation/NSFileManager.h>
27 #import <EOControl/EOQualifier.h>
30 #if 0 && GNUSTEP_BASE_LIBRARY /* supported in repository since 19990916-2254-MET */
31 /* hack until GNUstep provides the necessary callbacks */
32 # define NSNonRetainedObjectMapValueCallBacks NSObjectMapValueCallBacks
35 #if NeXT_Foundation_LIBRARY || COCOA_Foundation_LIBRARY
36 # include <NGExtensions/NGPropertyListParser.h>
39 #if LIB_FOUNDATION_LIBRARY
40 @interface NSBundle(UsedPrivates)
41 + (BOOL)isFlattenedDirLayout;
45 #if NeXT_RUNTIME || APPLE_RUNTIME
47 #include <objc/objc-runtime.h>
49 //OBJC_EXPORT void objc_setClassHandler(int (*)(const char *));
51 static BOOL debugClassHook = NO;
52 static BOOL hookDoLookup = YES;
54 static int _getClassHook(const char *className) {
56 if (className == NULL) return 0;
59 printf("lookup class '%s'.\n", className);
61 if (objc_lookUpClass(className))
65 static NGBundleManager *manager = nil;
70 printf("%s: look for class %s\n", __PRETTY_FUNCTION__, className);
72 manager = [NGBundleManager defaultBundleManager];
74 cns = [[NSString alloc] initWithCString:className];
75 bundle = [manager bundleForClassNamed:cns];
76 [cns release]; cns = nil;
80 NSLog(@"%s: found bundle %@", __PRETTY_FUNCTION__,
84 if (![manager loadBundle:bundle]) {
86 "bundleManager couldn't load bundle for class '%s'.\n",
91 Class c = objc_lookUpClass(className);
92 NSLog(@"%s: loaded bundle %@ for className %s class %@",
94 bundle, className, c);
106 #include <objc/objc-api.h>
108 static Class (*oldClassLoadHook)(const char *_name) = NULL;
110 static inline BOOL _isValidClassName(const char *_name) {
113 if (_name == NULL) return NO;
115 for (len = 0; (len < 256) && (*_name != '\0'); len++, _name++) {
117 if (!isalnum((int)*_name))
121 return (len == 256) ? NO : YES;
124 static Class _classLoadHook(const char *_name) {
125 if (_isValidClassName(_name)) {
126 static NGBundleManager *manager = nil;
129 //NSLog(@"%s: look for class %s", __PRETTY_FUNCTION__, _name);
131 manager = [NGBundleManager defaultBundleManager];
133 bundle = [manager bundleForClassNamed:[NSString stringWithCString:_name]];
136 NSLog(@"%s: found bundle %@", __PRETTY_FUNCTION__, [bundle bundlePath]);
139 if ([manager loadBundle:bundle]) {
143 hook = _objc_lookup_class;
144 _objc_lookup_class = NULL;
145 clazz = objc_lookup_class(_name);
146 _objc_lookup_class = hook;
148 if (clazz) return clazz;
152 return (oldClassLoadHook != NULL) ? oldClassLoadHook(_name) : Nil;
154 #endif // GNU_RUNTIME
156 NSString *NGBundleWasLoadedNotificationName = @"NGBundleWasLoadedNotification";
158 @interface NSBundle(NGBundleManagerPrivate)
159 - (BOOL)_loadForBundleManager:(NGBundleManager *)_manager;
162 @interface NGBundleManager(PrivateMethods)
164 - (void)registerBundle:(NSBundle *)_bundle
165 classes:(NSArray *)_classes
166 categories:(NSArray *)_categories;
168 - (NSString *)pathForBundleProvidingResource:(NSString *)_resourceName
169 ofType:(NSString *)_type
170 resourceSelector:(NGBundleResourceSelector)_selector
171 context:(void *)_ctx;
173 - (NSString *)makeBundleInfoPath:(NSString *)_path;
177 static BOOL _selectClassByVersion(NSString *_resourceName,
178 NSString *_resourceType,
180 NSDictionary *_resourceConfig,
181 NGBundleManager *_bundleManager,
187 if (![_resourceType isEqualToString:@"classes"])
190 if (_version == NULL)
192 if ([(id)_version intValue] == -1)
195 if ((tmp = [_resourceConfig objectForKey:@"version"])) {
196 classVersion = [tmp intValue];
198 if (classVersion < [(id)_version intValue]) {
199 NSLog(@"WARNING: class version mismatch for class %@: "
200 @"requested at least version %i, got version %i",
201 _resourceName, [(id)_version intValue], classVersion);
204 if ((tmp = [_resourceConfig objectForKey:@"exact-version"])) {
205 classVersion = [tmp intValue];
207 if (classVersion != [(id)_version intValue]) {
208 NSLog(@"WARNING: class version mismatch for class %@: "
209 @"requested exact version %i, got version %i",
210 _resourceName, [(id)_version intValue], classVersion);
216 @implementation NGBundleManager
219 static NGBundleManager *defaultManager = nil;
220 static BOOL debugOn = NO;
222 #if defined(__MINGW32__)
223 static NSString *NGEnvVarPathSeparator = @";";
225 static NSString *NGEnvVarPathSeparator = @":";
229 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
231 debugOn = [ud boolForKey:@"NGBundleManagerDebugEnabled"];
236 if (_objc_lookup_class != _classLoadHook) {
237 oldClassLoadHook = _objc_lookup_class;
238 _objc_lookup_class = _classLoadHook;
243 + (id)defaultBundleManager {
244 if (defaultManager == nil) {
245 defaultManager = [[NGBundleManager alloc] init];
247 #if NeXT_RUNTIME || APPLE_RUNTIME
249 static BOOL didRegisterCallback = NO;
251 if (!didRegisterCallback) {
252 didRegisterCallback = YES;
253 objc_setClassHandler(_getClassHook);
257 if (_objc_lookup_class != _classLoadHook) {
258 oldClassLoadHook = _objc_lookup_class;
259 _objc_lookup_class = _classLoadHook;
263 return defaultManager;
266 /* setup bundle search path */
268 - (void)_addMainBundlePathToPathArray:(NSMutableArray *)_paths {
272 pi = [NSProcessInfo processInfo];
273 path = [[pi arguments] objectAtIndex:0];
274 path = [path stringByDeletingLastPathComponent];
276 if ([path isEqual:@""])
280 // TODO: to be correct this would need to read the bundle-info
283 The path is the complete path to the executable, including the
284 processor, the OS and the library combo. Strip these directories
285 from the main bundle's path.
287 #if LIB_FOUNDATION_LIBRARY
288 if (![NSBundle isFlattenedDirLayout])
290 path = [[[path stringByDeletingLastPathComponent]
291 stringByDeletingLastPathComponent]
292 stringByDeletingLastPathComponent];
295 [_paths addObject:path];
298 - (void)_addBundlePathDefaultToPathArray:(NSMutableArray *)_paths {
302 if ((ud = [NSUserDefaults standardUserDefaults]) == nil) {
303 // got this with gstep-base during the port, apparently it happens
304 // if the bundle manager is created inside the setup process of
305 // gstep-base (for whatever reason)
306 NSLog(@"ERROR(NGBundleManager): got no system userdefaults object!");
312 if ((paths = [ud arrayForKey:@"NGBundlePath"]) == nil) {
313 if ((paths = [ud stringForKey:@"NGBundlePath"]) != nil)
314 paths = [paths componentsSeparatedByString:NGEnvVarPathSeparator];
317 [_paths addObjectsFromArray:paths];
319 NSLog(@"Note: NGBundlePath default is not configured.");
322 - (void)_addEnvironmentPathToPathArray:(NSMutableArray *)_paths {
326 pi = [NSProcessInfo processInfo];
327 paths = [[pi environment] objectForKey:@"NGBundlePath"];
329 paths = [paths componentsSeparatedByString:NGEnvVarPathSeparator];
330 if (paths) [_paths addObjectsFromArray:paths];
333 - (void)_addGNUstepPathsToPathArray:(NSMutableArray *)_paths {
336 // TODO: whats that supposed to do?
337 // TODO: replace with proper path locator function!
343 env = [[NSProcessInfo processInfo] environment];
345 if ((tmp = [env objectForKey:@"GNUSTEP_PATHPREFIX_LIST"]) == nil)
346 tmp = [env objectForKey:@"GNUSTEP_PATHLIST"];
347 tmp = [tmp componentsSeparatedByString:@":"];
349 for (i = 0, count = [tmp count]; i < count; i++) {
350 p = [tmp objectAtIndex:i];
351 p = [p stringByAppendingPathComponent:@"Library"];
352 p = [p stringByAppendingPathComponent:@"Bundles"];
353 if ([self->bundleSearchPaths containsObject:p]) continue;
355 if (p) [self->bundleSearchPaths addObject:p];
360 - (void)_setupBundleSearchPathes {
363 pi = [NSProcessInfo processInfo];
365 /* setup bundle search path */
367 self->bundleSearchPaths = [[NSMutableArray alloc] initWithCapacity:16];
369 [self _addMainBundlePathToPathArray:self->bundleSearchPaths];
370 [self _addBundlePathDefaultToPathArray:self->bundleSearchPaths];
371 [self _addEnvironmentPathToPathArray:self->bundleSearchPaths];
372 [self _addGNUstepPathsToPathArray:self->bundleSearchPaths];
374 #if DEBUG && NeXT_Foundation_LIBRARY && 0
375 NSLog(@"%s: bundle search pathes:\n%@", __PRETTY_FUNCTION__,
376 self->bundleSearchPaths);
380 - (void)_registerLoadedBundles {
381 NSEnumerator *currentBundles;
382 NSBundle *loadedBundle;
384 currentBundles = [[NSBundle allBundles] objectEnumerator];
385 while ((loadedBundle = [currentBundles nextObject]) != nil)
386 [self registerBundle:loadedBundle classes:nil categories:nil];
389 - (void)_registerForBundleLoadNotification {
390 [[NSNotificationCenter defaultCenter]
392 selector:@selector(_bundleDidLoadNotifcation:)
393 name:@"NSBundleDidLoadNotification"
398 #if GNUSTEP_BASE_LIBRARY
399 if ([NSUserDefaults standardUserDefaults] == nil) {
400 /* called inside setup process, deny creation (HACK) */
406 if ((self = [super init])) {
407 self->classToBundle =
408 NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks,
409 NSNonRetainedObjectMapValueCallBacks,
411 self->classNameToBundle =
412 NSCreateMapTable(NSObjectMapKeyCallBacks,
413 NSNonRetainedObjectMapValueCallBacks,
415 self->categoryNameToBundle =
416 NSCreateMapTable(NSObjectMapKeyCallBacks,
417 NSNonRetainedObjectMapValueCallBacks,
420 NSCreateMapTable(NSObjectMapKeyCallBacks,
421 NSNonRetainedObjectMapValueCallBacks,
423 self->pathToBundleInfo =
424 NSCreateMapTable(NSObjectMapKeyCallBacks,
425 NSObjectMapValueCallBacks,
428 NSCreateMapTable(NSObjectMapKeyCallBacks,
429 NSNonRetainedObjectMapValueCallBacks,
431 self->loadedBundles =
432 NSCreateMapTable(NSNonRetainedObjectMapKeyCallBacks,
433 NSObjectMapValueCallBacks,
436 [self _setupBundleSearchPathes];
437 [self _registerLoadedBundles];
438 [self _registerForBundleLoadNotification];
444 [self->loadingBundles release];
445 if (self->loadedBundles) NSFreeMapTable(self->loadedBundles);
446 if (self->classToBundle) NSFreeMapTable(self->classToBundle);
447 if (self->classNameToBundle) NSFreeMapTable(self->classNameToBundle);
448 if (self->categoryNameToBundle) NSFreeMapTable(self->categoryNameToBundle);
449 if (self->pathToBundle) NSFreeMapTable(self->pathToBundle);
450 if (self->pathToBundleInfo) NSFreeMapTable(self->pathToBundleInfo);
451 if (self->nameToBundle) NSFreeMapTable(self->nameToBundle);
452 [self->bundleSearchPaths release];
458 - (void)setBundleSearchPaths:(NSArray *)_paths {
459 ASSIGNCOPY(self->bundleSearchPaths, _paths);
461 - (NSArray *)bundleSearchPaths {
462 return self->bundleSearchPaths;
465 /* registering bundles */
467 - (void)registerBundle:(NSBundle *)_bundle
468 classes:(NSArray *)_classes
469 categories:(NSArray *)_categories
474 #if NeXT_RUNTIME || APPLE_RUNTIME
475 v = [_bundle bundlePath];
476 if ([v hasSuffix:@"Libraries"] || [v hasSuffix:@"Tools"]) {
478 fprintf(stderr, "INVALID BUNDLE: %s\n", [[_bundle bundlePath] cString]);
484 NSLog(@"NGBundleManager: register loaded bundle %@", [_bundle bundlePath]);
487 e = [_classes objectEnumerator];
488 while ((v = [e nextObject]) != nil) {
489 #if NeXT_RUNTIME || APPLE_RUNTIME
493 NSMapInsert(self->classToBundle, NSClassFromString(v), _bundle);
494 NSMapInsert(self->classNameToBundle, v, _bundle);
496 #if NeXT_RUNTIME || APPLE_RUNTIME
501 e = [_categories objectEnumerator];
502 while ((v = [e nextObject]) != nil)
503 NSMapInsert(self->categoryNameToBundle, v, _bundle);
508 - (NSString *)pathForBundleWithName:(NSString *)_name type:(NSString *)_type {
509 NSFileManager *fm = [NSFileManager defaultManager];
512 NSString *bundlePath;
515 /* first check in table */
518 bundlePath = [_name stringByAppendingPathExtension:_type];
520 if ((bundle = NSMapGet(self->nameToBundle, bundlePath)))
521 return [bundle bundlePath];
523 e = [self->bundleSearchPaths objectEnumerator];
524 while ((path = [e nextObject])) {
527 if ([fm fileExistsAtPath:path isDirectory:&isDir]) {
528 if (!isDir) continue;
530 if ([[path lastPathComponent] isEqualToString:bundlePath]) {
531 // direct match (a bundle was specified in the path)
537 tmp = [path stringByAppendingPathComponent:bundlePath];
538 if ([fm fileExistsAtPath:tmp isDirectory:&isDir]) {
549 /* getting bundles */
551 - (NSBundle *)bundleForClass:(Class)aClass {
552 /* this method never loads a dynamic bundle (since the class is set up) */
558 bundle = NSMapGet(self->classToBundle, aClass);
560 #if NeXT_Foundation_LIBRARY || COCOA_Foundation_LIBRARY
564 bundle = [NSBundle bundleForClass:aClass];
565 if (bundle == [NSBundle mainBundle])
568 p = [bundle bundlePath];
569 if ([p hasSuffix:@"Libraries"]) {
571 fprintf(stderr, "%s: Dylib bundle: 0x%08X: %s\n",
573 (unsigned int )bundle, [[bundle bundlePath] cString]);
577 else if ([p hasSuffix:@"Tools"]) {
579 fprintf(stderr, "%s: Tool bundle: 0x%08X: %s\n",
581 (unsigned int )bundle, [[bundle bundlePath] cString]);
590 if the class wasn't loaded from a bundle, it's *either* the main bundle
591 or a bundle loaded before NGExtension was loaded !!!
594 #if !LIB_FOUNDATION_LIBRARY && !GNUSTEP_BASE_LIBRARY
595 // Note: incorrect behaviour if NGExtensions is dynamically loaded !
596 // TODO: can we do anything about this? Can we detect the situation and
597 // print a log instead of the compile warning?
598 // Note: the above refers to the situation when a framework is implicitly
599 // loaded by loading a bundle (the framework is not linked against
602 bundle = [NSBundle mainBundle];
603 NSMapInsert(self->classToBundle, aClass, bundle);
604 NSMapInsert(self->classNameToBundle, NSStringFromClass(aClass), bundle);
608 - (NSBundle *)bundleWithPath:(NSString *)path {
609 NSBundle *bundle = nil;
612 path = [path stringByResolvingSymlinksInPath];
616 if (debugOn) NSLog(@"find bundle for path: '%@'", path);
617 bundle = NSMapGet(self->pathToBundle, path);
620 if (debugOn) NSLog(@" found: %@", bundle);
624 if ((bundle = [(NGBundle *)[NGBundle alloc] initWithPath:path]) == nil) {
625 NSLog(@"ERROR(%s): could not create bundle for path: '%@'",
626 __PRETTY_FUNCTION__, path);
630 bn = [[bundle bundleName]
631 stringByAppendingPathExtension:[bundle bundleType]],
633 NSMapInsert(self->pathToBundle, path, bundle);
634 NSMapInsert(self->nameToBundle, bn, bundle);
638 - (NSBundle *)bundleWithName:(NSString *)_name type:(NSString *)_type {
642 bn = [_name stringByAppendingPathExtension:_type];
643 bundle = NSMapGet(self->nameToBundle, bn);
645 if (![bundle isNotNull]) {
646 bundle = [self bundleWithPath:
647 [self pathForBundleWithName:_name type:_type]];
650 if (![bundle isNotNull]) /* NSNull is used to signal missing bundles */
653 if (![[bundle bundleType] isEqualToString:_type])
659 - (NSBundle *)bundleWithName:(NSString *)_name {
660 return [self bundleWithName:_name type:@"bundle"];
663 - (NSBundle *)bundleForClassNamed:(NSString *)_className {
664 NSString *path = nil;
665 NSBundle *bundle = nil;
667 if (_className == nil)
670 /* first check in table */
672 if ((bundle = NSMapGet(self->classNameToBundle, _className)) != nil)
676 /* then look in runtime, reset load callback to avoid recursion */
682 loadCallback = _objc_lookup_class;
683 _objc_lookup_class = NULL;
684 clazz = NSClassFromString(_className);
685 _objc_lookup_class = loadCallback;
688 /* the class is already loaded */
689 bundle = [self bundleForClass:clazz];
690 NSMapInsert(self->classNameToBundle, _className, bundle);
694 #elif NeXT_RUNTIME || APPLE_RUNTIME
698 hookDoLookup = NO; // THREAD
699 clazz = NSClassFromString(_className);
703 /* the class is already loaded */
705 printf("found class in runtime: %s\n", [_className cString]);
707 bundle = [self bundleForClass:clazz];
708 NSMapInsert(self->classNameToBundle, _className, bundle);
713 printf("did NOT find class in runtime: %s\n", [_className cString]);
718 path = [self pathForBundleProvidingResource:_className
720 resourceSelector:_selectClassByVersion
721 context:NULL /* version */];
723 path = [path stringByResolvingSymlinksInPath];
724 NSAssert(path, @"couldn't resolve symlinks in path ..");
730 if ((bundle = [self bundleWithPath:path]) != nil)
731 NSMapInsert(self->classNameToBundle, _className, bundle);
742 - (NSArray *)bundlesRequiredByBundle:(NSBundle *)_bundle {
743 [self doesNotRecognizeSelector:_cmd];
747 - (NSArray *)classesProvidedByBundle:(NSBundle *)_bundle {
748 return [[_bundle providedResourcesOfType:@"classes"] valueForKey:@"name"];
750 - (NSArray *)classesRequiredByBundle:(NSBundle *)_bundle {
751 [self doesNotRecognizeSelector:_cmd];
757 - (NSString *)makeBundleInfoPath:(NSString *)_path {
758 #if (NeXT_Foundation_LIBRARY || APPLE_Foundation_LIBRARY) && !defined(GSWARN)
759 return [[[_path stringByAppendingPathComponent:@"Contents"]
760 stringByAppendingPathComponent:@"Resources"]
761 stringByAppendingPathComponent:@"bundle-info.plist"];
763 return [_path stringByAppendingPathComponent:@"bundle-info.plist"];
767 - (id)_initializeLoadedBundle:(NSBundle *)_bundle
768 info:(NSDictionary *)_bundleInfo
772 /* check whether a handler was specified */
774 if ((handler = [_bundleInfo objectForKey:@"bundleHandler"]) != nil) {
775 [self debugWithFormat:@"lookup bundle handler %@ of bundle: %@",
778 if ((handler = NSClassFromString(handler)) == nil) {
779 NSLog(@"ERROR: did not find handler class %@ of bundle %@.",
780 [_bundleInfo objectForKey:@"bundleHandler"], [_bundle bundlePath]);
781 handler = [_bundle principalClass];
784 handler = [handler alloc];
786 if ([handler respondsToSelector:@selector(initForBundle:bundleManager:)])
787 handler = [handler initForBundle:_bundle bundleManager:self];
789 handler = [handler init];
790 handler = [handler autorelease];
792 if (handler == nil) {
793 NSLog(@"ERROR: could not instantiate handler class %@ of bundle %@.",
794 [_bundleInfo objectForKey:@"bundleHandler"], [_bundle bundlePath]);
795 handler = [_bundle principalClass];
799 [self debugWithFormat:
800 @"no bundle handler, lookup principal class of bundle: %@",
802 if ((handler = [_bundle principalClass]) == nil) {
803 /* use NGBundle class as default bundle handler */
804 #if !(NeXT_Foundation_LIBRARY || COCOA_Foundation_LIBRARY)
805 [self warnWithFormat:@"bundle has no principal class: %@", _bundle];
807 handler = [NGBundle class];
810 [self debugWithFormat:@" => %@", handler];
818 - (NSDictionary *)_loadBundleInfoAtExistingPath:(NSString *)_path {
819 NSDictionary *bundleInfo;
822 #if NeXT_Foundation_LIBRARY || COCOA_Foundation_LIBRARY
823 bundleInfo = NGParsePropertyListFromFile(_path);
825 bundleInfo = [NSDictionary dictionaryWithContentsOfFile:_path];
827 if (bundleInfo == nil) {
828 NSLog(@"couldn't load bundle-info at path '%@' !", _path);
832 /* check required bundle manager version */
833 info = [bundleInfo objectForKey:@"requires"];
834 if ((info = [(NSDictionary *)info objectForKey:@"bundleManagerVersion"])) {
835 if ([info intValue] > [[self class] version]) {
836 /* bundle manager version does not match ... */
840 NSMapInsert(self->pathToBundleInfo, _path, bundleInfo);
844 - (NSBundle *)_locateBundleForClassInfo:(NSDictionary *)_classInfo {
848 if (_classInfo == nil)
850 if ((className = [_classInfo objectForKey:@"name"]) == nil) {
851 NSLog(@"ERROR: missing classname in bundle-info.plist class section !");
855 // TODO: do we need to check the runtime for already loaded classes?
856 // Yes, I think so. But avoid recursions
858 #if APPLE_Foundation_LIBRARY || COCOA_Foundation_LIBRARY
859 // TODO: HACK, see above. w/o this, we get issues.
860 if ([className hasPrefix:@"NS"])
865 if ((bundle = [self bundleForClassNamed:className]) == nil) {
866 #if 0 // class might be already loaded
867 NSLog(@"ERROR: did not find class %@ required by bundle %@.",
868 className, [_bundle bundlePath]);
873 NSLog(@"CLASS %@ => BUNDLE %@", className, bundle);
877 - (NSArray *)_locateBundlesForClassInfos:(NSEnumerator *)_classInfos {
878 NSMutableArray *requiredBundles;
881 requiredBundles = [NSMutableArray arrayWithCapacity:16];
882 while ((i = [_classInfos nextObject]) != nil) {
885 if ((bundle = [self _locateBundleForClassInfo:i]) == nil)
888 [requiredBundles addObject:bundle];
890 return requiredBundles;
893 - (BOOL)_preLoadBundle:(NSBundle *)_bundle info:(NSDictionary *)_bundleInfo {
894 /* TODO: split up this huge method */
895 NSDictionary *requires;
896 NSMutableArray *requiredBundles = nil;
897 NSBundle *requiredBundle = nil;
899 requires = [_bundleInfo objectForKey:@"requires"];
902 /* invalid bundle info specified */
905 /* load required bundles */
910 /* locate required bundles */
912 e = [[requires objectForKey:@"bundles"] objectEnumerator];
913 while ((i = [e nextObject]) != nil) {
914 NSString *bundleName;
916 if (![i respondsToSelector:@selector(objectForKey:)]) {
917 NSLog(@"ERROR(%s): invalid bundle-info of bundle %@ !!!\n"
918 @" requires-entry is not a dictionary: %@",
919 __PRETTY_FUNCTION__, _bundle, i);
923 if ((bundleName = [i objectForKey:@"name"])) {
926 type = [i objectForKey:@"type"];
927 if (type == nil) type = @"bundle";
929 if ((requiredBundle = [self bundleWithName:bundleName type:type])) {
930 if (requiredBundles == nil)
931 requiredBundles = [NSMutableArray arrayWithCapacity:16];
933 [requiredBundles addObject:requiredBundle];
936 NSLog(@"ERROR(NGBundleManager): did not find bundle '%@' (type=%@) "
937 @"required by bundle %@.",
938 bundleName, type, [_bundle bundlePath]);
943 NSLog(@"ERROR: error in bundle-info.plist of bundle %@", _bundle);
947 /* load located bundles */
951 e = [requiredBundles objectEnumerator];
952 while ((requiredBundle = [e nextObject]) != nil) {
955 if ((bundleMaster = [self loadBundle:requiredBundle]) == Nil) {
956 NSLog(@"ERROR: could not load bundle %@ (%@) required by bundle %@.",
957 [requiredBundle bundlePath], requiredBundle,
958 [_bundle bundlePath]);
964 /* load required classes */
969 reqClasses = [requires objectForKey:@"classes"];
971 bundles = [self _locateBundlesForClassInfos:[reqClasses objectEnumerator]];
972 if (requiredBundles == nil)
973 requiredBundles = [NSMutableArray arrayWithCapacity:16];
974 [requiredBundles addObjectsFromArray:bundles];
977 /* load located bundles */
981 e = [requiredBundles objectEnumerator];
982 while ((requiredBundle = [e nextObject]) != nil) {
985 if ((bundleMaster = [self loadBundle:requiredBundle]) == Nil) {
986 NSLog(@"ERROR: could not load bundle %@ (%@) required by bundle %@.",
987 [requiredBundle bundlePath], requiredBundle,
988 [_bundle bundlePath]);
994 /* check whether versions of classes match */
999 e = [[requires objectForKey:@"classes"] objectEnumerator];
1000 while ((i = [e nextObject]) != nil) {
1001 NSString *className;
1004 if ((className = [i objectForKey:@"name"]) == nil)
1007 if ((clazz = NSClassFromString(className)) == Nil)
1010 if ([i objectForKey:@"exact-version"]) {
1013 v = [[i objectForKey:@"exact-version"] intValue];
1015 if (v != [clazz version]) {
1016 NSLog(@"ERROR: required exact class match failed:\n"
1018 @" required version: %i\n"
1019 @" loaded version: %i\n"
1023 [_bundle bundlePath]);
1026 else if ([i objectForKey:@"version"]) {
1029 v = [[i objectForKey:@"version"] intValue];
1031 if (v > [clazz version]) {
1032 NSLog(@"ERROR: provided class does not match required version:\n"
1034 @" least required version: %i\n"
1035 @" loaded version: %i\n"
1039 [_bundle bundlePath]);
1047 - (BOOL)_postLoadBundle:(NSBundle *)_bundle info:(NSDictionary *)_bundleInfo {
1051 - (id)loadBundle:(NSBundle *)_bundle {
1052 NSString *path = nil;
1053 NSDictionary *bundleInfo = nil;
1054 id bundleManager = nil;
1057 NSAssert(self->loadedBundles, @"missing loadedBundles hashmap ..");
1060 if ((bundleManager = NSMapGet(self->loadedBundles, _bundle)))
1061 return bundleManager;
1063 if (_bundle == [NSBundle mainBundle])
1064 return [NSBundle mainBundle];
1066 if ([self->loadingBundles containsObject:_bundle])
1070 if (self->loadingBundles == nil)
1071 self->loadingBundles = [[NSMutableSet allocWithZone:[self zone]] init];
1072 [self->loadingBundles addObject:_bundle];
1074 path = [_bundle bundlePath];
1075 path = [self makeBundleInfoPath:path];
1077 if ((bundleInfo = NSMapGet(self->pathToBundleInfo, path)) == nil) {
1078 if ([[NSFileManager defaultManager] fileExistsAtPath:path])
1079 bundleInfo = [self _loadBundleInfoAtExistingPath:path];
1082 if (![self _preLoadBundle:_bundle info:bundleInfo])
1085 if (![_bundle _loadForBundleManager:self])
1088 if (![self _postLoadBundle:_bundle info:bundleInfo])
1091 if ((bundleManager =
1092 [self _initializeLoadedBundle:_bundle info:bundleInfo])) {
1093 NSMapInsert(self->loadedBundles, _bundle, bundleManager);
1095 if ([bundleManager respondsToSelector:
1096 @selector(bundleManager:didLoadBundle:)])
1097 [bundleManager bundleManager:self didLoadBundle:_bundle];
1101 NSLog(@"ERROR(%s): couldn't initialize loaded bundle '%@'",
1102 __PRETTY_FUNCTION__, [_bundle bundlePath]);
1106 [self->loadingBundles removeObject:_bundle];
1108 if (bundleManager) {
1109 if (bundleInfo == nil)
1110 bundleInfo = [NSDictionary dictionary];
1112 [[NSNotificationCenter defaultCenter]
1113 postNotificationName:
1114 NGBundleWasLoadedNotificationName
1116 userInfo:[NSDictionary dictionaryWithObjectsAndKeys:
1117 self, @"NGBundleManager",
1118 bundleManager, @"NGBundleHandler",
1119 bundleInfo, @"NGBundleInfo",
1122 return bundleManager;
1127 - (id)principalObjectOfBundle:(NSBundle *)_bundle {
1128 return (id)NSMapGet(self->loadedBundles, _bundle);
1133 static BOOL _doesInfoMatch(NSArray *keys, NSDictionary *dict, NSDictionary *info)
1137 for (i = 0, count = [keys count]; i < count; i++) {
1141 key = [keys objectAtIndex:i];
1142 vv = [info objectForKey:key];
1145 /* info has no matching key */
1149 kv = [dict objectForKey:key];
1150 if (![kv isEqual:vv])
1156 - (NSDictionary *)configForResource:(id)_resource ofType:(NSString *)_type
1157 providedByBundle:(NSBundle *)_bundle
1159 NSDictionary *bundleInfo = nil;
1161 NSEnumerator *providedResources;
1162 NSArray *rnKeys = nil;
1166 if ([_resource respondsToSelector:@selector(objectForKey:)]) {
1167 rnKeys = [_resource allKeys];
1168 rnKeyCount = [rnKeys count];
1171 infoPath = [self makeBundleInfoPath:[_bundle bundlePath]];
1173 /* check whether info is in cache */
1174 if ((bundleInfo = NSMapGet(self->pathToBundleInfo, infoPath)) == nil) {
1175 if (![[NSFileManager defaultManager] fileExistsAtPath:infoPath])
1176 /* no bundle-info.plist available .. */
1180 bundleInfo = [self _loadBundleInfoAtExistingPath:infoPath];
1183 /* get provided resources config */
1186 [[(NSDictionary *)[bundleInfo objectForKey:@"provides"] objectForKey:_type]
1188 if (providedResources == nil) return nil;
1190 /* scan provided resources */
1192 while ((info = [providedResources nextObject])) {
1194 if (!_doesInfoMatch(rnKeys, _resource, info))
1200 name = [[(NSDictionary *)info objectForKey:@"name"] stringValue];
1201 if (name == nil) continue;
1202 if (![name isEqualToString:_resource]) continue;
1209 - (void)_processInfoForProvidedResources:(NSDictionary *)info
1210 ofType:(NSString *)_type path:(NSString *)path
1211 resourceName:(NSString *)_resourceName
1212 resourceSelector:(NGBundleResourceSelector)_selector
1213 context:(void *)_context
1214 andAddToResultArray:(NSMutableArray *)result
1216 NSEnumerator *providedResources = nil;
1217 if (info == nil) return;
1219 /* direct match (a bundle was specified in the path) */
1221 providedResources = [[(NSDictionary *)[info objectForKey:@"provides"]
1225 if (providedResources == nil) return;
1227 /* scan provide array */
1228 while ((info = [providedResources nextObject])) {
1231 if ((name = [[info objectForKey:@"name"] stringValue]) == nil)
1234 if (_resourceName) {
1235 if (![name isEqualToString:_resourceName])
1239 if (!_selector(_resourceName, _type, path, info, self, _context))
1243 [result addObject:path];
1247 - (NSArray *)pathsForBundlesProvidingResource:(NSString *)_resourceName
1248 ofType:(NSString *)_type
1249 resourceSelector:(NGBundleResourceSelector)_selector
1250 context:(void *)_context
1252 /* TODO: split up method */
1253 NSMutableArray *result = nil;
1259 NSLog(@"BM LOOKUP pathes (%d bundles loaded): %@ / %@",
1260 NSCountMapTable(self->loadedBundles), _resourceName, _type);
1263 fm = [NSFileManager defaultManager];
1264 result = [NSMutableArray arrayWithCapacity:64];
1266 // TODO: look in loaded bundles
1268 /* check physical pathes */
1270 e = [self->bundleSearchPaths objectEnumerator];
1271 while ((path = [e nextObject]) != nil) {
1274 NSString *tmp, *bundleDirPath;
1277 if (![fm fileExistsAtPath:path isDirectory:&isDir])
1280 if (!isDir) continue;
1282 /* check whether an appropriate bundle is contained in 'path' */
1284 dir = [[fm directoryContentsAtPath:path] objectEnumerator];
1285 while ((bundleDirPath = [dir nextObject]) != nil) {
1286 NSDictionary *bundleInfo = nil;
1287 NSEnumerator *providedResources = nil;
1291 bundleDirPath = [path stringByAppendingPathComponent:bundleDirPath];
1292 infoPath = [self makeBundleInfoPath:bundleDirPath];
1294 // TODO: can we use _doesBundleInfo:path:providedResource:... ?
1295 if ((bundleInfo = NSMapGet(self->pathToBundleInfo, infoPath))==nil) {
1296 if (![fm fileExistsAtPath:infoPath])
1299 bundleInfo = [self _loadBundleInfoAtExistingPath:infoPath];
1303 [[(NSDictionary *)[bundleInfo objectForKey:@"provides"]
1306 if (providedResources == nil) continue;
1308 /* scan 'provides' array */
1309 while ((info = [providedResources nextObject])) {
1312 name = [[(NSDictionary *)info objectForKey:@"name"] stringValue];
1313 if (name == nil) continue;
1315 if (_resourceName != nil) {
1316 if (![name isEqualToString:_resourceName])
1319 if (_selector != NULL) {
1320 if (!_selector(name, _type, bundleDirPath, info, self, _context))
1324 [result addObject:bundleDirPath];
1329 /* check for direct match (NGBundlePath element is a bundle) */
1331 tmp = [self makeBundleInfoPath:path];
1333 if ((info = NSMapGet(self->pathToBundleInfo, tmp)) == nil) {
1334 if ([fm fileExistsAtPath:tmp])
1335 info = [self _loadBundleInfoAtExistingPath:tmp];
1338 [self _processInfoForProvidedResources:info ofType:_type path:path
1339 resourceName:_resourceName resourceSelector:_selector
1341 andAddToResultArray:result];
1344 if ([result count] == 0) {
1345 [self logWithFormat:
1346 @"Note(%s): method does not search in loaded bundles for "
1347 @"resources of type '%@'",
1348 __PRETTY_FUNCTION__, _type];
1351 return [[result copy] autorelease];
1354 - (BOOL)_doesBundleInfo:(NSDictionary *)_bundleInfo path:(NSString *)_path
1355 provideResource:(id)_resourceName ofType:(NSString *)_type
1356 rnKeys:(NSArray *)_rnKeys
1357 resourceSelector:(NGBundleResourceSelector)_selector context:(void *)_context
1359 NSEnumerator *providedResources;
1363 [[(NSDictionary *)[_bundleInfo objectForKey:@"provides"]
1364 objectForKey:_type] objectEnumerator];
1365 if (providedResources == nil) return NO;
1367 /* scan provide array */
1368 while ((info = [providedResources nextObject])) {
1369 if (_rnKeys != nil) {
1370 if (!_doesInfoMatch(_rnKeys, _resourceName, info))
1376 name = [[(NSDictionary *)info objectForKey:@"name"] stringValue];
1377 if (name == nil) continue;
1378 if (![name isEqualToString:_resourceName]) continue;
1381 if (_selector != NULL) {
1382 if (!_selector(_resourceName, _type, _path, info, self, _context))
1386 /* all conditions applied (found) */
1392 - (NSString *)pathOfLoadedBundleProvidingResource:(id)_resourceName
1393 ofType:(NSString *)_type
1394 resourceSelector:(NGBundleResourceSelector)_selector context:(void *)_context
1396 NSMapEnumerator menum;
1398 NSDictionary *bundleInfo;
1401 rnKeys = ([_resourceName respondsToSelector:@selector(objectForKey:)])
1402 ? [_resourceName allKeys]
1405 menum = NSEnumerateMapTable(self->pathToBundleInfo);
1406 while (NSNextMapEnumeratorPair(&menum, (void *)&path, (void *)&bundleInfo)) {
1408 NSLog(@"check loaded bundle for resource %@: %@", _resourceName,
1412 if ([self _doesBundleInfo:bundleInfo path:path
1413 provideResource:_resourceName ofType:_type rnKeys:rnKeys
1414 resourceSelector:_selector context:_context])
1415 /* strip bundle-info.plist name */
1416 return [path stringByDeletingLastPathComponent];
1422 - (NSString *)pathForBundleProvidingResource:(id)_resourceName
1423 ofType:(NSString *)_type
1424 resourceSelector:(NGBundleResourceSelector)_selector
1425 context:(void *)_context
1427 /* main path lookup method */
1428 // TODO: this method seriously needs some refactoring
1432 NSArray *rnKeys = nil;
1436 NSLog(@"BM LOOKUP path (%d bundles loaded): %@ / %@",
1437 NSCountMapTable(self->loadedBundles), _resourceName, _type);
1440 /* look in loaded bundles */
1442 path = [self pathOfLoadedBundleProvidingResource:_resourceName ofType:_type
1443 resourceSelector:_selector context:_context];
1444 if (path != nil) return path;
1446 /* look in filesystem */
1448 if ([_resourceName respondsToSelector:@selector(objectForKey:)]) {
1449 rnKeys = [_resourceName allKeys];
1450 rnKeyCount = [rnKeys count];
1453 fm = [NSFileManager defaultManager];
1454 e = [self->bundleSearchPaths objectEnumerator];
1455 while ((path = [e nextObject]) != nil) {
1461 if (![fm fileExistsAtPath:path isDirectory:&isDir])
1464 if (!isDir) continue;
1466 /* check whether an appropriate bundle is contained in 'path' */
1468 dir = [[fm directoryContentsAtPath:path] objectEnumerator];
1469 while ((tmp = [dir nextObject]) != nil) {
1470 NSDictionary *bundleInfo = nil;
1473 tmp = [path stringByAppendingPathComponent:tmp];
1474 infoPath = [self makeBundleInfoPath:tmp];
1477 NSLog(@"check path path=%@ info=%@", tmp, infoPath);
1479 if ((bundleInfo = NSMapGet(self->pathToBundleInfo, infoPath)) == nil) {
1480 if (![fm fileExistsAtPath:infoPath])
1483 bundleInfo = [self _loadBundleInfoAtExistingPath:infoPath];
1486 NSLog(@"found info for path=%@ info=%@: %@", tmp,infoPath,bundleInfo);
1488 if ([self _doesBundleInfo:bundleInfo path:tmp
1489 provideResource:_resourceName ofType:_type rnKeys:rnKeys
1490 resourceSelector:_selector context:_context])
1494 /* check for direct match */
1496 tmp = [self makeBundleInfoPath:path];
1498 if ((info = NSMapGet(self->pathToBundleInfo, tmp)) == nil) {
1499 if ([fm fileExistsAtPath:tmp])
1500 info = [self _loadBundleInfoAtExistingPath:tmp];
1502 NSLog(@"WARNING(%s): did not find direct path '%@'",
1503 __PRETTY_FUNCTION__, tmp);
1508 // direct match (a bundle was specified in the path)
1509 NSEnumerator *providedResources;
1510 NSDictionary *provides;
1512 provides = [(NSDictionary *)info objectForKey:@"provides"];
1513 providedResources = [[provides objectForKey:_type] objectEnumerator];
1515 if (providedResources == nil) continue;
1517 // scan provide array
1518 while ((info = [providedResources nextObject])) {
1520 if (!_doesInfoMatch(rnKeys, _resourceName, info))
1526 name = [[(NSDictionary *)info objectForKey:@"name"] stringValue];
1527 if (name == nil) continue;
1528 if (![name isEqualToString:_resourceName]) continue;
1532 if (!_selector(_resourceName, _type, tmp, info, self, _context))
1535 /* all conditions applied */
1543 - (NSBundle *)bundleProvidingResource:(id)_name ofType:(NSString *)_type {
1546 if (debugOn) NSLog(@"BM LOOKUP: %@ / %@", _name, _type);
1548 bp = [self pathForBundleProvidingResource:_name
1550 resourceSelector:NULL context:nil];
1551 if ([bp length] == 0) {
1552 #if (NeXT_Foundation_LIBRARY || APPLE_Foundation_LIBRARY) && HEAVY_DEBUG
1553 NSLog(@"%s: found no resource '%@' of type '%@' ...",
1554 __PRETTY_FUNCTION__, _resourceName, _resourceType);
1556 if (debugOn) NSLog(@" did not find: %@ / %@", _name, _type);
1560 if (debugOn) NSLog(@" FOUND: %@", bp);
1561 return [self bundleWithPath:bp];
1564 - (NSArray *)bundlesProvidingResource:(id)_resourceName
1565 ofType:(NSString *)_type
1568 NSMutableArray *bundles;
1571 paths = [self pathsForBundlesProvidingResource:_resourceName
1573 resourceSelector:NULL context:nil];
1575 count = [paths count];
1576 if (paths == nil) return nil;
1577 if (count == 0) return paths;
1579 bundles = [NSMutableArray arrayWithCapacity:count];
1580 for (i = 0; i < count; i++) {
1583 if ((bundle = [self bundleWithPath:[paths objectAtIndex:i]]))
1584 [bundles addObject:bundle];
1586 return [[bundles copy] autorelease];
1589 - (NSArray *)providedResourcesOfType:(NSString *)_resourceType
1590 inBundle:(NSBundle *)_bundle
1593 NSDictionary *bundleInfo;
1595 path = [self makeBundleInfoPath:[_bundle bundlePath]];
1596 if (path == nil) return nil;
1598 /* retrieve bundle info dictionary */
1599 if ((bundleInfo = NSMapGet(self->pathToBundleInfo, path)) == nil)
1600 bundleInfo = [self _loadBundleInfoAtExistingPath:path];
1602 return [(NSDictionary *)[bundleInfo objectForKey:@"provides"]
1603 objectForKey:_resourceType];
1606 - (void)_addRegisteredProvidedResourcesOfType:(NSString *)_type
1607 toSet:(NSMutableSet *)_result
1609 NSMapEnumerator menum;
1611 NSDictionary *bundleInfo;
1613 menum = NSEnumerateMapTable(self->pathToBundleInfo);
1614 while (NSNextMapEnumeratorPair(&menum, (void *)&path, (void *)&bundleInfo)) {
1615 NSArray *providedResources;
1618 NSLog(@"check loaded bundle for resource types %@: %@", _type, path);
1621 [(NSDictionary *)[bundleInfo objectForKey:@"provides"]
1622 objectForKey:_type];
1623 if (providedResources == nil) continue;
1625 [_result addObjectsFromArray:providedResources];
1629 - (NSArray *)providedResourcesOfType:(NSString *)_resourceType {
1630 NSMutableSet *result = nil;
1631 NSFileManager *fm = [NSFileManager defaultManager];
1635 result = [NSMutableSet setWithCapacity:128];
1637 /* scan loaded bundles */
1639 [self _addRegisteredProvidedResourcesOfType:_resourceType toSet:result];
1641 /* scan all bundle search paths */
1643 e = [self->bundleSearchPaths objectEnumerator];
1644 while ((path = [e nextObject]) != nil) {
1650 if (![fm fileExistsAtPath:path isDirectory:&isDir])
1652 if (!isDir) continue;
1654 /* check whether an appropriate bundle is contained in 'path' */
1656 // TODO: move to own method
1657 dir = [[fm directoryContentsAtPath:path] objectEnumerator];
1658 while ((tmp = [dir nextObject]) != nil) {
1659 NSDictionary *bundleInfo = nil;
1660 NSArray *providedResources = nil;
1663 tmp = [path stringByAppendingPathComponent:tmp];
1664 infoPath = [self makeBundleInfoPath:tmp];
1667 NSLog(@" info path: %@", tmp);
1670 if ((bundleInfo = NSMapGet(self->pathToBundleInfo, infoPath)) == nil) {
1671 if (![fm fileExistsAtPath:infoPath])
1674 bundleInfo = [self _loadBundleInfoAtExistingPath:infoPath];
1678 [(NSDictionary *)[bundleInfo objectForKey:@"provides"]
1679 objectForKey:_resourceType];
1680 if (providedResources == nil) continue;
1682 [result addObjectsFromArray:providedResources];
1685 /* check for direct match */
1687 tmp = [self makeBundleInfoPath:path];
1689 if ((info = NSMapGet(self->pathToBundleInfo, tmp)) == nil) {
1690 if ([fm fileExistsAtPath:tmp])
1691 info = [self _loadBundleInfoAtExistingPath:tmp];
1695 // direct match (a bundle was specified in the path)
1696 NSArray *providedResources;
1697 NSDictionary *provides;
1699 provides = [(NSDictionary *)info objectForKey:@"provides"];
1700 providedResources = [provides objectForKey:_resourceType];
1702 if (providedResources == nil) continue;
1704 [result addObjectsFromArray:providedResources];
1707 return [result allObjects];
1710 - (NSBundle *)bundleProvidingResourceOfType:(NSString *)_resourceType
1711 matchingQualifier:(EOQualifier *)_qual
1713 NSFileManager *fm = [NSFileManager defaultManager];
1717 /* foreach search path entry */
1719 e = [self->bundleSearchPaths objectEnumerator];
1720 while ((path = [e nextObject])) {
1723 if ([fm fileExistsAtPath:path isDirectory:&isDir]) {
1726 if (!isDir) continue;
1728 /* check whether an appropriate bundle is contained in 'path' */
1732 dir = [[fm directoryContentsAtPath:path] objectEnumerator];
1733 while ((tmp = [dir nextObject])) {
1734 NSDictionary *bundleInfo;
1735 NSArray *providedResources;
1738 tmp = [path stringByAppendingPathComponent:tmp];
1739 infoPath = [self makeBundleInfoPath:tmp];
1741 if ((bundleInfo=NSMapGet(self->pathToBundleInfo, infoPath)) == nil) {
1742 if (![fm fileExistsAtPath:infoPath])
1745 bundleInfo = [self _loadBundleInfoAtExistingPath:infoPath];
1748 bundleInfo = [bundleInfo objectForKey:@"provides"];
1749 providedResources = [bundleInfo objectForKey:_resourceType];
1751 if (providedResources == nil) continue;
1754 [providedResources filteredArrayUsingQualifier:_qual];
1756 if ([providedResources count] > 0)
1757 return [self bundleWithPath:tmp];
1761 /* check for direct match */
1763 tmp = [self makeBundleInfoPath:path];
1765 if ((info = NSMapGet(self->pathToBundleInfo, tmp)) == nil) {
1766 if ([fm fileExistsAtPath:tmp])
1767 info = [self _loadBundleInfoAtExistingPath:tmp];
1771 // direct match (a bundle was specified in the path)
1772 NSArray *providedResources;
1773 NSDictionary *provides;
1775 provides = [(NSDictionary *)info objectForKey:@"provides"];
1776 providedResources = [provides objectForKey:_resourceType];
1778 if (providedResources == nil) continue;
1781 [providedResources filteredArrayUsingQualifier:_qual];
1783 if ([providedResources count] > 0)
1784 return [self bundleWithPath:path];
1791 - (NSBundle *)bundlesProvidingResourcesOfType:(NSString *)_resourceType
1792 matchingQualifier:(EOQualifier *)_qual
1794 NSMutableArray *bundles = nil;
1795 NSFileManager *fm = [NSFileManager defaultManager];
1799 bundles = [NSMutableArray arrayWithCapacity:128];
1801 /* foreach search path entry */
1803 e = [self->bundleSearchPaths objectEnumerator];
1804 while ((path = [e nextObject])) {
1807 if ([fm fileExistsAtPath:path isDirectory:&isDir]) {
1810 if (!isDir) continue;
1812 /* check whether an appropriate bundle is contained in 'path' */
1816 dir = [[fm directoryContentsAtPath:path] objectEnumerator];
1817 while ((tmp = [dir nextObject])) {
1818 NSDictionary *bundleInfo = nil;
1819 NSArray *providedResources = nil;
1822 tmp = [path stringByAppendingPathComponent:tmp];
1823 infoPath = [self makeBundleInfoPath:tmp];
1825 if ((bundleInfo=NSMapGet(self->pathToBundleInfo, infoPath)) == nil) {
1826 if (![fm fileExistsAtPath:infoPath])
1829 bundleInfo = [self _loadBundleInfoAtExistingPath:infoPath];
1832 bundleInfo = [bundleInfo objectForKey:@"provides"];
1833 providedResources = [bundleInfo objectForKey:_resourceType];
1835 if (providedResources == nil) continue;
1838 [providedResources filteredArrayUsingQualifier:_qual];
1840 if ([providedResources count] > 0)
1841 [bundles addObject:[self bundleWithPath:tmp]];
1845 /* check for direct match */
1847 tmp = [self makeBundleInfoPath:path];
1849 if ((info = NSMapGet(self->pathToBundleInfo, tmp)) == nil) {
1850 if ([fm fileExistsAtPath:tmp])
1851 info = [self _loadBundleInfoAtExistingPath:tmp];
1855 // direct match (a bundle was specified in the path)
1856 NSArray *providedResources;
1857 NSDictionary *provides;
1859 provides = [(NSDictionary *)info objectForKey:@"provides"];
1860 providedResources = [provides objectForKey:_resourceType];
1862 if (providedResources == nil) continue;
1865 [providedResources filteredArrayUsingQualifier:_qual];
1867 if ([providedResources count] > 0)
1868 [bundles addObject:[self bundleWithPath:path]];
1872 return [[bundles copy] autorelease];
1877 - (void)_bundleDidLoadNotifcation:(NSNotification *)_notification {
1878 NSDictionary *ui = [_notification userInfo];
1881 NSLog(@"bundle %@ did load with classes %@",
1882 [[_notification object] bundlePath],
1883 [ui objectForKey:@"NSLoadedClasses"]);
1886 [self registerBundle:[_notification object]
1887 classes:[ui objectForKey:@"NSLoadedClasses"]
1888 categories:[ui objectForKey:@"NSLoadedCategories"]];
1893 - (BOOL)isDebuggingEnabled {
1897 @end /* NGBundleManager */
1899 @implementation NSBundle(BundleManagerSupport)
1902 return [NGBundle alloc];
1904 + (id)allocWithZone:(NSZone *)zone {
1905 return [NGBundle allocWithZone:zone];
1908 #if !(NeXT_Foundation_LIBRARY || APPLE_Foundation_LIBRARY)
1909 //#warning remember, bundleForClass is not overridden !
1911 + (NSBundle *)bundleForClass:(Class)aClass {
1912 return [[NGBundleManager defaultBundleManager] bundleForClass:aClass];
1915 + (NSBundle *)bundleWithPath:(NSString*)path {
1916 return [[NGBundleManager defaultBundleManager] bundleWithPath:path];
1920 @end /* NSBundle(BundleManagerSupport) */
1922 @implementation NSBundle(NGBundleManagerExtensions)
1924 - (id)principalObject {
1925 return [[NGBundleManager defaultBundleManager]
1926 principalObjectOfBundle:self];
1929 - (NSArray *)providedResourcesOfType:(NSString *)_resourceType {
1930 return [[NGBundleManager defaultBundleManager]
1931 providedResourcesOfType:_resourceType
1935 - (NSString *)bundleName {
1936 return [[[self bundlePath] lastPathComponent] stringByDeletingPathExtension];
1939 - (NSString *)bundleType {
1940 return [[self bundlePath] pathExtension];
1943 - (NSArray *)providedClasses {
1944 return [[NGBundleManager defaultBundleManager] classesProvidedByBundle:self];
1947 - (NSArray *)requiredClasses {
1948 return [[NGBundleManager defaultBundleManager] classesRequiredByBundle:self];
1951 - (NSArray *)requiredBundles {
1952 return [[NGBundleManager defaultBundleManager] bundlesRequiredByBundle:self];
1955 - (NSDictionary *)configForResource:(id)_resource ofType:(NSString *)_type {
1956 return [[NGBundleManager defaultBundleManager]
1957 configForResource:_resource ofType:_type
1958 providedByBundle:self];
1963 - (BOOL)_loadForBundleManager:(NGBundleManager *)_manager {
1967 @end /* NSBundle(NGBundleManagerExtensions) */
1969 @implementation NSBundle(NGLanguageResourceExtensions)
1971 static BOOL debugLanguageLookup = NO;
1973 // locating resources
1975 - (NSString *)pathForResource:(NSString *)_name ofType:(NSString *)_ext
1976 inDirectory:(NSString *)_directory
1977 languages:(NSArray *)_languages
1980 NSString *path = nil;
1982 id (*objAtIdx)(id,SEL,int);
1984 if (debugLanguageLookup) {
1985 NSLog(@"LOOKUP(%s): %@ | %@ | %@ | %@", __PRETTY_FUNCTION__,
1986 _name, _ext, _directory, [_languages componentsJoinedByString:@","]);
1989 path = [self bundlePath];
1990 if ([_directory isNotNull]) {
1991 // TODO: should we change that?
1992 path = [path stringByAppendingPathComponent:_directory];
1995 #if (NeXT_Foundation_LIBRARY || APPLE_Foundation_LIBRARY)
1996 path = [path stringByAppendingPathComponent:@"Contents"];
1998 path = [path stringByAppendingPathComponent:@"Resources"];
2001 if (debugLanguageLookup) NSLog(@" BASE: %@", path);
2003 fm = [NSFileManager defaultManager];
2004 if (![fm fileExistsAtPath:path])
2007 if (_ext != nil) _name = [_name stringByAppendingPathExtension:_ext];
2009 langCount = [_languages count];
2010 objAtIdx = (langCount > 0)
2011 ? (void*)[_languages methodForSelector:@selector(objectAtIndex:)]
2014 for (i = 0; i < langCount; i++) {
2019 ? objAtIdx(_languages, @selector(objectAtIndex:), i)
2020 : [_languages objectAtIndex:i];
2022 language = [language stringByAppendingPathExtension:@"lproj"];
2023 lpath = [path stringByAppendingPathComponent:language];
2024 lpath = [lpath stringByAppendingPathComponent:_name];
2026 if ([fm fileExistsAtPath:lpath])
2030 if (debugLanguageLookup)
2031 NSLog(@" no language matched, check base: %@", path);
2033 /* now look into x.bundle/Resources/name.type */
2034 if ([fm fileExistsAtPath:[path stringByAppendingPathComponent:_name]])
2035 return [path stringByAppendingPathComponent:_name];
2040 - (NSString *)pathForResource:(NSString *)_name ofType:(NSString *)_ext
2041 languages:(NSArray *)_languages
2045 path = [self pathForResource:_name ofType:_ext
2046 inDirectory:@"Resources"
2047 languages:_languages];
2048 if (path) return path;
2050 path = [self pathForResource:_name ofType:_ext
2052 languages:_languages];
2056 @end /* NSBundle(NGLanguageResourceExtensions) */
2058 @implementation NGBundle
2061 return [self allocWithZone:NULL];
2063 + (id)allocWithZone:(NSZone*)zone {
2064 return NSAllocateObject(self, 0, zone);
2067 - (id)initWithPath:(NSString *)__path {
2068 return [super initWithPath:__path];
2073 - (BOOL)_loadForBundleManager:(NGBundleManager *)_manager {
2074 return [super load];
2078 NGBundleManager *bm;
2080 bm = [NGBundleManager defaultBundleManager];
2082 return [bm loadBundle:self] ? YES : NO;
2085 + (NSBundle *)bundleForClass:(Class)aClass {
2086 return [[NGBundleManager defaultBundleManager] bundleForClass:aClass];
2088 + (NSBundle *)bundleWithPath:(NSString*)path {
2089 return [[NGBundleManager defaultBundleManager] bundleWithPath:path];
2092 #if GNUSTEP_BASE_LIBRARY
2094 - (Class)principalClass {
2098 if ((c = [super principalClass]))
2101 if ((cname = [[self infoDictionary] objectForKey:@"NSPrincipalClass"]) ==nil)
2104 if ((c = NSClassFromString(cname)))
2107 NSLog(@"%s: did not find principal class named '%@' of bundle %@",
2108 __PRETTY_FUNCTION__, cname, self);
2114 - (NSString *)description {
2118 "<%s %p fullPath: %s infoDictionary: %p loaded=%s>",
2119 (char*)object_get_class_name(self),
2121 [[self bundlePath] cString],
2122 [self infoDictionary],
2123 self->_codeLoaded ? "yes" : "no");
2125 return [NSString stringWithCString:buffer];