2 Copyright (C) 2000-2004 SKYRIX Software AG
4 This file is part of OpenGroupware.org.
6 OGo is free software; you can redistribute it and/or modify it under
7 the terms of the GNU Lesser General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
11 OGo is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14 License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with OGo; see the file COPYING. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
22 #include "NGBundleManager.h"
24 #include <NGExtensions/NSObject+Logs.h>
25 #import <Foundation/NSFileManager.h>
26 #import <EOControl/EOQualifier.h>
29 #if 0 && GNUSTEP_BASE_LIBRARY /* supported in repository since 19990916-2254-MET */
30 /* hack until GNUstep provides the necessary callbacks */
31 # define NSNonRetainedObjectMapValueCallBacks NSObjectMapValueCallBacks
34 #if NeXT_Foundation_LIBRARY || COCOA_Foundation_LIBRARY
35 # include <NGExtensions/NGPropertyListParser.h>
38 #if LIB_FOUNDATION_LIBRARY
39 @interface NSBundle(UsedPrivates)
40 + (BOOL)isFlattenedDirLayout;
44 #if NeXT_RUNTIME || APPLE_RUNTIME
46 #include <objc/objc-runtime.h>
48 //OBJC_EXPORT void objc_setClassHandler(int (*)(const char *));
50 static BOOL debugClassHook = NO;
52 static int _getClassHook(const char *className) {
53 if (className == NULL) return 0;
56 printf("lookup class '%s'.\n", className);
58 if (objc_lookUpClass(className))
62 static NGBundleManager *manager = nil;
66 NSLog(@"%s: look for class %s", __PRETTY_FUNCTION__, className);
68 manager = [NGBundleManager defaultBundleManager];
70 bundle = [manager bundleForClassNamed:
71 [NSString stringWithCString:className]];
74 NSLog(@"%s: found bundle %@", __PRETTY_FUNCTION__,
78 if (![manager loadBundle:bundle]) {
80 "bundleManager couldn't load bundle for class '%s'.\n",
85 Class c = objc_lookUpClass(className);
86 NSLog(@"%s: loaded bundle %@ for className %s class %@",
88 bundle, className, c);
100 #include <objc/objc-api.h>
102 static Class (*oldClassLoadHook)(const char *_name) = NULL;
104 static inline BOOL _isValidClassName(const char *_name) {
107 if (_name == NULL) return NO;
109 for (len = 0; (len < 256) && (*_name != '\0'); len++, _name++) {
111 if (!isalnum((int)*_name))
115 return (len == 256) ? NO : YES;
118 static Class _classLoadHook(const char *_name) {
119 if (_isValidClassName(_name)) {
120 static NGBundleManager *manager = nil;
123 //NSLog(@"%s: look for class %s", __PRETTY_FUNCTION__, _name);
125 manager = [NGBundleManager defaultBundleManager];
127 bundle = [manager bundleForClassNamed:[NSString stringWithCString:_name]];
130 NSLog(@"%s: found bundle %@", __PRETTY_FUNCTION__, [bundle bundlePath]);
133 if ([manager loadBundle:bundle]) {
137 hook = _objc_lookup_class;
138 _objc_lookup_class = NULL;
139 clazz = objc_lookup_class(_name);
140 _objc_lookup_class = hook;
142 if (clazz) return clazz;
146 return (oldClassLoadHook != NULL) ? oldClassLoadHook(_name) : Nil;
148 #endif // GNU_RUNTIME
150 NSString *NGBundleWasLoadedNotificationName = @"NGBundleWasLoadedNotification";
152 @interface NSBundle(NGBundleManagerPrivate)
153 - (BOOL)_loadForBundleManager:(NGBundleManager *)_manager;
156 @interface NGBundleManager(PrivateMethods)
158 - (void)registerBundle:(NSBundle *)_bundle
159 classes:(NSArray *)_classes
160 categories:(NSArray *)_categories;
162 - (NSString *)pathForBundleProvidingResource:(NSString *)_resourceName
163 ofType:(NSString *)_type
164 resourceSelector:(NGBundleResourceSelector)_selector
165 context:(void *)_ctx;
167 - (NSString *)makeBundleInfoPath:(NSString *)_path;
171 static BOOL _selectClassByVersion(NSString *_resourceName,
172 NSString *_resourceType,
174 NSDictionary *_resourceConfig,
175 NGBundleManager *_bundleManager,
181 if (![_resourceType isEqualToString:@"classes"])
184 if (_version == NULL)
186 if ([(id)_version intValue] == -1)
189 if ((tmp = [_resourceConfig objectForKey:@"version"])) {
190 classVersion = [tmp intValue];
192 if (classVersion < [(id)_version intValue]) {
193 NSLog(@"WARNING: class version mismatch for class %@: "
194 @"requested at least version %i, got version %i",
195 _resourceName, [(id)_version intValue], classVersion);
198 if ((tmp = [_resourceConfig objectForKey:@"exact-version"])) {
199 classVersion = [tmp intValue];
201 if (classVersion != [(id)_version intValue]) {
202 NSLog(@"WARNING: class version mismatch for class %@: "
203 @"requested exact version %i, got version %i",
204 _resourceName, [(id)_version intValue], classVersion);
210 @implementation NGBundleManager
213 static NGBundleManager *defaultManager = nil;
214 static BOOL debugOn = NO;
216 #if defined(__MINGW32__)
217 static NSString *NGEnvVarPathSeparator = @";";
219 static NSString *NGEnvVarPathSeparator = @":";
223 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
225 debugOn = [ud boolForKey:@"NGBundleManagerDebugEnabled"];
230 if (_objc_lookup_class != _classLoadHook) {
231 oldClassLoadHook = _objc_lookup_class;
232 _objc_lookup_class = _classLoadHook;
237 + (id)defaultBundleManager {
238 if (defaultManager == nil) {
239 defaultManager = [[NGBundleManager alloc] init];
241 #if NeXT_RUNTIME || APPLE_RUNTIME
243 static BOOL didRegisterCallback = NO;
245 if (!didRegisterCallback) {
246 didRegisterCallback = YES;
247 objc_setClassHandler(_getClassHook);
253 if (_objc_lookup_class != _classLoadHook) {
254 oldClassLoadHook = _objc_lookup_class;
255 _objc_lookup_class = _classLoadHook;
259 return defaultManager;
262 /* setup bundle search path */
264 - (void)_addMainBundlePathToPathArray:(NSMutableArray *)_paths {
268 pi = [NSProcessInfo processInfo];
269 path = [[pi arguments] objectAtIndex:0];
270 path = [path stringByDeletingLastPathComponent];
272 if ([path isEqual:@""])
276 // TODO: to be correct this would need to read the bundle-info
279 The path is the complete path to the executable, including the
280 processor, the OS and the library combo. Strip these directories
281 from the main bundle's path.
283 #if LIB_FOUNDATION_LIBRARY
284 if (![NSBundle isFlattenedDirLayout])
286 path = [[[path stringByDeletingLastPathComponent]
287 stringByDeletingLastPathComponent]
288 stringByDeletingLastPathComponent];
291 [_paths addObject:path];
294 - (void)_addBundlePathDefaultToPathArray:(NSMutableArray *)_paths {
298 if ((ud = [NSUserDefaults standardUserDefaults]) == nil) {
299 // got this with gstep-base during the port, apparently it happens
300 // if the bundle manager is created inside the setup process of
301 // gstep-base (for whatever reason)
302 NSLog(@"ERROR(NGBundleManager): got no system userdefaults object!");
308 if ((paths = [ud arrayForKey:@"NGBundlePath"]) == nil) {
309 if ((paths = [ud stringForKey:@"NGBundlePath"]) != nil)
310 paths = [paths componentsSeparatedByString:NGEnvVarPathSeparator];
313 [_paths addObjectsFromArray:paths];
315 NSLog(@"Note: NGBundlePath default is not configured.");
318 - (void)_addEnvironmentPathToPathArray:(NSMutableArray *)_paths {
322 pi = [NSProcessInfo processInfo];
323 paths = [[pi environment] objectForKey:@"NGBundlePath"];
325 paths = [paths componentsSeparatedByString:NGEnvVarPathSeparator];
326 if (paths) [_paths addObjectsFromArray:paths];
329 - (void)_addGNUstepPathsToPathArray:(NSMutableArray *)_paths {
332 // TODO: whats that supposed to do?
333 // TODO: replace with proper path locator function!
339 env = [[NSProcessInfo processInfo] environment];
341 if ((tmp = [env objectForKey:@"GNUSTEP_PATHPREFIX_LIST"]) == nil)
342 tmp = [env objectForKey:@"GNUSTEP_PATHLIST"];
343 tmp = [tmp componentsSeparatedByString:@":"];
345 for (i = 0, count = [tmp count]; i < count; i++) {
346 p = [tmp objectAtIndex:i];
347 p = [p stringByAppendingPathComponent:@"Library"];
348 p = [p stringByAppendingPathComponent:@"Bundles"];
349 if ([self->bundleSearchPaths containsObject:p]) continue;
351 if (p) [self->bundleSearchPaths addObject:p];
356 - (void)_setupBundleSearchPathes {
359 pi = [NSProcessInfo processInfo];
361 /* setup bundle search path */
363 self->bundleSearchPaths = [[NSMutableArray alloc] initWithCapacity:16];
365 [self _addMainBundlePathToPathArray:self->bundleSearchPaths];
366 [self _addBundlePathDefaultToPathArray:self->bundleSearchPaths];
367 [self _addEnvironmentPathToPathArray:self->bundleSearchPaths];
368 [self _addGNUstepPathsToPathArray:self->bundleSearchPaths];
370 #if DEBUG && NeXT_Foundation_LIBRARY && 0
371 NSLog(@"%s: bundle search pathes:\n%@", __PRETTY_FUNCTION__,
372 self->bundleSearchPaths);
376 - (void)_registerLoadedBundles {
377 NSEnumerator *currentBundles;
378 NSBundle *loadedBundle;
380 currentBundles = [[NSBundle allBundles] objectEnumerator];
381 while ((loadedBundle = [currentBundles nextObject]))
382 [self registerBundle:loadedBundle classes:nil categories:nil];
385 - (void)_registerForBundleLoadNotification {
386 [[NSNotificationCenter defaultCenter]
388 selector:@selector(_bundleDidLoadNotifcation:)
389 name:@"NSBundleDidLoadNotification"
394 #if GNUSTEP_BASE_LIBRARY
395 if ([NSUserDefaults standardUserDefaults] == nil) {
396 /* called inside setup process, deny creation (HACK) */
402 if ((self = [super init])) {
403 self->classToBundle =
404 NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks,
405 NSNonRetainedObjectMapValueCallBacks,
407 self->classNameToBundle =
408 NSCreateMapTable(NSObjectMapKeyCallBacks,
409 NSNonRetainedObjectMapValueCallBacks,
411 self->categoryNameToBundle =
412 NSCreateMapTable(NSObjectMapKeyCallBacks,
413 NSNonRetainedObjectMapValueCallBacks,
416 NSCreateMapTable(NSObjectMapKeyCallBacks,
417 NSNonRetainedObjectMapValueCallBacks,
419 self->pathToBundleInfo =
420 NSCreateMapTable(NSObjectMapKeyCallBacks,
421 NSObjectMapValueCallBacks,
424 NSCreateMapTable(NSObjectMapKeyCallBacks,
425 NSNonRetainedObjectMapValueCallBacks,
427 self->loadedBundles =
428 NSCreateMapTable(NSNonRetainedObjectMapKeyCallBacks,
429 NSObjectMapValueCallBacks,
432 [self _setupBundleSearchPathes];
433 [self _registerLoadedBundles];
434 [self _registerForBundleLoadNotification];
440 [self->loadingBundles release];
441 if (self->loadedBundles) NSFreeMapTable(self->loadedBundles);
442 if (self->classToBundle) NSFreeMapTable(self->classToBundle);
443 if (self->classNameToBundle) NSFreeMapTable(self->classNameToBundle);
444 if (self->categoryNameToBundle) NSFreeMapTable(self->categoryNameToBundle);
445 if (self->pathToBundle) NSFreeMapTable(self->pathToBundle);
446 if (self->pathToBundleInfo) NSFreeMapTable(self->pathToBundleInfo);
447 if (self->nameToBundle) NSFreeMapTable(self->nameToBundle);
448 [self->bundleSearchPaths release];
454 - (void)setBundleSearchPaths:(NSArray *)_paths {
455 ASSIGNCOPY(self->bundleSearchPaths, _paths);
457 - (NSArray *)bundleSearchPaths {
458 return self->bundleSearchPaths;
461 /* registering bundles */
463 - (void)registerBundle:(NSBundle *)_bundle
464 classes:(NSArray *)_classes
465 categories:(NSArray *)_categories
470 //NSLog(@"NGBundleManager: register loaded bundle %@", [_bundle bundlePath]);
472 e = [_classes objectEnumerator];
473 while ((v = [e nextObject])) {
474 NSMapInsert(self->classToBundle, NSClassFromString(v), _bundle);
475 NSMapInsert(self->classNameToBundle, v, _bundle);
478 e = [_categories objectEnumerator];
479 while ((v = [e nextObject]))
480 NSMapInsert(self->categoryNameToBundle, v, _bundle);
485 - (NSString *)pathForBundleWithName:(NSString *)_name type:(NSString *)_type {
486 NSFileManager *fm = [NSFileManager defaultManager];
489 NSString *bundlePath;
492 /* first check in table */
495 bundlePath = [_name stringByAppendingPathExtension:_type];
497 if ((bundle = NSMapGet(self->nameToBundle, bundlePath)))
498 return [bundle bundlePath];
500 e = [self->bundleSearchPaths objectEnumerator];
501 while ((path = [e nextObject])) {
504 if ([fm fileExistsAtPath:path isDirectory:&isDir]) {
505 if (!isDir) continue;
507 if ([[path lastPathComponent] isEqualToString:bundlePath]) {
508 // direct match (a bundle was specified in the path)
514 tmp = [path stringByAppendingPathComponent:bundlePath];
515 if ([fm fileExistsAtPath:tmp isDirectory:&isDir]) {
526 /* getting bundles */
528 - (NSBundle *)bundleForClass:(Class)aClass {
529 /* this method never loads a dynamic bundle (since the class is set up) */
532 bundle = NSMapGet(self->classToBundle, aClass);
534 #if NeXT_Foundation_LIBRARY || COCOA_Foundation_LIBRARY
536 bundle = [NSBundle bundleForClass:aClass];
537 if (bundle == [NSBundle mainBundle])
543 if the class wasn't loaded from a bundle, it's *either* the main bundle
544 or a bundle loaded before NGExtension was loaded !!!
547 #if !LIB_FOUNDATION_LIBRARY && !GNUSTEP_BASE_LIBRARY
548 // Note: incorrect behaviour if NGExtensions is dynamically loaded !
549 // TODO: can we do anything about this? Can we detect the situation and
550 // print a log instead of the compile warning?
551 // Note: the above refers to the situation when a framework is implicitly
552 // loaded by loading a bundle (the framework is not linked against
555 bundle = [NSBundle mainBundle];
556 NSMapInsert(self->classToBundle, aClass, bundle);
557 NSMapInsert(self->classNameToBundle, NSStringFromClass(aClass), bundle);
561 - (NSBundle *)bundleWithPath:(NSString *)path {
562 NSBundle *bundle = nil;
565 path = [path stringByResolvingSymlinksInPath];
569 if (debugOn) NSLog(@"find bundle for path: '%@'", path);
570 bundle = NSMapGet(self->pathToBundle, path);
573 if (debugOn) NSLog(@" found: %@", bundle);
577 if ((bundle = [(NGBundle *)[NGBundle alloc] initWithPath:path]) == nil) {
578 NSLog(@"ERROR(%s): could not create bundle for path: '%@'",
579 __PRETTY_FUNCTION__, path);
583 bn = [[bundle bundleName]
584 stringByAppendingPathExtension:[bundle bundleType]],
586 NSMapInsert(self->pathToBundle, path, bundle);
587 NSMapInsert(self->nameToBundle, bn, bundle);
591 - (NSBundle *)bundleWithName:(NSString *)_name type:(NSString *)_type {
595 bn = [_name stringByAppendingPathExtension:_type];
596 bundle = NSMapGet(self->nameToBundle, bn);
599 bundle = [self bundleWithPath:
600 [self pathForBundleWithName:_name type:_type]];
603 if (bundle != nil && ![[bundle bundleType] isEqualToString:_type])
608 - (NSBundle *)bundleWithName:(NSString *)_name {
609 return [self bundleWithName:_name type:@"bundle"];
612 - (NSBundle *)bundleForClassNamed:(NSString *)_className {
613 NSString *path = nil;
614 NSBundle *bundle = nil;
616 if (_className == nil)
619 /* first check in table */
621 if ((bundle = NSMapGet(self->classNameToBundle, _className)))
625 /* then look in runtime, reset load callback to avoid recursion */
632 loadCallback = _objc_lookup_class;
633 _objc_lookup_class = NULL;
634 clazz = NSClassFromString(_className);
635 _objc_lookup_class = loadCallback;
638 /* the class is already loaded */
639 bundle = [self bundleForClass:clazz];
640 NSMapInsert(self->classNameToBundle, _className, bundle);
646 path = [self pathForBundleProvidingResource:_className
648 resourceSelector:_selectClassByVersion
649 context:NULL /* version */];
651 path = [path stringByResolvingSymlinksInPath];
652 NSAssert(path, @"couldn't resolve symlinks in path ..");
658 if ((bundle = [self bundleWithPath:path]))
659 NSMapInsert(self->classNameToBundle, _className, bundle);
670 - (NSArray *)bundlesRequiredByBundle:(NSBundle *)_bundle {
671 [self doesNotRecognizeSelector:_cmd];
675 - (NSArray *)classesProvidedByBundle:(NSBundle *)_bundle {
676 [self doesNotRecognizeSelector:_cmd];
679 - (NSArray *)classesRequiredByBundle:(NSBundle *)_bundle {
680 [self doesNotRecognizeSelector:_cmd];
686 - (NSString *)makeBundleInfoPath:(NSString *)_path {
687 #if (NeXT_Foundation_LIBRARY || APPLE_Foundation_LIBRARY) && !defined(GSWARN)
688 return [[[_path stringByAppendingPathComponent:@"Contents"]
689 stringByAppendingPathComponent:@"Resources"]
690 stringByAppendingPathComponent:@"bundle-info.plist"];
692 return [_path stringByAppendingPathComponent:@"bundle-info.plist"];
696 - (id)_initializeLoadedBundle:(NSBundle *)_bundle
697 info:(NSDictionary *)_bundleInfo
701 /* check whether a handler was specified */
703 if ((handler = [_bundleInfo objectForKey:@"bundleHandler"]) != nil) {
704 [self debugWithFormat:@"lookup bundle handler %@ of bundle: %@",
707 if ((handler = NSClassFromString(handler)) == nil) {
708 NSLog(@"ERROR: did not find handler class %@ of bundle %@.",
709 [_bundleInfo objectForKey:@"bundleHandler"], [_bundle bundlePath]);
710 handler = [_bundle principalClass];
713 handler = [handler alloc];
715 if ([handler respondsToSelector:@selector(initForBundle:bundleManager:)])
716 handler = [handler initForBundle:_bundle bundleManager:self];
718 handler = [handler init];
719 handler = [handler autorelease];
721 if (handler == nil) {
722 NSLog(@"ERROR: could not instantiate handler class %@ of bundle %@.",
723 [_bundleInfo objectForKey:@"bundleHandler"], [_bundle bundlePath]);
724 handler = [_bundle principalClass];
728 [self debugWithFormat:
729 @"no bundle handler, lookup principal class of bundle: %@",
731 if ((handler = [_bundle principalClass]) == nil)
732 /* use NGBundle class as default bundle handler */
733 handler = [NGBundle class];
741 - (NSDictionary *)_loadBundleInfoAtExistingPath:(NSString *)_path {
742 NSDictionary *bundleInfo;
745 #if NeXT_Foundation_LIBRARY || COCOA_Foundation_LIBRARY
746 bundleInfo = NGParsePropertyListFromFile(_path);
748 bundleInfo = [NSDictionary dictionaryWithContentsOfFile:_path];
750 if (bundleInfo == nil) {
751 NSLog(@"couldn't load bundle-info at path '%@' !", _path);
755 /* check required bundle manager version */
756 info = [bundleInfo objectForKey:@"requires"];
757 if ((info = [(NSDictionary *)info objectForKey:@"bundleManagerVersion"])) {
758 if ([info intValue] > [[self class] version]) {
759 /* bundle manager version does not match ... */
763 NSMapInsert(self->pathToBundleInfo, _path, bundleInfo);
767 - (NSBundle *)_locateBundleForClassInfo:(NSDictionary *)_classInfo {
771 if (_classInfo == nil)
773 if ((className = [_classInfo objectForKey:@"name"]) == nil) {
774 NSLog(@"ERROR: missing classname in bundle-info.plist class section !");
778 if ((bundle = [self bundleForClassNamed:className]) == nil) {
779 #if 0 // class might be already loaded
780 NSLog(@"ERROR: did not find class %@ required by bundle %@.",
781 className, [_bundle bundlePath]);
787 - (NSArray *)_locateBundlesForClassInfos:(NSEnumerator *)_classInfos {
788 NSMutableArray *requiredBundles;
791 requiredBundles = [NSMutableArray arrayWithCapacity:16];
792 while ((i = [_classInfos nextObject])) {
795 if ((bundle = [self _locateBundleForClassInfo:i]) == nil)
798 [requiredBundles addObject:bundle];
800 return requiredBundles;
803 - (BOOL)_preLoadBundle:(NSBundle *)_bundle info:(NSDictionary *)_bundleInfo {
804 /* TODO: split up this huge method */
805 NSDictionary *requires = nil;
806 NSMutableArray *requiredBundles = nil;
807 NSBundle *requiredBundle = nil;
809 requires = [_bundleInfo objectForKey:@"requires"];
812 /* invalid bundle info specified */
815 /* load required bundles */
820 /* locate required bundles */
822 e = [[requires objectForKey:@"bundles"] objectEnumerator];
823 while ((i = [e nextObject])) {
824 NSString *bundleName;
826 if (![i respondsToSelector:@selector(objectForKey:)]) {
827 NSLog(@"ERROR(%s): invalid bundle-info of bundle %@ !!!\n"
828 @" requires-entry is not a dictionary: %@",
829 __PRETTY_FUNCTION__, _bundle, i);
833 if ((bundleName = [i objectForKey:@"name"])) {
836 type = [i objectForKey:@"type"];
837 if (type == nil) type = @"bundle";
839 if ((requiredBundle = [self bundleWithName:bundleName type:type])) {
840 if (requiredBundles == nil)
841 requiredBundles = [NSMutableArray arrayWithCapacity:16];
843 [requiredBundles addObject:requiredBundle];
846 NSLog(@"ERROR(NGBundleManager): did not find bundle '%@' (type=%@) "
847 @"required by bundle %@.",
848 bundleName, type, [_bundle bundlePath]);
853 NSLog(@"ERROR: error in bundle-info.plist of bundle %@", _bundle);
857 /* load located bundles */
861 e = [requiredBundles objectEnumerator];
862 while ((requiredBundle = [e nextObject])) {
865 if ((bundleMaster = [self loadBundle:requiredBundle]) == Nil) {
866 NSLog(@"ERROR: could not load bundle %@ (%@) required by bundle %@.",
867 [requiredBundle bundlePath], requiredBundle, [_bundle bundlePath]);
873 /* load required classes */
878 reqClasses = [requires objectForKey:@"classes"];
879 bundles = [self _locateBundlesForClassInfos:[reqClasses objectEnumerator]];
880 if (requiredBundles == nil)
881 requiredBundles = [NSMutableArray arrayWithCapacity:16];
882 [requiredBundles addObjectsFromArray:bundles];
885 /* load located bundles */
889 e = [requiredBundles objectEnumerator];
890 while ((requiredBundle = [e nextObject])) {
893 if ((bundleMaster = [self loadBundle:requiredBundle]) == Nil) {
894 NSLog(@"ERROR: could not load bundle %@ (%@) required by bundle %@.",
895 [requiredBundle bundlePath], requiredBundle, [_bundle bundlePath]);
901 /* check whether versions of classes match */
906 e = [[requires objectForKey:@"classes"] objectEnumerator];
907 while ((i = [e nextObject])) {
911 if ((className = [i objectForKey:@"name"]) == nil)
914 if ((clazz = NSClassFromString(className)) == Nil)
917 if ([i objectForKey:@"exact-version"]) {
920 v = [[i objectForKey:@"exact-version"] intValue];
922 if (v != [clazz version]) {
923 NSLog(@"ERROR: required exact class match failed:\n"
925 @" required version: %i\n"
926 @" loaded version: %i\n"
930 [_bundle bundlePath]);
933 else if ([i objectForKey:@"version"]) {
936 v = [[i objectForKey:@"version"] intValue];
938 if (v > [clazz version]) {
939 NSLog(@"ERROR: provided class does not match required version:\n"
941 @" least required version: %i\n"
942 @" loaded version: %i\n"
946 [_bundle bundlePath]);
954 - (BOOL)_postLoadBundle:(NSBundle *)_bundle info:(NSDictionary *)_bundleInfo {
958 - (id)loadBundle:(NSBundle *)_bundle {
959 NSString *path = nil;
960 NSDictionary *bundleInfo = nil;
961 id bundleManager = nil;
964 NSAssert(self->loadedBundles, @"missing loadedBundles hashmap ..");
967 if ((bundleManager = NSMapGet(self->loadedBundles, _bundle)))
968 return bundleManager;
970 if (_bundle == [NSBundle mainBundle])
971 return [NSBundle mainBundle];
973 if ([self->loadingBundles containsObject:_bundle])
977 if (self->loadingBundles == nil)
978 self->loadingBundles = [[NSMutableSet allocWithZone:[self zone]] init];
979 [self->loadingBundles addObject:_bundle];
981 path = [_bundle bundlePath];
982 path = [self makeBundleInfoPath:path];
984 if ((bundleInfo = NSMapGet(self->pathToBundleInfo, path)) == nil) {
985 if ([[NSFileManager defaultManager] fileExistsAtPath:path])
986 bundleInfo = [self _loadBundleInfoAtExistingPath:path];
989 if (![self _preLoadBundle:_bundle info:bundleInfo])
992 if (![_bundle _loadForBundleManager:self])
995 if (![self _postLoadBundle:_bundle info:bundleInfo])
999 [self _initializeLoadedBundle:_bundle info:bundleInfo])) {
1000 NSMapInsert(self->loadedBundles, _bundle, bundleManager);
1002 if ([bundleManager respondsToSelector:
1003 @selector(bundleManager:didLoadBundle:)])
1004 [bundleManager bundleManager:self didLoadBundle:_bundle];
1008 NSLog(@"ERROR(%s): couldn't initialize loaded bundle '%@'",
1009 __PRETTY_FUNCTION__, [_bundle bundlePath]);
1013 [self->loadingBundles removeObject:_bundle];
1015 if (bundleManager) {
1016 if (bundleInfo == nil)
1017 bundleInfo = [NSDictionary dictionary];
1019 [[NSNotificationCenter defaultCenter]
1020 postNotificationName:
1021 NGBundleWasLoadedNotificationName
1023 userInfo:[NSDictionary dictionaryWithObjectsAndKeys:
1024 self, @"NGBundleManager",
1025 bundleManager, @"NGBundleHandler",
1026 bundleInfo, @"NGBundleInfo",
1029 return bundleManager;
1034 - (id)principalObjectOfBundle:(NSBundle *)_bundle {
1035 return (id)NSMapGet(self->loadedBundles, _bundle);
1040 static BOOL _doesInfoMatch(NSArray *keys, NSDictionary *dict, NSDictionary *info)
1044 for (i = 0, count = [keys count]; i < count; i++) {
1048 key = [keys objectAtIndex:i];
1049 vv = [info objectForKey:key];
1052 /* info has no matching key */
1056 kv = [dict objectForKey:key];
1057 if (![kv isEqual:vv])
1063 - (NSDictionary *)configForResource:(id)_resource ofType:(NSString *)_type
1064 providedByBundle:(NSBundle *)_bundle
1066 NSDictionary *bundleInfo = nil;
1068 NSEnumerator *providedResources;
1069 NSArray *rnKeys = nil;
1073 if ([_resource respondsToSelector:@selector(objectForKey:)]) {
1074 rnKeys = [_resource allKeys];
1075 rnKeyCount = [rnKeys count];
1078 infoPath = [self makeBundleInfoPath:[_bundle bundlePath]];
1080 /* check whether info is in cache */
1081 if ((bundleInfo = NSMapGet(self->pathToBundleInfo, infoPath)) == nil) {
1082 if (![[NSFileManager defaultManager] fileExistsAtPath:infoPath])
1083 /* no bundle-info.plist available .. */
1087 bundleInfo = [self _loadBundleInfoAtExistingPath:infoPath];
1090 /* get provided resources config */
1093 [[(NSDictionary *)[bundleInfo objectForKey:@"provides"] objectForKey:_type]
1095 if (providedResources == nil) return nil;
1097 /* scan provided resources */
1099 while ((info = [providedResources nextObject])) {
1101 if (!_doesInfoMatch(rnKeys, _resource, info))
1107 name = [[(NSDictionary *)info objectForKey:@"name"] stringValue];
1108 if (name == nil) continue;
1109 if (![name isEqualToString:_resource]) continue;
1116 - (void)_processInfoForProvidedResources:(NSDictionary *)info
1117 ofType:(NSString *)_type path:(NSString *)path
1118 resourceName:(NSString *)_resourceName
1119 resourceSelector:(NGBundleResourceSelector)_selector
1120 context:(void *)_context
1121 andAddToResultArray:(NSMutableArray *)result
1123 NSEnumerator *providedResources = nil;
1124 if (info == nil) return;
1126 /* direct match (a bundle was specified in the path) */
1128 providedResources = [[(NSDictionary *)[info objectForKey:@"provides"]
1132 if (providedResources == nil) return;
1134 /* scan provide array */
1135 while ((info = [providedResources nextObject])) {
1138 if ((name = [[info objectForKey:@"name"] stringValue]) == nil)
1141 if (_resourceName) {
1142 if (![name isEqualToString:_resourceName])
1146 if (!_selector(_resourceName, _type, path, info, self, _context))
1150 [result addObject:path];
1154 - (NSArray *)pathsForBundlesProvidingResource:(NSString *)_resourceName
1155 ofType:(NSString *)_type
1156 resourceSelector:(NGBundleResourceSelector)_selector
1157 context:(void *)_context
1159 /* TODO: split up method */
1160 NSMutableArray *result = nil;
1166 NSLog(@"BM LOOKUP pathes (%d bundles loaded): %@ / %@",
1167 NSCountMapTable(self->loadedBundles), _resourceName, _type);
1170 fm = [NSFileManager defaultManager];
1171 result = [NSMutableArray arrayWithCapacity:64];
1173 // TODO: look in loaded bundles
1175 /* check physical pathes */
1177 e = [self->bundleSearchPaths objectEnumerator];
1178 while ((path = [e nextObject]) != nil) {
1181 NSString *tmp, *bundleDirPath;
1184 if (![fm fileExistsAtPath:path isDirectory:&isDir])
1187 if (!isDir) continue;
1189 /* check whether an appropriate bundle is contained in 'path' */
1191 dir = [[fm directoryContentsAtPath:path] objectEnumerator];
1192 while ((bundleDirPath = [dir nextObject]) != nil) {
1193 NSDictionary *bundleInfo = nil;
1194 NSEnumerator *providedResources = nil;
1198 bundleDirPath = [path stringByAppendingPathComponent:bundleDirPath];
1199 infoPath = [self makeBundleInfoPath:bundleDirPath];
1201 // TODO: can we use _doesBundleInfo:path:providedResource:... ?
1202 if ((bundleInfo = NSMapGet(self->pathToBundleInfo, infoPath))==nil) {
1203 if (![fm fileExistsAtPath:infoPath])
1206 bundleInfo = [self _loadBundleInfoAtExistingPath:infoPath];
1210 [[(NSDictionary *)[bundleInfo objectForKey:@"provides"]
1213 if (providedResources == nil) continue;
1215 /* scan 'provides' array */
1216 while ((info = [providedResources nextObject])) {
1219 name = [[(NSDictionary *)info objectForKey:@"name"] stringValue];
1220 if (name == nil) continue;
1222 if (_resourceName != nil) {
1223 if (![name isEqualToString:_resourceName])
1226 if (_selector != NULL) {
1227 if (!_selector(name, _type, bundleDirPath, info, self, _context))
1231 [result addObject:bundleDirPath];
1236 /* check for direct match (NGBundlePath element is a bundle) */
1238 tmp = [self makeBundleInfoPath:path];
1240 if ((info = NSMapGet(self->pathToBundleInfo, tmp)) == nil) {
1241 if ([fm fileExistsAtPath:tmp])
1242 info = [self _loadBundleInfoAtExistingPath:tmp];
1245 [self _processInfoForProvidedResources:info ofType:_type path:path
1246 resourceName:_resourceName resourceSelector:_selector
1248 andAddToResultArray:result];
1251 if ([result count] == 0) {
1252 [self logWithFormat:
1253 @"Note(%s): method does not search in loaded bundles for "
1254 @"resources of type '%@'",
1255 __PRETTY_FUNCTION__, _type];
1258 return [[result copy] autorelease];
1261 - (BOOL)_doesBundleInfo:(NSDictionary *)_bundleInfo path:(NSString *)_path
1262 provideResource:(id)_resourceName ofType:(NSString *)_type
1263 rnKeys:(NSArray *)_rnKeys
1264 resourceSelector:(NGBundleResourceSelector)_selector context:(void *)_context
1266 NSEnumerator *providedResources;
1270 [[(NSDictionary *)[_bundleInfo objectForKey:@"provides"]
1271 objectForKey:_type] objectEnumerator];
1272 if (providedResources == nil) return NO;
1274 /* scan provide array */
1275 while ((info = [providedResources nextObject])) {
1276 if (_rnKeys != nil) {
1277 if (!_doesInfoMatch(_rnKeys, _resourceName, info))
1283 name = [[(NSDictionary *)info objectForKey:@"name"] stringValue];
1284 if (name == nil) continue;
1285 if (![name isEqualToString:_resourceName]) continue;
1288 if (_selector != NULL) {
1289 if (!_selector(_resourceName, _type, _path, info, self, _context))
1293 /* all conditions applied (found) */
1299 - (NSString *)pathOfLoadedBundleProvidingResource:(id)_resourceName
1300 ofType:(NSString *)_type
1301 resourceSelector:(NGBundleResourceSelector)_selector context:(void *)_context
1303 NSMapEnumerator menum;
1305 NSDictionary *bundleInfo;
1308 rnKeys = ([_resourceName respondsToSelector:@selector(objectForKey:)])
1309 ? [_resourceName allKeys]
1312 menum = NSEnumerateMapTable(self->pathToBundleInfo);
1313 while (NSNextMapEnumeratorPair(&menum, (void *)&path, (void *)&bundleInfo)) {
1315 NSLog(@"check loaded bundle for resource %@: %@", _resourceName,
1319 if ([self _doesBundleInfo:bundleInfo path:path
1320 provideResource:_resourceName ofType:_type rnKeys:rnKeys
1321 resourceSelector:_selector context:_context])
1322 /* strip bundle-info.plist name */
1323 return [path stringByDeletingLastPathComponent];
1329 - (NSString *)pathForBundleProvidingResource:(id)_resourceName
1330 ofType:(NSString *)_type
1331 resourceSelector:(NGBundleResourceSelector)_selector
1332 context:(void *)_context
1334 /* main path lookup method */
1335 // TODO: this method seriously needs some refactoring
1339 NSArray *rnKeys = nil;
1343 NSLog(@"BM LOOKUP path (%d bundles loaded): %@ / %@",
1344 NSCountMapTable(self->loadedBundles), _resourceName, _type);
1347 /* look in loaded bundles */
1349 path = [self pathOfLoadedBundleProvidingResource:_resourceName ofType:_type
1350 resourceSelector:_selector context:_context];
1351 if (path != nil) return path;
1353 /* look in filesystem */
1355 if ([_resourceName respondsToSelector:@selector(objectForKey:)]) {
1356 rnKeys = [_resourceName allKeys];
1357 rnKeyCount = [rnKeys count];
1360 fm = [NSFileManager defaultManager];
1361 e = [self->bundleSearchPaths objectEnumerator];
1362 while ((path = [e nextObject]) != nil) {
1368 if (![fm fileExistsAtPath:path isDirectory:&isDir])
1371 if (!isDir) continue;
1373 /* check whether an appropriate bundle is contained in 'path' */
1375 dir = [[fm directoryContentsAtPath:path] objectEnumerator];
1376 while ((tmp = [dir nextObject])) {
1377 NSDictionary *bundleInfo = nil;
1380 tmp = [path stringByAppendingPathComponent:tmp];
1381 infoPath = [self makeBundleInfoPath:tmp];
1384 NSLog(@"check path path=%@ info=%@", tmp, infoPath);
1386 if ((bundleInfo = NSMapGet(self->pathToBundleInfo, infoPath)) == nil) {
1387 if (![fm fileExistsAtPath:infoPath])
1390 bundleInfo = [self _loadBundleInfoAtExistingPath:infoPath];
1393 NSLog(@"found info for path=%@ info=%@: %@", tmp,infoPath,bundleInfo);
1395 if ([self _doesBundleInfo:bundleInfo path:tmp
1396 provideResource:_resourceName ofType:_type rnKeys:rnKeys
1397 resourceSelector:_selector context:_context])
1401 /* check for direct match */
1403 tmp = [self makeBundleInfoPath:path];
1405 if ((info = NSMapGet(self->pathToBundleInfo, tmp)) == nil) {
1406 if ([fm fileExistsAtPath:tmp])
1407 info = [self _loadBundleInfoAtExistingPath:tmp];
1409 NSLog(@"WARNING(%s): did not find direct path '%@'",
1410 __PRETTY_FUNCTION__, tmp);
1415 // direct match (a bundle was specified in the path)
1416 NSEnumerator *providedResources;
1417 NSDictionary *provides;
1419 provides = [(NSDictionary *)info objectForKey:@"provides"];
1420 providedResources = [[provides objectForKey:_type] objectEnumerator];
1422 if (providedResources == nil) continue;
1424 // scan provide array
1425 while ((info = [providedResources nextObject])) {
1427 if (!_doesInfoMatch(rnKeys, _resourceName, info))
1433 name = [[(NSDictionary *)info objectForKey:@"name"] stringValue];
1434 if (name == nil) continue;
1435 if (![name isEqualToString:_resourceName]) continue;
1439 if (!_selector(_resourceName, _type, tmp, info, self, _context))
1442 /* all conditions applied */
1450 - (NSBundle *)bundleProvidingResource:(id)_name ofType:(NSString *)_type {
1453 if (debugOn) NSLog(@"BM LOOKUP: %@ / %@", _name, _type);
1455 bp = [self pathForBundleProvidingResource:_name
1457 resourceSelector:NULL context:nil];
1458 if ([bp length] == 0) {
1459 #if (NeXT_Foundation_LIBRARY || APPLE_Foundation_LIBRARY) && HEAVY_DEBUG
1460 NSLog(@"%s: found no resource '%@' of type '%@' ...",
1461 __PRETTY_FUNCTION__, _resourceName, _resourceType);
1463 if (debugOn) NSLog(@" did not find: %@ / %@", _name, _type);
1467 if (debugOn) NSLog(@" FOUND: %@", bp);
1468 return [self bundleWithPath:bp];
1471 - (NSArray *)bundlesProvidingResource:(id)_resourceName
1472 ofType:(NSString *)_type
1475 NSMutableArray *bundles;
1478 paths = [self pathsForBundlesProvidingResource:_resourceName
1480 resourceSelector:NULL context:nil];
1482 count = [paths count];
1483 if (paths == nil) return nil;
1484 if (count == 0) return paths;
1486 bundles = [NSMutableArray arrayWithCapacity:count];
1487 for (i = 0; i < count; i++) {
1490 if ((bundle = [self bundleWithPath:[paths objectAtIndex:i]]))
1491 [bundles addObject:bundle];
1493 return [[bundles copy] autorelease];
1496 - (NSArray *)providedResourcesOfType:(NSString *)_resourceType
1497 inBundle:(NSBundle *)_bundle
1500 NSDictionary *bundleInfo;
1502 path = [self makeBundleInfoPath:[_bundle bundlePath]];
1503 if (path == nil) return nil;
1505 /* retrieve bundle info dictionary */
1506 if ((bundleInfo = NSMapGet(self->pathToBundleInfo, path)) == nil)
1507 bundleInfo = [self _loadBundleInfoAtExistingPath:path];
1509 return [(NSDictionary *)[bundleInfo objectForKey:@"provides"]
1510 objectForKey:_resourceType];
1513 - (void)_addRegisteredProvidedResourcesOfType:(NSString *)_type
1514 toSet:(NSMutableSet *)_result
1516 NSMapEnumerator menum;
1518 NSDictionary *bundleInfo;
1520 menum = NSEnumerateMapTable(self->pathToBundleInfo);
1521 while (NSNextMapEnumeratorPair(&menum, (void *)&path, (void *)&bundleInfo)) {
1522 NSArray *providedResources;
1525 NSLog(@"check loaded bundle for resource types %@: %@", _type, path);
1528 [(NSDictionary *)[bundleInfo objectForKey:@"provides"]
1529 objectForKey:_type];
1530 if (providedResources == nil) continue;
1532 [_result addObjectsFromArray:providedResources];
1536 - (NSArray *)providedResourcesOfType:(NSString *)_resourceType {
1537 NSMutableSet *result = nil;
1538 NSFileManager *fm = [NSFileManager defaultManager];
1542 result = [NSMutableSet setWithCapacity:128];
1544 /* scan loaded bundles */
1546 [self _addRegisteredProvidedResourcesOfType:_resourceType toSet:result];
1548 /* scan all bundle search paths */
1550 e = [self->bundleSearchPaths objectEnumerator];
1551 while ((path = [e nextObject])) {
1557 if (![fm fileExistsAtPath:path isDirectory:&isDir])
1559 if (!isDir) continue;
1561 /* check whether an appropriate bundle is contained in 'path' */
1563 dir = [[fm directoryContentsAtPath:path] objectEnumerator];
1564 while ((tmp = [dir nextObject])) {
1565 NSDictionary *bundleInfo = nil;
1566 NSArray *providedResources = nil;
1569 tmp = [path stringByAppendingPathComponent:tmp];
1570 infoPath = [self makeBundleInfoPath:tmp];
1573 NSLog(@" info path: %@", tmp);
1576 if ((bundleInfo = NSMapGet(self->pathToBundleInfo, infoPath)) == nil) {
1577 if (![fm fileExistsAtPath:infoPath])
1580 bundleInfo = [self _loadBundleInfoAtExistingPath:infoPath];
1584 [(NSDictionary *)[bundleInfo objectForKey:@"provides"]
1585 objectForKey:_resourceType];
1586 if (providedResources == nil) continue;
1588 [result addObjectsFromArray:providedResources];
1591 /* check for direct match */
1593 tmp = [self makeBundleInfoPath:path];
1595 if ((info = NSMapGet(self->pathToBundleInfo, tmp)) == nil) {
1596 if ([fm fileExistsAtPath:tmp])
1597 info = [self _loadBundleInfoAtExistingPath:tmp];
1601 // direct match (a bundle was specified in the path)
1602 NSArray *providedResources;
1603 NSDictionary *provides;
1605 provides = [(NSDictionary *)info objectForKey:@"provides"];
1606 providedResources = [provides objectForKey:_resourceType];
1608 if (providedResources == nil) continue;
1610 [result addObjectsFromArray:providedResources];
1613 return [result allObjects];
1616 - (NSBundle *)bundleProvidingResourceOfType:(NSString *)_resourceType
1617 matchingQualifier:(EOQualifier *)_qual
1619 NSFileManager *fm = [NSFileManager defaultManager];
1623 /* foreach search path entry */
1625 e = [self->bundleSearchPaths objectEnumerator];
1626 while ((path = [e nextObject])) {
1629 if ([fm fileExistsAtPath:path isDirectory:&isDir]) {
1632 if (!isDir) continue;
1634 /* check whether an appropriate bundle is contained in 'path' */
1638 dir = [[fm directoryContentsAtPath:path] objectEnumerator];
1639 while ((tmp = [dir nextObject])) {
1640 NSDictionary *bundleInfo;
1641 NSArray *providedResources;
1644 tmp = [path stringByAppendingPathComponent:tmp];
1645 infoPath = [self makeBundleInfoPath:tmp];
1647 if ((bundleInfo=NSMapGet(self->pathToBundleInfo, infoPath)) == nil) {
1648 if (![fm fileExistsAtPath:infoPath])
1651 bundleInfo = [self _loadBundleInfoAtExistingPath:infoPath];
1654 bundleInfo = [bundleInfo objectForKey:@"provides"];
1655 providedResources = [bundleInfo objectForKey:_resourceType];
1657 if (providedResources == nil) continue;
1660 [providedResources filteredArrayUsingQualifier:_qual];
1662 if ([providedResources count] > 0)
1663 return [self bundleWithPath:tmp];
1667 /* check for direct match */
1669 tmp = [self makeBundleInfoPath:path];
1671 if ((info = NSMapGet(self->pathToBundleInfo, tmp)) == nil) {
1672 if ([fm fileExistsAtPath:tmp])
1673 info = [self _loadBundleInfoAtExistingPath:tmp];
1677 // direct match (a bundle was specified in the path)
1678 NSArray *providedResources;
1679 NSDictionary *provides;
1681 provides = [(NSDictionary *)info objectForKey:@"provides"];
1682 providedResources = [provides objectForKey:_resourceType];
1684 if (providedResources == nil) continue;
1687 [providedResources filteredArrayUsingQualifier:_qual];
1689 if ([providedResources count] > 0)
1690 return [self bundleWithPath:path];
1697 - (NSBundle *)bundlesProvidingResourcesOfType:(NSString *)_resourceType
1698 matchingQualifier:(EOQualifier *)_qual
1700 NSMutableArray *bundles = nil;
1701 NSFileManager *fm = [NSFileManager defaultManager];
1705 bundles = [NSMutableArray arrayWithCapacity:128];
1707 /* foreach search path entry */
1709 e = [self->bundleSearchPaths objectEnumerator];
1710 while ((path = [e nextObject])) {
1713 if ([fm fileExistsAtPath:path isDirectory:&isDir]) {
1716 if (!isDir) continue;
1718 /* check whether an appropriate bundle is contained in 'path' */
1722 dir = [[fm directoryContentsAtPath:path] objectEnumerator];
1723 while ((tmp = [dir nextObject])) {
1724 NSDictionary *bundleInfo = nil;
1725 NSArray *providedResources = nil;
1728 tmp = [path stringByAppendingPathComponent:tmp];
1729 infoPath = [self makeBundleInfoPath:tmp];
1731 if ((bundleInfo=NSMapGet(self->pathToBundleInfo, infoPath)) == nil) {
1732 if (![fm fileExistsAtPath:infoPath])
1735 bundleInfo = [self _loadBundleInfoAtExistingPath:infoPath];
1738 bundleInfo = [bundleInfo objectForKey:@"provides"];
1739 providedResources = [bundleInfo objectForKey:_resourceType];
1741 if (providedResources == nil) continue;
1744 [providedResources filteredArrayUsingQualifier:_qual];
1746 if ([providedResources count] > 0)
1747 [bundles addObject:[self bundleWithPath:tmp]];
1751 /* check for direct match */
1753 tmp = [self makeBundleInfoPath:path];
1755 if ((info = NSMapGet(self->pathToBundleInfo, tmp)) == nil) {
1756 if ([fm fileExistsAtPath:tmp])
1757 info = [self _loadBundleInfoAtExistingPath:tmp];
1761 // direct match (a bundle was specified in the path)
1762 NSArray *providedResources;
1763 NSDictionary *provides;
1765 provides = [(NSDictionary *)info objectForKey:@"provides"];
1766 providedResources = [provides objectForKey:_resourceType];
1768 if (providedResources == nil) continue;
1771 [providedResources filteredArrayUsingQualifier:_qual];
1773 if ([providedResources count] > 0)
1774 [bundles addObject:[self bundleWithPath:path]];
1778 return [[bundles copy] autorelease];
1783 - (void)_bundleDidLoadNotifcation:(NSNotification *)_notification {
1784 NSDictionary *ui = [_notification userInfo];
1787 NSLog(@"bundle %@ did load with classes %@",
1788 [[_notification object] bundlePath],
1789 [ui objectForKey:@"NSLoadedClasses"]);
1792 [self registerBundle:[_notification object]
1793 classes:[ui objectForKey:@"NSLoadedClasses"]
1794 categories:[ui objectForKey:@"NSLoadedCategories"]];
1799 - (BOOL)isDebuggingEnabled {
1803 @end /* NGBundleManager */
1805 @implementation NSBundle(BundleManagerSupport)
1808 return [NGBundle alloc];
1810 + (id)allocWithZone:(NSZone *)zone {
1811 return [NGBundle allocWithZone:zone];
1814 #if !(NeXT_Foundation_LIBRARY || APPLE_Foundation_LIBRARY)
1815 //#warning remember, bundleForClass is not overridden !
1817 + (NSBundle *)bundleForClass:(Class)aClass {
1818 return [[NGBundleManager defaultBundleManager] bundleForClass:aClass];
1821 + (NSBundle *)bundleWithPath:(NSString*)path {
1822 return [[NGBundleManager defaultBundleManager] bundleWithPath:path];
1826 @end /* NSBundle(BundleManagerSupport) */
1828 @implementation NSBundle(NGBundleManagerExtensions)
1830 - (id)principalObject {
1831 return [[NGBundleManager defaultBundleManager]
1832 principalObjectOfBundle:self];
1835 - (NSArray *)providedResourcesOfType:(NSString *)_resourceType {
1836 return [[NGBundleManager defaultBundleManager]
1837 providedResourcesOfType:_resourceType
1841 - (NSString *)bundleName {
1842 return [[[self bundlePath] lastPathComponent] stringByDeletingPathExtension];
1845 - (NSString *)bundleType {
1846 return [[self bundlePath] pathExtension];
1849 - (NSArray *)providedClasses {
1850 return [[NGBundleManager defaultBundleManager] classesProvidedByBundle:self];
1853 - (NSArray *)requiredClasses {
1854 return [[NGBundleManager defaultBundleManager] classesRequiredByBundle:self];
1857 - (NSArray *)requiredBundles {
1858 return [[NGBundleManager defaultBundleManager] bundlesRequiredByBundle:self];
1861 - (NSDictionary *)configForResource:(id)_resource ofType:(NSString *)_type {
1862 return [[NGBundleManager defaultBundleManager]
1863 configForResource:_resource ofType:_type
1864 providedByBundle:self];
1869 - (BOOL)_loadForBundleManager:(NGBundleManager *)_manager {
1873 @end /* NSBundle(NGBundleManagerExtensions) */
1875 @implementation NSBundle(NGLanguageResourceExtensions)
1877 // locating resources
1879 - (NSString *)pathForResource:(NSString *)_name ofType:(NSString *)_ext
1880 inDirectory:(NSString *)_directory
1881 languages:(NSArray *)_languages
1883 NSFileManager *fm = [NSFileManager defaultManager];
1884 NSString *path = nil;
1886 id (*objAtIdx)(id,SEL,int);
1889 ? [[self bundlePath] stringByAppendingPathComponent:_directory]
1890 : [self bundlePath];
1892 if (![fm fileExistsAtPath:path])
1895 if (_ext) _name = [_name stringByAppendingPathExtension:_ext];
1897 langCount = [_languages count];
1898 objAtIdx = (langCount > 0)
1899 ? (void*)[_languages methodForSelector:@selector(objectAtIndex:)]
1902 for (i = 0; i < langCount; i++) {
1907 ? objAtIdx(_languages, @selector(objectAtIndex:), i)
1908 : [_languages objectAtIndex:i];
1910 language = [language stringByAppendingPathExtension:@"lproj"];
1911 lpath = [path stringByAppendingPathComponent:language];
1912 lpath = [lpath stringByAppendingPathComponent:_name];
1914 if ([fm fileExistsAtPath:lpath])
1918 /* now look into x.bundle/Resources/name.type */
1919 if ([fm fileExistsAtPath:[path stringByAppendingPathComponent:_name]])
1920 return [path stringByAppendingPathComponent:_name];
1925 - (NSString *)pathForResource:(NSString *)_name ofType:(NSString *)_ext
1926 languages:(NSArray *)_languages
1930 path = [self pathForResource:_name ofType:_ext
1931 inDirectory:@"Resources"
1932 languages:_languages];
1933 if (path) return path;
1935 path = [self pathForResource:_name ofType:_ext
1937 languages:_languages];
1941 @end /* NSBundle(NGLanguageResourceExtensions) */
1943 @implementation NGBundle
1946 return [self allocWithZone:NULL];
1948 + (id)allocWithZone:(NSZone*)zone {
1949 return NSAllocateObject(self, 0, zone);
1952 - (id)initWithPath:(NSString *)__path {
1953 return [super initWithPath:__path];
1958 - (BOOL)_loadForBundleManager:(NGBundleManager *)_manager {
1959 return [super load];
1963 NGBundleManager *bm;
1965 bm = [NGBundleManager defaultBundleManager];
1967 return [bm loadBundle:self] ? YES : NO;
1970 + (NSBundle *)bundleForClass:(Class)aClass {
1971 return [[NGBundleManager defaultBundleManager] bundleForClass:aClass];
1973 + (NSBundle *)bundleWithPath:(NSString*)path {
1974 return [[NGBundleManager defaultBundleManager] bundleWithPath:path];
1977 #if GNUSTEP_BASE_LIBRARY
1979 - (Class)principalClass {
1983 if ((c = [super principalClass]))
1986 if ((cname = [[self infoDictionary] objectForKey:@"NSPrincipalClass"]) ==nil)
1989 if ((c = NSClassFromString(cname)))
1992 NSLog(@"%s: did not find principal class named '%@' of bundle %@",
1993 __PRETTY_FUNCTION__, cname, self);
1999 - (NSString *)description {
2003 "<%s %p fullPath: %s infoDictionary: %p loaded=%s>",
2004 (char*)object_get_class_name(self),
2006 [[self bundlePath] cString],
2007 [self infoDictionary],
2008 self->_codeLoaded ? "yes" : "no");
2010 return [NSString stringWithCString:buffer];