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;
53 static int _getClassHook(const char *className) {
54 if (className == NULL) return 0;
57 printf("lookup class '%s'.\n", className);
59 if (objc_lookUpClass(className))
63 static NGBundleManager *manager = nil;
67 NSLog(@"%s: look for class %s", __PRETTY_FUNCTION__, className);
69 manager = [NGBundleManager defaultBundleManager];
71 bundle = [manager bundleForClassNamed:
72 [NSString stringWithCString:className]];
75 NSLog(@"%s: found bundle %@", __PRETTY_FUNCTION__,
79 if (![manager loadBundle:bundle]) {
81 "bundleManager couldn't load bundle for class '%s'.\n",
86 Class c = objc_lookUpClass(className);
87 NSLog(@"%s: loaded bundle %@ for className %s class %@",
89 bundle, className, c);
101 #include <objc/objc-api.h>
103 static Class (*oldClassLoadHook)(const char *_name) = NULL;
105 static inline BOOL _isValidClassName(const char *_name) {
108 if (_name == NULL) return NO;
110 for (len = 0; (len < 256) && (*_name != '\0'); len++, _name++) {
112 if (!isalnum((int)*_name))
116 return (len == 256) ? NO : YES;
119 static Class _classLoadHook(const char *_name) {
120 if (_isValidClassName(_name)) {
121 static NGBundleManager *manager = nil;
124 //NSLog(@"%s: look for class %s", __PRETTY_FUNCTION__, _name);
126 manager = [NGBundleManager defaultBundleManager];
128 bundle = [manager bundleForClassNamed:[NSString stringWithCString:_name]];
131 NSLog(@"%s: found bundle %@", __PRETTY_FUNCTION__, [bundle bundlePath]);
134 if ([manager loadBundle:bundle]) {
138 hook = _objc_lookup_class;
139 _objc_lookup_class = NULL;
140 clazz = objc_lookup_class(_name);
141 _objc_lookup_class = hook;
143 if (clazz) return clazz;
147 return (oldClassLoadHook != NULL) ? oldClassLoadHook(_name) : Nil;
149 #endif // GNU_RUNTIME
151 NSString *NGBundleWasLoadedNotificationName = @"NGBundleWasLoadedNotification";
153 @interface NSBundle(NGBundleManagerPrivate)
154 - (BOOL)_loadForBundleManager:(NGBundleManager *)_manager;
157 @interface NGBundleManager(PrivateMethods)
159 - (void)registerBundle:(NSBundle *)_bundle
160 classes:(NSArray *)_classes
161 categories:(NSArray *)_categories;
163 - (NSString *)pathForBundleProvidingResource:(NSString *)_resourceName
164 ofType:(NSString *)_type
165 resourceSelector:(NGBundleResourceSelector)_selector
166 context:(void *)_ctx;
168 - (NSString *)makeBundleInfoPath:(NSString *)_path;
172 static BOOL _selectClassByVersion(NSString *_resourceName,
173 NSString *_resourceType,
175 NSDictionary *_resourceConfig,
176 NGBundleManager *_bundleManager,
182 if (![_resourceType isEqualToString:@"classes"])
185 if (_version == NULL)
187 if ([(id)_version intValue] == -1)
190 if ((tmp = [_resourceConfig objectForKey:@"version"])) {
191 classVersion = [tmp intValue];
193 if (classVersion < [(id)_version intValue]) {
194 NSLog(@"WARNING: class version mismatch for class %@: "
195 @"requested at least version %i, got version %i",
196 _resourceName, [(id)_version intValue], classVersion);
199 if ((tmp = [_resourceConfig objectForKey:@"exact-version"])) {
200 classVersion = [tmp intValue];
202 if (classVersion != [(id)_version intValue]) {
203 NSLog(@"WARNING: class version mismatch for class %@: "
204 @"requested exact version %i, got version %i",
205 _resourceName, [(id)_version intValue], classVersion);
211 @implementation NGBundleManager
214 static NGBundleManager *defaultManager = nil;
215 static BOOL debugOn = NO;
217 #if defined(__MINGW32__)
218 static NSString *NGEnvVarPathSeparator = @";";
220 static NSString *NGEnvVarPathSeparator = @":";
224 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
226 debugOn = [ud boolForKey:@"NGBundleManagerDebugEnabled"];
231 if (_objc_lookup_class != _classLoadHook) {
232 oldClassLoadHook = _objc_lookup_class;
233 _objc_lookup_class = _classLoadHook;
238 + (id)defaultBundleManager {
239 if (defaultManager == nil) {
240 defaultManager = [[NGBundleManager alloc] init];
242 #if NeXT_RUNTIME || APPLE_RUNTIME
244 static BOOL didRegisterCallback = NO;
246 if (!didRegisterCallback) {
247 didRegisterCallback = YES;
248 objc_setClassHandler(_getClassHook);
254 if (_objc_lookup_class != _classLoadHook) {
255 oldClassLoadHook = _objc_lookup_class;
256 _objc_lookup_class = _classLoadHook;
260 return defaultManager;
263 /* setup bundle search path */
265 - (void)_addMainBundlePathToPathArray:(NSMutableArray *)_paths {
269 pi = [NSProcessInfo processInfo];
270 path = [[pi arguments] objectAtIndex:0];
271 path = [path stringByDeletingLastPathComponent];
273 if ([path isEqual:@""])
277 // TODO: to be correct this would need to read the bundle-info
280 The path is the complete path to the executable, including the
281 processor, the OS and the library combo. Strip these directories
282 from the main bundle's path.
284 #if LIB_FOUNDATION_LIBRARY
285 if (![NSBundle isFlattenedDirLayout])
287 path = [[[path stringByDeletingLastPathComponent]
288 stringByDeletingLastPathComponent]
289 stringByDeletingLastPathComponent];
292 [_paths addObject:path];
295 - (void)_addBundlePathDefaultToPathArray:(NSMutableArray *)_paths {
299 if ((ud = [NSUserDefaults standardUserDefaults]) == nil) {
300 // got this with gstep-base during the port, apparently it happens
301 // if the bundle manager is created inside the setup process of
302 // gstep-base (for whatever reason)
303 NSLog(@"ERROR(NGBundleManager): got no system userdefaults object!");
309 if ((paths = [ud arrayForKey:@"NGBundlePath"]) == nil) {
310 if ((paths = [ud stringForKey:@"NGBundlePath"]) != nil)
311 paths = [paths componentsSeparatedByString:NGEnvVarPathSeparator];
314 [_paths addObjectsFromArray:paths];
316 NSLog(@"Note: NGBundlePath default is not configured.");
319 - (void)_addEnvironmentPathToPathArray:(NSMutableArray *)_paths {
323 pi = [NSProcessInfo processInfo];
324 paths = [[pi environment] objectForKey:@"NGBundlePath"];
326 paths = [paths componentsSeparatedByString:NGEnvVarPathSeparator];
327 if (paths) [_paths addObjectsFromArray:paths];
330 - (void)_addGNUstepPathsToPathArray:(NSMutableArray *)_paths {
333 // TODO: whats that supposed to do?
334 // TODO: replace with proper path locator function!
340 env = [[NSProcessInfo processInfo] environment];
342 if ((tmp = [env objectForKey:@"GNUSTEP_PATHPREFIX_LIST"]) == nil)
343 tmp = [env objectForKey:@"GNUSTEP_PATHLIST"];
344 tmp = [tmp componentsSeparatedByString:@":"];
346 for (i = 0, count = [tmp count]; i < count; i++) {
347 p = [tmp objectAtIndex:i];
348 p = [p stringByAppendingPathComponent:@"Library"];
349 p = [p stringByAppendingPathComponent:@"Bundles"];
350 if ([self->bundleSearchPaths containsObject:p]) continue;
352 if (p) [self->bundleSearchPaths addObject:p];
357 - (void)_setupBundleSearchPathes {
360 pi = [NSProcessInfo processInfo];
362 /* setup bundle search path */
364 self->bundleSearchPaths = [[NSMutableArray alloc] initWithCapacity:16];
366 [self _addMainBundlePathToPathArray:self->bundleSearchPaths];
367 [self _addBundlePathDefaultToPathArray:self->bundleSearchPaths];
368 [self _addEnvironmentPathToPathArray:self->bundleSearchPaths];
369 [self _addGNUstepPathsToPathArray:self->bundleSearchPaths];
371 #if DEBUG && NeXT_Foundation_LIBRARY && 0
372 NSLog(@"%s: bundle search pathes:\n%@", __PRETTY_FUNCTION__,
373 self->bundleSearchPaths);
377 - (void)_registerLoadedBundles {
378 NSEnumerator *currentBundles;
379 NSBundle *loadedBundle;
381 currentBundles = [[NSBundle allBundles] objectEnumerator];
382 while ((loadedBundle = [currentBundles nextObject]))
383 [self registerBundle:loadedBundle classes:nil categories:nil];
386 - (void)_registerForBundleLoadNotification {
387 [[NSNotificationCenter defaultCenter]
389 selector:@selector(_bundleDidLoadNotifcation:)
390 name:@"NSBundleDidLoadNotification"
395 #if GNUSTEP_BASE_LIBRARY
396 if ([NSUserDefaults standardUserDefaults] == nil) {
397 /* called inside setup process, deny creation (HACK) */
403 if ((self = [super init])) {
404 self->classToBundle =
405 NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks,
406 NSNonRetainedObjectMapValueCallBacks,
408 self->classNameToBundle =
409 NSCreateMapTable(NSObjectMapKeyCallBacks,
410 NSNonRetainedObjectMapValueCallBacks,
412 self->categoryNameToBundle =
413 NSCreateMapTable(NSObjectMapKeyCallBacks,
414 NSNonRetainedObjectMapValueCallBacks,
417 NSCreateMapTable(NSObjectMapKeyCallBacks,
418 NSNonRetainedObjectMapValueCallBacks,
420 self->pathToBundleInfo =
421 NSCreateMapTable(NSObjectMapKeyCallBacks,
422 NSObjectMapValueCallBacks,
425 NSCreateMapTable(NSObjectMapKeyCallBacks,
426 NSNonRetainedObjectMapValueCallBacks,
428 self->loadedBundles =
429 NSCreateMapTable(NSNonRetainedObjectMapKeyCallBacks,
430 NSObjectMapValueCallBacks,
433 [self _setupBundleSearchPathes];
434 [self _registerLoadedBundles];
435 [self _registerForBundleLoadNotification];
441 [self->loadingBundles release];
442 if (self->loadedBundles) NSFreeMapTable(self->loadedBundles);
443 if (self->classToBundle) NSFreeMapTable(self->classToBundle);
444 if (self->classNameToBundle) NSFreeMapTable(self->classNameToBundle);
445 if (self->categoryNameToBundle) NSFreeMapTable(self->categoryNameToBundle);
446 if (self->pathToBundle) NSFreeMapTable(self->pathToBundle);
447 if (self->pathToBundleInfo) NSFreeMapTable(self->pathToBundleInfo);
448 if (self->nameToBundle) NSFreeMapTable(self->nameToBundle);
449 [self->bundleSearchPaths release];
455 - (void)setBundleSearchPaths:(NSArray *)_paths {
456 ASSIGNCOPY(self->bundleSearchPaths, _paths);
458 - (NSArray *)bundleSearchPaths {
459 return self->bundleSearchPaths;
462 /* registering bundles */
464 - (void)registerBundle:(NSBundle *)_bundle
465 classes:(NSArray *)_classes
466 categories:(NSArray *)_categories
471 //NSLog(@"NGBundleManager: register loaded bundle %@", [_bundle bundlePath]);
473 e = [_classes objectEnumerator];
474 while ((v = [e nextObject])) {
475 NSMapInsert(self->classToBundle, NSClassFromString(v), _bundle);
476 NSMapInsert(self->classNameToBundle, v, _bundle);
479 e = [_categories objectEnumerator];
480 while ((v = [e nextObject]))
481 NSMapInsert(self->categoryNameToBundle, v, _bundle);
486 - (NSString *)pathForBundleWithName:(NSString *)_name type:(NSString *)_type {
487 NSFileManager *fm = [NSFileManager defaultManager];
490 NSString *bundlePath;
493 /* first check in table */
496 bundlePath = [_name stringByAppendingPathExtension:_type];
498 if ((bundle = NSMapGet(self->nameToBundle, bundlePath)))
499 return [bundle bundlePath];
501 e = [self->bundleSearchPaths objectEnumerator];
502 while ((path = [e nextObject])) {
505 if ([fm fileExistsAtPath:path isDirectory:&isDir]) {
506 if (!isDir) continue;
508 if ([[path lastPathComponent] isEqualToString:bundlePath]) {
509 // direct match (a bundle was specified in the path)
515 tmp = [path stringByAppendingPathComponent:bundlePath];
516 if ([fm fileExistsAtPath:tmp isDirectory:&isDir]) {
527 /* getting bundles */
529 - (NSBundle *)bundleForClass:(Class)aClass {
530 /* this method never loads a dynamic bundle (since the class is set up) */
533 bundle = NSMapGet(self->classToBundle, aClass);
535 #if NeXT_Foundation_LIBRARY || COCOA_Foundation_LIBRARY
537 bundle = [NSBundle bundleForClass:aClass];
538 if (bundle == [NSBundle mainBundle])
544 if the class wasn't loaded from a bundle, it's *either* the main bundle
545 or a bundle loaded before NGExtension was loaded !!!
548 #if !LIB_FOUNDATION_LIBRARY && !GNUSTEP_BASE_LIBRARY
549 // Note: incorrect behaviour if NGExtensions is dynamically loaded !
550 // TODO: can we do anything about this? Can we detect the situation and
551 // print a log instead of the compile warning?
552 // Note: the above refers to the situation when a framework is implicitly
553 // loaded by loading a bundle (the framework is not linked against
556 bundle = [NSBundle mainBundle];
557 NSMapInsert(self->classToBundle, aClass, bundle);
558 NSMapInsert(self->classNameToBundle, NSStringFromClass(aClass), bundle);
562 - (NSBundle *)bundleWithPath:(NSString *)path {
563 NSBundle *bundle = nil;
566 path = [path stringByResolvingSymlinksInPath];
570 if (debugOn) NSLog(@"find bundle for path: '%@'", path);
571 bundle = NSMapGet(self->pathToBundle, path);
574 if (debugOn) NSLog(@" found: %@", bundle);
578 if ((bundle = [(NGBundle *)[NGBundle alloc] initWithPath:path]) == nil) {
579 NSLog(@"ERROR(%s): could not create bundle for path: '%@'",
580 __PRETTY_FUNCTION__, path);
584 bn = [[bundle bundleName]
585 stringByAppendingPathExtension:[bundle bundleType]],
587 NSMapInsert(self->pathToBundle, path, bundle);
588 NSMapInsert(self->nameToBundle, bn, bundle);
592 - (NSBundle *)bundleWithName:(NSString *)_name type:(NSString *)_type {
596 bn = [_name stringByAppendingPathExtension:_type];
597 bundle = NSMapGet(self->nameToBundle, bn);
599 if (![bundle isNotNull]) {
600 bundle = [self bundleWithPath:
601 [self pathForBundleWithName:_name type:_type]];
604 if (![bundle isNotNull]) /* NSNull is used to signal missing bundles */
607 if (![[bundle bundleType] isEqualToString:_type])
613 - (NSBundle *)bundleWithName:(NSString *)_name {
614 return [self bundleWithName:_name type:@"bundle"];
617 - (NSBundle *)bundleForClassNamed:(NSString *)_className {
618 NSString *path = nil;
619 NSBundle *bundle = nil;
621 if (_className == nil)
624 /* first check in table */
626 if ((bundle = NSMapGet(self->classNameToBundle, _className)))
630 /* then look in runtime, reset load callback to avoid recursion */
637 loadCallback = _objc_lookup_class;
638 _objc_lookup_class = NULL;
639 clazz = NSClassFromString(_className);
640 _objc_lookup_class = loadCallback;
643 /* the class is already loaded */
644 bundle = [self bundleForClass:clazz];
645 NSMapInsert(self->classNameToBundle, _className, bundle);
651 path = [self pathForBundleProvidingResource:_className
653 resourceSelector:_selectClassByVersion
654 context:NULL /* version */];
656 path = [path stringByResolvingSymlinksInPath];
657 NSAssert(path, @"couldn't resolve symlinks in path ..");
663 if ((bundle = [self bundleWithPath:path]))
664 NSMapInsert(self->classNameToBundle, _className, bundle);
675 - (NSArray *)bundlesRequiredByBundle:(NSBundle *)_bundle {
676 [self doesNotRecognizeSelector:_cmd];
680 - (NSArray *)classesProvidedByBundle:(NSBundle *)_bundle {
681 return [[_bundle providedResourcesOfType:@"classes"] valueForKey:@"name"];
683 - (NSArray *)classesRequiredByBundle:(NSBundle *)_bundle {
684 [self doesNotRecognizeSelector:_cmd];
690 - (NSString *)makeBundleInfoPath:(NSString *)_path {
691 #if (NeXT_Foundation_LIBRARY || APPLE_Foundation_LIBRARY) && !defined(GSWARN)
692 return [[[_path stringByAppendingPathComponent:@"Contents"]
693 stringByAppendingPathComponent:@"Resources"]
694 stringByAppendingPathComponent:@"bundle-info.plist"];
696 return [_path stringByAppendingPathComponent:@"bundle-info.plist"];
700 - (id)_initializeLoadedBundle:(NSBundle *)_bundle
701 info:(NSDictionary *)_bundleInfo
705 /* check whether a handler was specified */
707 if ((handler = [_bundleInfo objectForKey:@"bundleHandler"]) != nil) {
708 [self debugWithFormat:@"lookup bundle handler %@ of bundle: %@",
711 if ((handler = NSClassFromString(handler)) == nil) {
712 NSLog(@"ERROR: did not find handler class %@ of bundle %@.",
713 [_bundleInfo objectForKey:@"bundleHandler"], [_bundle bundlePath]);
714 handler = [_bundle principalClass];
717 handler = [handler alloc];
719 if ([handler respondsToSelector:@selector(initForBundle:bundleManager:)])
720 handler = [handler initForBundle:_bundle bundleManager:self];
722 handler = [handler init];
723 handler = [handler autorelease];
725 if (handler == nil) {
726 NSLog(@"ERROR: could not instantiate handler class %@ of bundle %@.",
727 [_bundleInfo objectForKey:@"bundleHandler"], [_bundle bundlePath]);
728 handler = [_bundle principalClass];
732 [self debugWithFormat:
733 @"no bundle handler, lookup principal class of bundle: %@",
735 if ((handler = [_bundle principalClass]) == nil)
736 /* use NGBundle class as default bundle handler */
737 handler = [NGBundle class];
745 - (NSDictionary *)_loadBundleInfoAtExistingPath:(NSString *)_path {
746 NSDictionary *bundleInfo;
749 #if NeXT_Foundation_LIBRARY || COCOA_Foundation_LIBRARY
750 bundleInfo = NGParsePropertyListFromFile(_path);
752 bundleInfo = [NSDictionary dictionaryWithContentsOfFile:_path];
754 if (bundleInfo == nil) {
755 NSLog(@"couldn't load bundle-info at path '%@' !", _path);
759 /* check required bundle manager version */
760 info = [bundleInfo objectForKey:@"requires"];
761 if ((info = [(NSDictionary *)info objectForKey:@"bundleManagerVersion"])) {
762 if ([info intValue] > [[self class] version]) {
763 /* bundle manager version does not match ... */
767 NSMapInsert(self->pathToBundleInfo, _path, bundleInfo);
771 - (NSBundle *)_locateBundleForClassInfo:(NSDictionary *)_classInfo {
775 if (_classInfo == nil)
777 if ((className = [_classInfo objectForKey:@"name"]) == nil) {
778 NSLog(@"ERROR: missing classname in bundle-info.plist class section !");
782 if ((bundle = [self bundleForClassNamed:className]) == nil) {
783 #if 0 // class might be already loaded
784 NSLog(@"ERROR: did not find class %@ required by bundle %@.",
785 className, [_bundle bundlePath]);
791 - (NSArray *)_locateBundlesForClassInfos:(NSEnumerator *)_classInfos {
792 NSMutableArray *requiredBundles;
795 requiredBundles = [NSMutableArray arrayWithCapacity:16];
796 while ((i = [_classInfos nextObject])) {
799 if ((bundle = [self _locateBundleForClassInfo:i]) == nil)
802 [requiredBundles addObject:bundle];
804 return requiredBundles;
807 - (BOOL)_preLoadBundle:(NSBundle *)_bundle info:(NSDictionary *)_bundleInfo {
808 /* TODO: split up this huge method */
809 NSDictionary *requires = nil;
810 NSMutableArray *requiredBundles = nil;
811 NSBundle *requiredBundle = nil;
813 requires = [_bundleInfo objectForKey:@"requires"];
816 /* invalid bundle info specified */
819 /* load required bundles */
824 /* locate required bundles */
826 e = [[requires objectForKey:@"bundles"] objectEnumerator];
827 while ((i = [e nextObject])) {
828 NSString *bundleName;
830 if (![i respondsToSelector:@selector(objectForKey:)]) {
831 NSLog(@"ERROR(%s): invalid bundle-info of bundle %@ !!!\n"
832 @" requires-entry is not a dictionary: %@",
833 __PRETTY_FUNCTION__, _bundle, i);
837 if ((bundleName = [i objectForKey:@"name"])) {
840 type = [i objectForKey:@"type"];
841 if (type == nil) type = @"bundle";
843 if ((requiredBundle = [self bundleWithName:bundleName type:type])) {
844 if (requiredBundles == nil)
845 requiredBundles = [NSMutableArray arrayWithCapacity:16];
847 [requiredBundles addObject:requiredBundle];
850 NSLog(@"ERROR(NGBundleManager): did not find bundle '%@' (type=%@) "
851 @"required by bundle %@.",
852 bundleName, type, [_bundle bundlePath]);
857 NSLog(@"ERROR: error in bundle-info.plist of bundle %@", _bundle);
861 /* load located bundles */
865 e = [requiredBundles objectEnumerator];
866 while ((requiredBundle = [e nextObject])) {
869 if ((bundleMaster = [self loadBundle:requiredBundle]) == Nil) {
870 NSLog(@"ERROR: could not load bundle %@ (%@) required by bundle %@.",
871 [requiredBundle bundlePath], requiredBundle, [_bundle bundlePath]);
877 /* load required classes */
882 reqClasses = [requires objectForKey:@"classes"];
883 bundles = [self _locateBundlesForClassInfos:[reqClasses objectEnumerator]];
884 if (requiredBundles == nil)
885 requiredBundles = [NSMutableArray arrayWithCapacity:16];
886 [requiredBundles addObjectsFromArray:bundles];
889 /* load located bundles */
893 e = [requiredBundles objectEnumerator];
894 while ((requiredBundle = [e nextObject])) {
897 if ((bundleMaster = [self loadBundle:requiredBundle]) == Nil) {
898 NSLog(@"ERROR: could not load bundle %@ (%@) required by bundle %@.",
899 [requiredBundle bundlePath], requiredBundle, [_bundle bundlePath]);
905 /* check whether versions of classes match */
910 e = [[requires objectForKey:@"classes"] objectEnumerator];
911 while ((i = [e nextObject])) {
915 if ((className = [i objectForKey:@"name"]) == nil)
918 if ((clazz = NSClassFromString(className)) == Nil)
921 if ([i objectForKey:@"exact-version"]) {
924 v = [[i objectForKey:@"exact-version"] intValue];
926 if (v != [clazz version]) {
927 NSLog(@"ERROR: required exact class match failed:\n"
929 @" required version: %i\n"
930 @" loaded version: %i\n"
934 [_bundle bundlePath]);
937 else if ([i objectForKey:@"version"]) {
940 v = [[i objectForKey:@"version"] intValue];
942 if (v > [clazz version]) {
943 NSLog(@"ERROR: provided class does not match required version:\n"
945 @" least required version: %i\n"
946 @" loaded version: %i\n"
950 [_bundle bundlePath]);
958 - (BOOL)_postLoadBundle:(NSBundle *)_bundle info:(NSDictionary *)_bundleInfo {
962 - (id)loadBundle:(NSBundle *)_bundle {
963 NSString *path = nil;
964 NSDictionary *bundleInfo = nil;
965 id bundleManager = nil;
968 NSAssert(self->loadedBundles, @"missing loadedBundles hashmap ..");
971 if ((bundleManager = NSMapGet(self->loadedBundles, _bundle)))
972 return bundleManager;
974 if (_bundle == [NSBundle mainBundle])
975 return [NSBundle mainBundle];
977 if ([self->loadingBundles containsObject:_bundle])
981 if (self->loadingBundles == nil)
982 self->loadingBundles = [[NSMutableSet allocWithZone:[self zone]] init];
983 [self->loadingBundles addObject:_bundle];
985 path = [_bundle bundlePath];
986 path = [self makeBundleInfoPath:path];
988 if ((bundleInfo = NSMapGet(self->pathToBundleInfo, path)) == nil) {
989 if ([[NSFileManager defaultManager] fileExistsAtPath:path])
990 bundleInfo = [self _loadBundleInfoAtExistingPath:path];
993 if (![self _preLoadBundle:_bundle info:bundleInfo])
996 if (![_bundle _loadForBundleManager:self])
999 if (![self _postLoadBundle:_bundle info:bundleInfo])
1002 if ((bundleManager =
1003 [self _initializeLoadedBundle:_bundle info:bundleInfo])) {
1004 NSMapInsert(self->loadedBundles, _bundle, bundleManager);
1006 if ([bundleManager respondsToSelector:
1007 @selector(bundleManager:didLoadBundle:)])
1008 [bundleManager bundleManager:self didLoadBundle:_bundle];
1012 NSLog(@"ERROR(%s): couldn't initialize loaded bundle '%@'",
1013 __PRETTY_FUNCTION__, [_bundle bundlePath]);
1017 [self->loadingBundles removeObject:_bundle];
1019 if (bundleManager) {
1020 if (bundleInfo == nil)
1021 bundleInfo = [NSDictionary dictionary];
1023 [[NSNotificationCenter defaultCenter]
1024 postNotificationName:
1025 NGBundleWasLoadedNotificationName
1027 userInfo:[NSDictionary dictionaryWithObjectsAndKeys:
1028 self, @"NGBundleManager",
1029 bundleManager, @"NGBundleHandler",
1030 bundleInfo, @"NGBundleInfo",
1033 return bundleManager;
1038 - (id)principalObjectOfBundle:(NSBundle *)_bundle {
1039 return (id)NSMapGet(self->loadedBundles, _bundle);
1044 static BOOL _doesInfoMatch(NSArray *keys, NSDictionary *dict, NSDictionary *info)
1048 for (i = 0, count = [keys count]; i < count; i++) {
1052 key = [keys objectAtIndex:i];
1053 vv = [info objectForKey:key];
1056 /* info has no matching key */
1060 kv = [dict objectForKey:key];
1061 if (![kv isEqual:vv])
1067 - (NSDictionary *)configForResource:(id)_resource ofType:(NSString *)_type
1068 providedByBundle:(NSBundle *)_bundle
1070 NSDictionary *bundleInfo = nil;
1072 NSEnumerator *providedResources;
1073 NSArray *rnKeys = nil;
1077 if ([_resource respondsToSelector:@selector(objectForKey:)]) {
1078 rnKeys = [_resource allKeys];
1079 rnKeyCount = [rnKeys count];
1082 infoPath = [self makeBundleInfoPath:[_bundle bundlePath]];
1084 /* check whether info is in cache */
1085 if ((bundleInfo = NSMapGet(self->pathToBundleInfo, infoPath)) == nil) {
1086 if (![[NSFileManager defaultManager] fileExistsAtPath:infoPath])
1087 /* no bundle-info.plist available .. */
1091 bundleInfo = [self _loadBundleInfoAtExistingPath:infoPath];
1094 /* get provided resources config */
1097 [[(NSDictionary *)[bundleInfo objectForKey:@"provides"] objectForKey:_type]
1099 if (providedResources == nil) return nil;
1101 /* scan provided resources */
1103 while ((info = [providedResources nextObject])) {
1105 if (!_doesInfoMatch(rnKeys, _resource, info))
1111 name = [[(NSDictionary *)info objectForKey:@"name"] stringValue];
1112 if (name == nil) continue;
1113 if (![name isEqualToString:_resource]) continue;
1120 - (void)_processInfoForProvidedResources:(NSDictionary *)info
1121 ofType:(NSString *)_type path:(NSString *)path
1122 resourceName:(NSString *)_resourceName
1123 resourceSelector:(NGBundleResourceSelector)_selector
1124 context:(void *)_context
1125 andAddToResultArray:(NSMutableArray *)result
1127 NSEnumerator *providedResources = nil;
1128 if (info == nil) return;
1130 /* direct match (a bundle was specified in the path) */
1132 providedResources = [[(NSDictionary *)[info objectForKey:@"provides"]
1136 if (providedResources == nil) return;
1138 /* scan provide array */
1139 while ((info = [providedResources nextObject])) {
1142 if ((name = [[info objectForKey:@"name"] stringValue]) == nil)
1145 if (_resourceName) {
1146 if (![name isEqualToString:_resourceName])
1150 if (!_selector(_resourceName, _type, path, info, self, _context))
1154 [result addObject:path];
1158 - (NSArray *)pathsForBundlesProvidingResource:(NSString *)_resourceName
1159 ofType:(NSString *)_type
1160 resourceSelector:(NGBundleResourceSelector)_selector
1161 context:(void *)_context
1163 /* TODO: split up method */
1164 NSMutableArray *result = nil;
1170 NSLog(@"BM LOOKUP pathes (%d bundles loaded): %@ / %@",
1171 NSCountMapTable(self->loadedBundles), _resourceName, _type);
1174 fm = [NSFileManager defaultManager];
1175 result = [NSMutableArray arrayWithCapacity:64];
1177 // TODO: look in loaded bundles
1179 /* check physical pathes */
1181 e = [self->bundleSearchPaths objectEnumerator];
1182 while ((path = [e nextObject]) != nil) {
1185 NSString *tmp, *bundleDirPath;
1188 if (![fm fileExistsAtPath:path isDirectory:&isDir])
1191 if (!isDir) continue;
1193 /* check whether an appropriate bundle is contained in 'path' */
1195 dir = [[fm directoryContentsAtPath:path] objectEnumerator];
1196 while ((bundleDirPath = [dir nextObject]) != nil) {
1197 NSDictionary *bundleInfo = nil;
1198 NSEnumerator *providedResources = nil;
1202 bundleDirPath = [path stringByAppendingPathComponent:bundleDirPath];
1203 infoPath = [self makeBundleInfoPath:bundleDirPath];
1205 // TODO: can we use _doesBundleInfo:path:providedResource:... ?
1206 if ((bundleInfo = NSMapGet(self->pathToBundleInfo, infoPath))==nil) {
1207 if (![fm fileExistsAtPath:infoPath])
1210 bundleInfo = [self _loadBundleInfoAtExistingPath:infoPath];
1214 [[(NSDictionary *)[bundleInfo objectForKey:@"provides"]
1217 if (providedResources == nil) continue;
1219 /* scan 'provides' array */
1220 while ((info = [providedResources nextObject])) {
1223 name = [[(NSDictionary *)info objectForKey:@"name"] stringValue];
1224 if (name == nil) continue;
1226 if (_resourceName != nil) {
1227 if (![name isEqualToString:_resourceName])
1230 if (_selector != NULL) {
1231 if (!_selector(name, _type, bundleDirPath, info, self, _context))
1235 [result addObject:bundleDirPath];
1240 /* check for direct match (NGBundlePath element is a bundle) */
1242 tmp = [self makeBundleInfoPath:path];
1244 if ((info = NSMapGet(self->pathToBundleInfo, tmp)) == nil) {
1245 if ([fm fileExistsAtPath:tmp])
1246 info = [self _loadBundleInfoAtExistingPath:tmp];
1249 [self _processInfoForProvidedResources:info ofType:_type path:path
1250 resourceName:_resourceName resourceSelector:_selector
1252 andAddToResultArray:result];
1255 if ([result count] == 0) {
1256 [self logWithFormat:
1257 @"Note(%s): method does not search in loaded bundles for "
1258 @"resources of type '%@'",
1259 __PRETTY_FUNCTION__, _type];
1262 return [[result copy] autorelease];
1265 - (BOOL)_doesBundleInfo:(NSDictionary *)_bundleInfo path:(NSString *)_path
1266 provideResource:(id)_resourceName ofType:(NSString *)_type
1267 rnKeys:(NSArray *)_rnKeys
1268 resourceSelector:(NGBundleResourceSelector)_selector context:(void *)_context
1270 NSEnumerator *providedResources;
1274 [[(NSDictionary *)[_bundleInfo objectForKey:@"provides"]
1275 objectForKey:_type] objectEnumerator];
1276 if (providedResources == nil) return NO;
1278 /* scan provide array */
1279 while ((info = [providedResources nextObject])) {
1280 if (_rnKeys != nil) {
1281 if (!_doesInfoMatch(_rnKeys, _resourceName, info))
1287 name = [[(NSDictionary *)info objectForKey:@"name"] stringValue];
1288 if (name == nil) continue;
1289 if (![name isEqualToString:_resourceName]) continue;
1292 if (_selector != NULL) {
1293 if (!_selector(_resourceName, _type, _path, info, self, _context))
1297 /* all conditions applied (found) */
1303 - (NSString *)pathOfLoadedBundleProvidingResource:(id)_resourceName
1304 ofType:(NSString *)_type
1305 resourceSelector:(NGBundleResourceSelector)_selector context:(void *)_context
1307 NSMapEnumerator menum;
1309 NSDictionary *bundleInfo;
1312 rnKeys = ([_resourceName respondsToSelector:@selector(objectForKey:)])
1313 ? [_resourceName allKeys]
1316 menum = NSEnumerateMapTable(self->pathToBundleInfo);
1317 while (NSNextMapEnumeratorPair(&menum, (void *)&path, (void *)&bundleInfo)) {
1319 NSLog(@"check loaded bundle for resource %@: %@", _resourceName,
1323 if ([self _doesBundleInfo:bundleInfo path:path
1324 provideResource:_resourceName ofType:_type rnKeys:rnKeys
1325 resourceSelector:_selector context:_context])
1326 /* strip bundle-info.plist name */
1327 return [path stringByDeletingLastPathComponent];
1333 - (NSString *)pathForBundleProvidingResource:(id)_resourceName
1334 ofType:(NSString *)_type
1335 resourceSelector:(NGBundleResourceSelector)_selector
1336 context:(void *)_context
1338 /* main path lookup method */
1339 // TODO: this method seriously needs some refactoring
1343 NSArray *rnKeys = nil;
1347 NSLog(@"BM LOOKUP path (%d bundles loaded): %@ / %@",
1348 NSCountMapTable(self->loadedBundles), _resourceName, _type);
1351 /* look in loaded bundles */
1353 path = [self pathOfLoadedBundleProvidingResource:_resourceName ofType:_type
1354 resourceSelector:_selector context:_context];
1355 if (path != nil) return path;
1357 /* look in filesystem */
1359 if ([_resourceName respondsToSelector:@selector(objectForKey:)]) {
1360 rnKeys = [_resourceName allKeys];
1361 rnKeyCount = [rnKeys count];
1364 fm = [NSFileManager defaultManager];
1365 e = [self->bundleSearchPaths objectEnumerator];
1366 while ((path = [e nextObject]) != nil) {
1372 if (![fm fileExistsAtPath:path isDirectory:&isDir])
1375 if (!isDir) continue;
1377 /* check whether an appropriate bundle is contained in 'path' */
1379 dir = [[fm directoryContentsAtPath:path] objectEnumerator];
1380 while ((tmp = [dir nextObject])) {
1381 NSDictionary *bundleInfo = nil;
1384 tmp = [path stringByAppendingPathComponent:tmp];
1385 infoPath = [self makeBundleInfoPath:tmp];
1388 NSLog(@"check path path=%@ info=%@", tmp, infoPath);
1390 if ((bundleInfo = NSMapGet(self->pathToBundleInfo, infoPath)) == nil) {
1391 if (![fm fileExistsAtPath:infoPath])
1394 bundleInfo = [self _loadBundleInfoAtExistingPath:infoPath];
1397 NSLog(@"found info for path=%@ info=%@: %@", tmp,infoPath,bundleInfo);
1399 if ([self _doesBundleInfo:bundleInfo path:tmp
1400 provideResource:_resourceName ofType:_type rnKeys:rnKeys
1401 resourceSelector:_selector context:_context])
1405 /* check for direct match */
1407 tmp = [self makeBundleInfoPath:path];
1409 if ((info = NSMapGet(self->pathToBundleInfo, tmp)) == nil) {
1410 if ([fm fileExistsAtPath:tmp])
1411 info = [self _loadBundleInfoAtExistingPath:tmp];
1413 NSLog(@"WARNING(%s): did not find direct path '%@'",
1414 __PRETTY_FUNCTION__, tmp);
1419 // direct match (a bundle was specified in the path)
1420 NSEnumerator *providedResources;
1421 NSDictionary *provides;
1423 provides = [(NSDictionary *)info objectForKey:@"provides"];
1424 providedResources = [[provides objectForKey:_type] objectEnumerator];
1426 if (providedResources == nil) continue;
1428 // scan provide array
1429 while ((info = [providedResources nextObject])) {
1431 if (!_doesInfoMatch(rnKeys, _resourceName, info))
1437 name = [[(NSDictionary *)info objectForKey:@"name"] stringValue];
1438 if (name == nil) continue;
1439 if (![name isEqualToString:_resourceName]) continue;
1443 if (!_selector(_resourceName, _type, tmp, info, self, _context))
1446 /* all conditions applied */
1454 - (NSBundle *)bundleProvidingResource:(id)_name ofType:(NSString *)_type {
1457 if (debugOn) NSLog(@"BM LOOKUP: %@ / %@", _name, _type);
1459 bp = [self pathForBundleProvidingResource:_name
1461 resourceSelector:NULL context:nil];
1462 if ([bp length] == 0) {
1463 #if (NeXT_Foundation_LIBRARY || APPLE_Foundation_LIBRARY) && HEAVY_DEBUG
1464 NSLog(@"%s: found no resource '%@' of type '%@' ...",
1465 __PRETTY_FUNCTION__, _resourceName, _resourceType);
1467 if (debugOn) NSLog(@" did not find: %@ / %@", _name, _type);
1471 if (debugOn) NSLog(@" FOUND: %@", bp);
1472 return [self bundleWithPath:bp];
1475 - (NSArray *)bundlesProvidingResource:(id)_resourceName
1476 ofType:(NSString *)_type
1479 NSMutableArray *bundles;
1482 paths = [self pathsForBundlesProvidingResource:_resourceName
1484 resourceSelector:NULL context:nil];
1486 count = [paths count];
1487 if (paths == nil) return nil;
1488 if (count == 0) return paths;
1490 bundles = [NSMutableArray arrayWithCapacity:count];
1491 for (i = 0; i < count; i++) {
1494 if ((bundle = [self bundleWithPath:[paths objectAtIndex:i]]))
1495 [bundles addObject:bundle];
1497 return [[bundles copy] autorelease];
1500 - (NSArray *)providedResourcesOfType:(NSString *)_resourceType
1501 inBundle:(NSBundle *)_bundle
1504 NSDictionary *bundleInfo;
1506 path = [self makeBundleInfoPath:[_bundle bundlePath]];
1507 if (path == nil) return nil;
1509 /* retrieve bundle info dictionary */
1510 if ((bundleInfo = NSMapGet(self->pathToBundleInfo, path)) == nil)
1511 bundleInfo = [self _loadBundleInfoAtExistingPath:path];
1513 return [(NSDictionary *)[bundleInfo objectForKey:@"provides"]
1514 objectForKey:_resourceType];
1517 - (void)_addRegisteredProvidedResourcesOfType:(NSString *)_type
1518 toSet:(NSMutableSet *)_result
1520 NSMapEnumerator menum;
1522 NSDictionary *bundleInfo;
1524 menum = NSEnumerateMapTable(self->pathToBundleInfo);
1525 while (NSNextMapEnumeratorPair(&menum, (void *)&path, (void *)&bundleInfo)) {
1526 NSArray *providedResources;
1529 NSLog(@"check loaded bundle for resource types %@: %@", _type, path);
1532 [(NSDictionary *)[bundleInfo objectForKey:@"provides"]
1533 objectForKey:_type];
1534 if (providedResources == nil) continue;
1536 [_result addObjectsFromArray:providedResources];
1540 - (NSArray *)providedResourcesOfType:(NSString *)_resourceType {
1541 NSMutableSet *result = nil;
1542 NSFileManager *fm = [NSFileManager defaultManager];
1546 result = [NSMutableSet setWithCapacity:128];
1548 /* scan loaded bundles */
1550 [self _addRegisteredProvidedResourcesOfType:_resourceType toSet:result];
1552 /* scan all bundle search paths */
1554 e = [self->bundleSearchPaths objectEnumerator];
1555 while ((path = [e nextObject]) != nil) {
1561 if (![fm fileExistsAtPath:path isDirectory:&isDir])
1563 if (!isDir) continue;
1565 /* check whether an appropriate bundle is contained in 'path' */
1567 // TODO: move to own method
1568 dir = [[fm directoryContentsAtPath:path] objectEnumerator];
1569 while ((tmp = [dir nextObject]) != nil) {
1570 NSDictionary *bundleInfo = nil;
1571 NSArray *providedResources = nil;
1574 tmp = [path stringByAppendingPathComponent:tmp];
1575 infoPath = [self makeBundleInfoPath:tmp];
1578 NSLog(@" info path: %@", tmp);
1581 if ((bundleInfo = NSMapGet(self->pathToBundleInfo, infoPath)) == nil) {
1582 if (![fm fileExistsAtPath:infoPath])
1585 bundleInfo = [self _loadBundleInfoAtExistingPath:infoPath];
1589 [(NSDictionary *)[bundleInfo objectForKey:@"provides"]
1590 objectForKey:_resourceType];
1591 if (providedResources == nil) continue;
1593 [result addObjectsFromArray:providedResources];
1596 /* check for direct match */
1598 tmp = [self makeBundleInfoPath:path];
1600 if ((info = NSMapGet(self->pathToBundleInfo, tmp)) == nil) {
1601 if ([fm fileExistsAtPath:tmp])
1602 info = [self _loadBundleInfoAtExistingPath:tmp];
1606 // direct match (a bundle was specified in the path)
1607 NSArray *providedResources;
1608 NSDictionary *provides;
1610 provides = [(NSDictionary *)info objectForKey:@"provides"];
1611 providedResources = [provides objectForKey:_resourceType];
1613 if (providedResources == nil) continue;
1615 [result addObjectsFromArray:providedResources];
1618 return [result allObjects];
1621 - (NSBundle *)bundleProvidingResourceOfType:(NSString *)_resourceType
1622 matchingQualifier:(EOQualifier *)_qual
1624 NSFileManager *fm = [NSFileManager defaultManager];
1628 /* foreach search path entry */
1630 e = [self->bundleSearchPaths objectEnumerator];
1631 while ((path = [e nextObject])) {
1634 if ([fm fileExistsAtPath:path isDirectory:&isDir]) {
1637 if (!isDir) continue;
1639 /* check whether an appropriate bundle is contained in 'path' */
1643 dir = [[fm directoryContentsAtPath:path] objectEnumerator];
1644 while ((tmp = [dir nextObject])) {
1645 NSDictionary *bundleInfo;
1646 NSArray *providedResources;
1649 tmp = [path stringByAppendingPathComponent:tmp];
1650 infoPath = [self makeBundleInfoPath:tmp];
1652 if ((bundleInfo=NSMapGet(self->pathToBundleInfo, infoPath)) == nil) {
1653 if (![fm fileExistsAtPath:infoPath])
1656 bundleInfo = [self _loadBundleInfoAtExistingPath:infoPath];
1659 bundleInfo = [bundleInfo objectForKey:@"provides"];
1660 providedResources = [bundleInfo objectForKey:_resourceType];
1662 if (providedResources == nil) continue;
1665 [providedResources filteredArrayUsingQualifier:_qual];
1667 if ([providedResources count] > 0)
1668 return [self bundleWithPath:tmp];
1672 /* check for direct match */
1674 tmp = [self makeBundleInfoPath:path];
1676 if ((info = NSMapGet(self->pathToBundleInfo, tmp)) == nil) {
1677 if ([fm fileExistsAtPath:tmp])
1678 info = [self _loadBundleInfoAtExistingPath:tmp];
1682 // direct match (a bundle was specified in the path)
1683 NSArray *providedResources;
1684 NSDictionary *provides;
1686 provides = [(NSDictionary *)info objectForKey:@"provides"];
1687 providedResources = [provides objectForKey:_resourceType];
1689 if (providedResources == nil) continue;
1692 [providedResources filteredArrayUsingQualifier:_qual];
1694 if ([providedResources count] > 0)
1695 return [self bundleWithPath:path];
1702 - (NSBundle *)bundlesProvidingResourcesOfType:(NSString *)_resourceType
1703 matchingQualifier:(EOQualifier *)_qual
1705 NSMutableArray *bundles = nil;
1706 NSFileManager *fm = [NSFileManager defaultManager];
1710 bundles = [NSMutableArray arrayWithCapacity:128];
1712 /* foreach search path entry */
1714 e = [self->bundleSearchPaths objectEnumerator];
1715 while ((path = [e nextObject])) {
1718 if ([fm fileExistsAtPath:path isDirectory:&isDir]) {
1721 if (!isDir) continue;
1723 /* check whether an appropriate bundle is contained in 'path' */
1727 dir = [[fm directoryContentsAtPath:path] objectEnumerator];
1728 while ((tmp = [dir nextObject])) {
1729 NSDictionary *bundleInfo = nil;
1730 NSArray *providedResources = nil;
1733 tmp = [path stringByAppendingPathComponent:tmp];
1734 infoPath = [self makeBundleInfoPath:tmp];
1736 if ((bundleInfo=NSMapGet(self->pathToBundleInfo, infoPath)) == nil) {
1737 if (![fm fileExistsAtPath:infoPath])
1740 bundleInfo = [self _loadBundleInfoAtExistingPath:infoPath];
1743 bundleInfo = [bundleInfo objectForKey:@"provides"];
1744 providedResources = [bundleInfo objectForKey:_resourceType];
1746 if (providedResources == nil) continue;
1749 [providedResources filteredArrayUsingQualifier:_qual];
1751 if ([providedResources count] > 0)
1752 [bundles addObject:[self bundleWithPath:tmp]];
1756 /* check for direct match */
1758 tmp = [self makeBundleInfoPath:path];
1760 if ((info = NSMapGet(self->pathToBundleInfo, tmp)) == nil) {
1761 if ([fm fileExistsAtPath:tmp])
1762 info = [self _loadBundleInfoAtExistingPath:tmp];
1766 // direct match (a bundle was specified in the path)
1767 NSArray *providedResources;
1768 NSDictionary *provides;
1770 provides = [(NSDictionary *)info objectForKey:@"provides"];
1771 providedResources = [provides objectForKey:_resourceType];
1773 if (providedResources == nil) continue;
1776 [providedResources filteredArrayUsingQualifier:_qual];
1778 if ([providedResources count] > 0)
1779 [bundles addObject:[self bundleWithPath:path]];
1783 return [[bundles copy] autorelease];
1788 - (void)_bundleDidLoadNotifcation:(NSNotification *)_notification {
1789 NSDictionary *ui = [_notification userInfo];
1792 NSLog(@"bundle %@ did load with classes %@",
1793 [[_notification object] bundlePath],
1794 [ui objectForKey:@"NSLoadedClasses"]);
1797 [self registerBundle:[_notification object]
1798 classes:[ui objectForKey:@"NSLoadedClasses"]
1799 categories:[ui objectForKey:@"NSLoadedCategories"]];
1804 - (BOOL)isDebuggingEnabled {
1808 @end /* NGBundleManager */
1810 @implementation NSBundle(BundleManagerSupport)
1813 return [NGBundle alloc];
1815 + (id)allocWithZone:(NSZone *)zone {
1816 return [NGBundle allocWithZone:zone];
1819 #if !(NeXT_Foundation_LIBRARY || APPLE_Foundation_LIBRARY)
1820 //#warning remember, bundleForClass is not overridden !
1822 + (NSBundle *)bundleForClass:(Class)aClass {
1823 return [[NGBundleManager defaultBundleManager] bundleForClass:aClass];
1826 + (NSBundle *)bundleWithPath:(NSString*)path {
1827 return [[NGBundleManager defaultBundleManager] bundleWithPath:path];
1831 @end /* NSBundle(BundleManagerSupport) */
1833 @implementation NSBundle(NGBundleManagerExtensions)
1835 - (id)principalObject {
1836 return [[NGBundleManager defaultBundleManager]
1837 principalObjectOfBundle:self];
1840 - (NSArray *)providedResourcesOfType:(NSString *)_resourceType {
1841 return [[NGBundleManager defaultBundleManager]
1842 providedResourcesOfType:_resourceType
1846 - (NSString *)bundleName {
1847 return [[[self bundlePath] lastPathComponent] stringByDeletingPathExtension];
1850 - (NSString *)bundleType {
1851 return [[self bundlePath] pathExtension];
1854 - (NSArray *)providedClasses {
1855 return [[NGBundleManager defaultBundleManager] classesProvidedByBundle:self];
1858 - (NSArray *)requiredClasses {
1859 return [[NGBundleManager defaultBundleManager] classesRequiredByBundle:self];
1862 - (NSArray *)requiredBundles {
1863 return [[NGBundleManager defaultBundleManager] bundlesRequiredByBundle:self];
1866 - (NSDictionary *)configForResource:(id)_resource ofType:(NSString *)_type {
1867 return [[NGBundleManager defaultBundleManager]
1868 configForResource:_resource ofType:_type
1869 providedByBundle:self];
1874 - (BOOL)_loadForBundleManager:(NGBundleManager *)_manager {
1878 @end /* NSBundle(NGBundleManagerExtensions) */
1880 @implementation NSBundle(NGLanguageResourceExtensions)
1882 static BOOL debugLanguageLookup = NO;
1884 // locating resources
1886 - (NSString *)pathForResource:(NSString *)_name ofType:(NSString *)_ext
1887 inDirectory:(NSString *)_directory
1888 languages:(NSArray *)_languages
1891 NSString *path = nil;
1893 id (*objAtIdx)(id,SEL,int);
1895 if (debugLanguageLookup) {
1896 NSLog(@"LOOKUP(%s): %@ | %@ | %@ | %@", __PRETTY_FUNCTION__,
1897 _name, _ext, _directory, [_languages componentsJoinedByString:@","]);
1900 path = [self bundlePath];
1901 if ([_directory isNotNull]) {
1902 // TODO: should we change that?
1903 path = [path stringByAppendingPathComponent:_directory];
1906 #if (NeXT_Foundation_LIBRARY || APPLE_Foundation_LIBRARY)
1907 path = [path stringByAppendingPathComponent:@"Contents"];
1909 path = [path stringByAppendingPathComponent:@"Resources"];
1912 if (debugLanguageLookup) NSLog(@" BASE: %@", path);
1914 fm = [NSFileManager defaultManager];
1915 if (![fm fileExistsAtPath:path])
1918 if (_ext != nil) _name = [_name stringByAppendingPathExtension:_ext];
1920 langCount = [_languages count];
1921 objAtIdx = (langCount > 0)
1922 ? (void*)[_languages methodForSelector:@selector(objectAtIndex:)]
1925 for (i = 0; i < langCount; i++) {
1930 ? objAtIdx(_languages, @selector(objectAtIndex:), i)
1931 : [_languages objectAtIndex:i];
1933 language = [language stringByAppendingPathExtension:@"lproj"];
1934 lpath = [path stringByAppendingPathComponent:language];
1935 lpath = [lpath stringByAppendingPathComponent:_name];
1937 if ([fm fileExistsAtPath:lpath])
1941 if (debugLanguageLookup)
1942 NSLog(@" no language matched, check base: %@", path);
1944 /* now look into x.bundle/Resources/name.type */
1945 if ([fm fileExistsAtPath:[path stringByAppendingPathComponent:_name]])
1946 return [path stringByAppendingPathComponent:_name];
1951 - (NSString *)pathForResource:(NSString *)_name ofType:(NSString *)_ext
1952 languages:(NSArray *)_languages
1956 path = [self pathForResource:_name ofType:_ext
1957 inDirectory:@"Resources"
1958 languages:_languages];
1959 if (path) return path;
1961 path = [self pathForResource:_name ofType:_ext
1963 languages:_languages];
1967 @end /* NSBundle(NGLanguageResourceExtensions) */
1969 @implementation NGBundle
1972 return [self allocWithZone:NULL];
1974 + (id)allocWithZone:(NSZone*)zone {
1975 return NSAllocateObject(self, 0, zone);
1978 - (id)initWithPath:(NSString *)__path {
1979 return [super initWithPath:__path];
1984 - (BOOL)_loadForBundleManager:(NGBundleManager *)_manager {
1985 return [super load];
1989 NGBundleManager *bm;
1991 bm = [NGBundleManager defaultBundleManager];
1993 return [bm loadBundle:self] ? YES : NO;
1996 + (NSBundle *)bundleForClass:(Class)aClass {
1997 return [[NGBundleManager defaultBundleManager] bundleForClass:aClass];
1999 + (NSBundle *)bundleWithPath:(NSString*)path {
2000 return [[NGBundleManager defaultBundleManager] bundleWithPath:path];
2003 #if GNUSTEP_BASE_LIBRARY
2005 - (Class)principalClass {
2009 if ((c = [super principalClass]))
2012 if ((cname = [[self infoDictionary] objectForKey:@"NSPrincipalClass"]) ==nil)
2015 if ((c = NSClassFromString(cname)))
2018 NSLog(@"%s: did not find principal class named '%@' of bundle %@",
2019 __PRETTY_FUNCTION__, cname, self);
2025 - (NSString *)description {
2029 "<%s %p fullPath: %s infoDictionary: %p loaded=%s>",
2030 (char*)object_get_class_name(self),
2032 [[self bundlePath] cString],
2033 [self infoDictionary],
2034 self->_codeLoaded ? "yes" : "no");
2036 return [NSString stringWithCString:buffer];