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 [self doesNotRecognizeSelector:_cmd];
684 - (NSArray *)classesRequiredByBundle:(NSBundle *)_bundle {
685 [self doesNotRecognizeSelector:_cmd];
691 - (NSString *)makeBundleInfoPath:(NSString *)_path {
692 #if (NeXT_Foundation_LIBRARY || APPLE_Foundation_LIBRARY) && !defined(GSWARN)
693 return [[[_path stringByAppendingPathComponent:@"Contents"]
694 stringByAppendingPathComponent:@"Resources"]
695 stringByAppendingPathComponent:@"bundle-info.plist"];
697 return [_path stringByAppendingPathComponent:@"bundle-info.plist"];
701 - (id)_initializeLoadedBundle:(NSBundle *)_bundle
702 info:(NSDictionary *)_bundleInfo
706 /* check whether a handler was specified */
708 if ((handler = [_bundleInfo objectForKey:@"bundleHandler"]) != nil) {
709 [self debugWithFormat:@"lookup bundle handler %@ of bundle: %@",
712 if ((handler = NSClassFromString(handler)) == nil) {
713 NSLog(@"ERROR: did not find handler class %@ of bundle %@.",
714 [_bundleInfo objectForKey:@"bundleHandler"], [_bundle bundlePath]);
715 handler = [_bundle principalClass];
718 handler = [handler alloc];
720 if ([handler respondsToSelector:@selector(initForBundle:bundleManager:)])
721 handler = [handler initForBundle:_bundle bundleManager:self];
723 handler = [handler init];
724 handler = [handler autorelease];
726 if (handler == nil) {
727 NSLog(@"ERROR: could not instantiate handler class %@ of bundle %@.",
728 [_bundleInfo objectForKey:@"bundleHandler"], [_bundle bundlePath]);
729 handler = [_bundle principalClass];
733 [self debugWithFormat:
734 @"no bundle handler, lookup principal class of bundle: %@",
736 if ((handler = [_bundle principalClass]) == nil)
737 /* use NGBundle class as default bundle handler */
738 handler = [NGBundle class];
746 - (NSDictionary *)_loadBundleInfoAtExistingPath:(NSString *)_path {
747 NSDictionary *bundleInfo;
750 #if NeXT_Foundation_LIBRARY || COCOA_Foundation_LIBRARY
751 bundleInfo = NGParsePropertyListFromFile(_path);
753 bundleInfo = [NSDictionary dictionaryWithContentsOfFile:_path];
755 if (bundleInfo == nil) {
756 NSLog(@"couldn't load bundle-info at path '%@' !", _path);
760 /* check required bundle manager version */
761 info = [bundleInfo objectForKey:@"requires"];
762 if ((info = [(NSDictionary *)info objectForKey:@"bundleManagerVersion"])) {
763 if ([info intValue] > [[self class] version]) {
764 /* bundle manager version does not match ... */
768 NSMapInsert(self->pathToBundleInfo, _path, bundleInfo);
772 - (NSBundle *)_locateBundleForClassInfo:(NSDictionary *)_classInfo {
776 if (_classInfo == nil)
778 if ((className = [_classInfo objectForKey:@"name"]) == nil) {
779 NSLog(@"ERROR: missing classname in bundle-info.plist class section !");
783 if ((bundle = [self bundleForClassNamed:className]) == nil) {
784 #if 0 // class might be already loaded
785 NSLog(@"ERROR: did not find class %@ required by bundle %@.",
786 className, [_bundle bundlePath]);
792 - (NSArray *)_locateBundlesForClassInfos:(NSEnumerator *)_classInfos {
793 NSMutableArray *requiredBundles;
796 requiredBundles = [NSMutableArray arrayWithCapacity:16];
797 while ((i = [_classInfos nextObject])) {
800 if ((bundle = [self _locateBundleForClassInfo:i]) == nil)
803 [requiredBundles addObject:bundle];
805 return requiredBundles;
808 - (BOOL)_preLoadBundle:(NSBundle *)_bundle info:(NSDictionary *)_bundleInfo {
809 /* TODO: split up this huge method */
810 NSDictionary *requires = nil;
811 NSMutableArray *requiredBundles = nil;
812 NSBundle *requiredBundle = nil;
814 requires = [_bundleInfo objectForKey:@"requires"];
817 /* invalid bundle info specified */
820 /* load required bundles */
825 /* locate required bundles */
827 e = [[requires objectForKey:@"bundles"] objectEnumerator];
828 while ((i = [e nextObject])) {
829 NSString *bundleName;
831 if (![i respondsToSelector:@selector(objectForKey:)]) {
832 NSLog(@"ERROR(%s): invalid bundle-info of bundle %@ !!!\n"
833 @" requires-entry is not a dictionary: %@",
834 __PRETTY_FUNCTION__, _bundle, i);
838 if ((bundleName = [i objectForKey:@"name"])) {
841 type = [i objectForKey:@"type"];
842 if (type == nil) type = @"bundle";
844 if ((requiredBundle = [self bundleWithName:bundleName type:type])) {
845 if (requiredBundles == nil)
846 requiredBundles = [NSMutableArray arrayWithCapacity:16];
848 [requiredBundles addObject:requiredBundle];
851 NSLog(@"ERROR(NGBundleManager): did not find bundle '%@' (type=%@) "
852 @"required by bundle %@.",
853 bundleName, type, [_bundle bundlePath]);
858 NSLog(@"ERROR: error in bundle-info.plist of bundle %@", _bundle);
862 /* load located bundles */
866 e = [requiredBundles objectEnumerator];
867 while ((requiredBundle = [e nextObject])) {
870 if ((bundleMaster = [self loadBundle:requiredBundle]) == Nil) {
871 NSLog(@"ERROR: could not load bundle %@ (%@) required by bundle %@.",
872 [requiredBundle bundlePath], requiredBundle, [_bundle bundlePath]);
878 /* load required classes */
883 reqClasses = [requires objectForKey:@"classes"];
884 bundles = [self _locateBundlesForClassInfos:[reqClasses objectEnumerator]];
885 if (requiredBundles == nil)
886 requiredBundles = [NSMutableArray arrayWithCapacity:16];
887 [requiredBundles addObjectsFromArray:bundles];
890 /* load located bundles */
894 e = [requiredBundles objectEnumerator];
895 while ((requiredBundle = [e nextObject])) {
898 if ((bundleMaster = [self loadBundle:requiredBundle]) == Nil) {
899 NSLog(@"ERROR: could not load bundle %@ (%@) required by bundle %@.",
900 [requiredBundle bundlePath], requiredBundle, [_bundle bundlePath]);
906 /* check whether versions of classes match */
911 e = [[requires objectForKey:@"classes"] objectEnumerator];
912 while ((i = [e nextObject])) {
916 if ((className = [i objectForKey:@"name"]) == nil)
919 if ((clazz = NSClassFromString(className)) == Nil)
922 if ([i objectForKey:@"exact-version"]) {
925 v = [[i objectForKey:@"exact-version"] intValue];
927 if (v != [clazz version]) {
928 NSLog(@"ERROR: required exact class match failed:\n"
930 @" required version: %i\n"
931 @" loaded version: %i\n"
935 [_bundle bundlePath]);
938 else if ([i objectForKey:@"version"]) {
941 v = [[i objectForKey:@"version"] intValue];
943 if (v > [clazz version]) {
944 NSLog(@"ERROR: provided class does not match required version:\n"
946 @" least required version: %i\n"
947 @" loaded version: %i\n"
951 [_bundle bundlePath]);
959 - (BOOL)_postLoadBundle:(NSBundle *)_bundle info:(NSDictionary *)_bundleInfo {
963 - (id)loadBundle:(NSBundle *)_bundle {
964 NSString *path = nil;
965 NSDictionary *bundleInfo = nil;
966 id bundleManager = nil;
969 NSAssert(self->loadedBundles, @"missing loadedBundles hashmap ..");
972 if ((bundleManager = NSMapGet(self->loadedBundles, _bundle)))
973 return bundleManager;
975 if (_bundle == [NSBundle mainBundle])
976 return [NSBundle mainBundle];
978 if ([self->loadingBundles containsObject:_bundle])
982 if (self->loadingBundles == nil)
983 self->loadingBundles = [[NSMutableSet allocWithZone:[self zone]] init];
984 [self->loadingBundles addObject:_bundle];
986 path = [_bundle bundlePath];
987 path = [self makeBundleInfoPath:path];
989 if ((bundleInfo = NSMapGet(self->pathToBundleInfo, path)) == nil) {
990 if ([[NSFileManager defaultManager] fileExistsAtPath:path])
991 bundleInfo = [self _loadBundleInfoAtExistingPath:path];
994 if (![self _preLoadBundle:_bundle info:bundleInfo])
997 if (![_bundle _loadForBundleManager:self])
1000 if (![self _postLoadBundle:_bundle info:bundleInfo])
1003 if ((bundleManager =
1004 [self _initializeLoadedBundle:_bundle info:bundleInfo])) {
1005 NSMapInsert(self->loadedBundles, _bundle, bundleManager);
1007 if ([bundleManager respondsToSelector:
1008 @selector(bundleManager:didLoadBundle:)])
1009 [bundleManager bundleManager:self didLoadBundle:_bundle];
1013 NSLog(@"ERROR(%s): couldn't initialize loaded bundle '%@'",
1014 __PRETTY_FUNCTION__, [_bundle bundlePath]);
1018 [self->loadingBundles removeObject:_bundle];
1020 if (bundleManager) {
1021 if (bundleInfo == nil)
1022 bundleInfo = [NSDictionary dictionary];
1024 [[NSNotificationCenter defaultCenter]
1025 postNotificationName:
1026 NGBundleWasLoadedNotificationName
1028 userInfo:[NSDictionary dictionaryWithObjectsAndKeys:
1029 self, @"NGBundleManager",
1030 bundleManager, @"NGBundleHandler",
1031 bundleInfo, @"NGBundleInfo",
1034 return bundleManager;
1039 - (id)principalObjectOfBundle:(NSBundle *)_bundle {
1040 return (id)NSMapGet(self->loadedBundles, _bundle);
1045 static BOOL _doesInfoMatch(NSArray *keys, NSDictionary *dict, NSDictionary *info)
1049 for (i = 0, count = [keys count]; i < count; i++) {
1053 key = [keys objectAtIndex:i];
1054 vv = [info objectForKey:key];
1057 /* info has no matching key */
1061 kv = [dict objectForKey:key];
1062 if (![kv isEqual:vv])
1068 - (NSDictionary *)configForResource:(id)_resource ofType:(NSString *)_type
1069 providedByBundle:(NSBundle *)_bundle
1071 NSDictionary *bundleInfo = nil;
1073 NSEnumerator *providedResources;
1074 NSArray *rnKeys = nil;
1078 if ([_resource respondsToSelector:@selector(objectForKey:)]) {
1079 rnKeys = [_resource allKeys];
1080 rnKeyCount = [rnKeys count];
1083 infoPath = [self makeBundleInfoPath:[_bundle bundlePath]];
1085 /* check whether info is in cache */
1086 if ((bundleInfo = NSMapGet(self->pathToBundleInfo, infoPath)) == nil) {
1087 if (![[NSFileManager defaultManager] fileExistsAtPath:infoPath])
1088 /* no bundle-info.plist available .. */
1092 bundleInfo = [self _loadBundleInfoAtExistingPath:infoPath];
1095 /* get provided resources config */
1098 [[(NSDictionary *)[bundleInfo objectForKey:@"provides"] objectForKey:_type]
1100 if (providedResources == nil) return nil;
1102 /* scan provided resources */
1104 while ((info = [providedResources nextObject])) {
1106 if (!_doesInfoMatch(rnKeys, _resource, info))
1112 name = [[(NSDictionary *)info objectForKey:@"name"] stringValue];
1113 if (name == nil) continue;
1114 if (![name isEqualToString:_resource]) continue;
1121 - (void)_processInfoForProvidedResources:(NSDictionary *)info
1122 ofType:(NSString *)_type path:(NSString *)path
1123 resourceName:(NSString *)_resourceName
1124 resourceSelector:(NGBundleResourceSelector)_selector
1125 context:(void *)_context
1126 andAddToResultArray:(NSMutableArray *)result
1128 NSEnumerator *providedResources = nil;
1129 if (info == nil) return;
1131 /* direct match (a bundle was specified in the path) */
1133 providedResources = [[(NSDictionary *)[info objectForKey:@"provides"]
1137 if (providedResources == nil) return;
1139 /* scan provide array */
1140 while ((info = [providedResources nextObject])) {
1143 if ((name = [[info objectForKey:@"name"] stringValue]) == nil)
1146 if (_resourceName) {
1147 if (![name isEqualToString:_resourceName])
1151 if (!_selector(_resourceName, _type, path, info, self, _context))
1155 [result addObject:path];
1159 - (NSArray *)pathsForBundlesProvidingResource:(NSString *)_resourceName
1160 ofType:(NSString *)_type
1161 resourceSelector:(NGBundleResourceSelector)_selector
1162 context:(void *)_context
1164 /* TODO: split up method */
1165 NSMutableArray *result = nil;
1171 NSLog(@"BM LOOKUP pathes (%d bundles loaded): %@ / %@",
1172 NSCountMapTable(self->loadedBundles), _resourceName, _type);
1175 fm = [NSFileManager defaultManager];
1176 result = [NSMutableArray arrayWithCapacity:64];
1178 // TODO: look in loaded bundles
1180 /* check physical pathes */
1182 e = [self->bundleSearchPaths objectEnumerator];
1183 while ((path = [e nextObject]) != nil) {
1186 NSString *tmp, *bundleDirPath;
1189 if (![fm fileExistsAtPath:path isDirectory:&isDir])
1192 if (!isDir) continue;
1194 /* check whether an appropriate bundle is contained in 'path' */
1196 dir = [[fm directoryContentsAtPath:path] objectEnumerator];
1197 while ((bundleDirPath = [dir nextObject]) != nil) {
1198 NSDictionary *bundleInfo = nil;
1199 NSEnumerator *providedResources = nil;
1203 bundleDirPath = [path stringByAppendingPathComponent:bundleDirPath];
1204 infoPath = [self makeBundleInfoPath:bundleDirPath];
1206 // TODO: can we use _doesBundleInfo:path:providedResource:... ?
1207 if ((bundleInfo = NSMapGet(self->pathToBundleInfo, infoPath))==nil) {
1208 if (![fm fileExistsAtPath:infoPath])
1211 bundleInfo = [self _loadBundleInfoAtExistingPath:infoPath];
1215 [[(NSDictionary *)[bundleInfo objectForKey:@"provides"]
1218 if (providedResources == nil) continue;
1220 /* scan 'provides' array */
1221 while ((info = [providedResources nextObject])) {
1224 name = [[(NSDictionary *)info objectForKey:@"name"] stringValue];
1225 if (name == nil) continue;
1227 if (_resourceName != nil) {
1228 if (![name isEqualToString:_resourceName])
1231 if (_selector != NULL) {
1232 if (!_selector(name, _type, bundleDirPath, info, self, _context))
1236 [result addObject:bundleDirPath];
1241 /* check for direct match (NGBundlePath element is a bundle) */
1243 tmp = [self makeBundleInfoPath:path];
1245 if ((info = NSMapGet(self->pathToBundleInfo, tmp)) == nil) {
1246 if ([fm fileExistsAtPath:tmp])
1247 info = [self _loadBundleInfoAtExistingPath:tmp];
1250 [self _processInfoForProvidedResources:info ofType:_type path:path
1251 resourceName:_resourceName resourceSelector:_selector
1253 andAddToResultArray:result];
1256 if ([result count] == 0) {
1257 [self logWithFormat:
1258 @"Note(%s): method does not search in loaded bundles for "
1259 @"resources of type '%@'",
1260 __PRETTY_FUNCTION__, _type];
1263 return [[result copy] autorelease];
1266 - (BOOL)_doesBundleInfo:(NSDictionary *)_bundleInfo path:(NSString *)_path
1267 provideResource:(id)_resourceName ofType:(NSString *)_type
1268 rnKeys:(NSArray *)_rnKeys
1269 resourceSelector:(NGBundleResourceSelector)_selector context:(void *)_context
1271 NSEnumerator *providedResources;
1275 [[(NSDictionary *)[_bundleInfo objectForKey:@"provides"]
1276 objectForKey:_type] objectEnumerator];
1277 if (providedResources == nil) return NO;
1279 /* scan provide array */
1280 while ((info = [providedResources nextObject])) {
1281 if (_rnKeys != nil) {
1282 if (!_doesInfoMatch(_rnKeys, _resourceName, info))
1288 name = [[(NSDictionary *)info objectForKey:@"name"] stringValue];
1289 if (name == nil) continue;
1290 if (![name isEqualToString:_resourceName]) continue;
1293 if (_selector != NULL) {
1294 if (!_selector(_resourceName, _type, _path, info, self, _context))
1298 /* all conditions applied (found) */
1304 - (NSString *)pathOfLoadedBundleProvidingResource:(id)_resourceName
1305 ofType:(NSString *)_type
1306 resourceSelector:(NGBundleResourceSelector)_selector context:(void *)_context
1308 NSMapEnumerator menum;
1310 NSDictionary *bundleInfo;
1313 rnKeys = ([_resourceName respondsToSelector:@selector(objectForKey:)])
1314 ? [_resourceName allKeys]
1317 menum = NSEnumerateMapTable(self->pathToBundleInfo);
1318 while (NSNextMapEnumeratorPair(&menum, (void *)&path, (void *)&bundleInfo)) {
1320 NSLog(@"check loaded bundle for resource %@: %@", _resourceName,
1324 if ([self _doesBundleInfo:bundleInfo path:path
1325 provideResource:_resourceName ofType:_type rnKeys:rnKeys
1326 resourceSelector:_selector context:_context])
1327 /* strip bundle-info.plist name */
1328 return [path stringByDeletingLastPathComponent];
1334 - (NSString *)pathForBundleProvidingResource:(id)_resourceName
1335 ofType:(NSString *)_type
1336 resourceSelector:(NGBundleResourceSelector)_selector
1337 context:(void *)_context
1339 /* main path lookup method */
1340 // TODO: this method seriously needs some refactoring
1344 NSArray *rnKeys = nil;
1348 NSLog(@"BM LOOKUP path (%d bundles loaded): %@ / %@",
1349 NSCountMapTable(self->loadedBundles), _resourceName, _type);
1352 /* look in loaded bundles */
1354 path = [self pathOfLoadedBundleProvidingResource:_resourceName ofType:_type
1355 resourceSelector:_selector context:_context];
1356 if (path != nil) return path;
1358 /* look in filesystem */
1360 if ([_resourceName respondsToSelector:@selector(objectForKey:)]) {
1361 rnKeys = [_resourceName allKeys];
1362 rnKeyCount = [rnKeys count];
1365 fm = [NSFileManager defaultManager];
1366 e = [self->bundleSearchPaths objectEnumerator];
1367 while ((path = [e nextObject]) != nil) {
1373 if (![fm fileExistsAtPath:path isDirectory:&isDir])
1376 if (!isDir) continue;
1378 /* check whether an appropriate bundle is contained in 'path' */
1380 dir = [[fm directoryContentsAtPath:path] objectEnumerator];
1381 while ((tmp = [dir nextObject])) {
1382 NSDictionary *bundleInfo = nil;
1385 tmp = [path stringByAppendingPathComponent:tmp];
1386 infoPath = [self makeBundleInfoPath:tmp];
1389 NSLog(@"check path path=%@ info=%@", tmp, infoPath);
1391 if ((bundleInfo = NSMapGet(self->pathToBundleInfo, infoPath)) == nil) {
1392 if (![fm fileExistsAtPath:infoPath])
1395 bundleInfo = [self _loadBundleInfoAtExistingPath:infoPath];
1398 NSLog(@"found info for path=%@ info=%@: %@", tmp,infoPath,bundleInfo);
1400 if ([self _doesBundleInfo:bundleInfo path:tmp
1401 provideResource:_resourceName ofType:_type rnKeys:rnKeys
1402 resourceSelector:_selector context:_context])
1406 /* check for direct match */
1408 tmp = [self makeBundleInfoPath:path];
1410 if ((info = NSMapGet(self->pathToBundleInfo, tmp)) == nil) {
1411 if ([fm fileExistsAtPath:tmp])
1412 info = [self _loadBundleInfoAtExistingPath:tmp];
1414 NSLog(@"WARNING(%s): did not find direct path '%@'",
1415 __PRETTY_FUNCTION__, tmp);
1420 // direct match (a bundle was specified in the path)
1421 NSEnumerator *providedResources;
1422 NSDictionary *provides;
1424 provides = [(NSDictionary *)info objectForKey:@"provides"];
1425 providedResources = [[provides objectForKey:_type] objectEnumerator];
1427 if (providedResources == nil) continue;
1429 // scan provide array
1430 while ((info = [providedResources nextObject])) {
1432 if (!_doesInfoMatch(rnKeys, _resourceName, info))
1438 name = [[(NSDictionary *)info objectForKey:@"name"] stringValue];
1439 if (name == nil) continue;
1440 if (![name isEqualToString:_resourceName]) continue;
1444 if (!_selector(_resourceName, _type, tmp, info, self, _context))
1447 /* all conditions applied */
1455 - (NSBundle *)bundleProvidingResource:(id)_name ofType:(NSString *)_type {
1458 if (debugOn) NSLog(@"BM LOOKUP: %@ / %@", _name, _type);
1460 bp = [self pathForBundleProvidingResource:_name
1462 resourceSelector:NULL context:nil];
1463 if ([bp length] == 0) {
1464 #if (NeXT_Foundation_LIBRARY || APPLE_Foundation_LIBRARY) && HEAVY_DEBUG
1465 NSLog(@"%s: found no resource '%@' of type '%@' ...",
1466 __PRETTY_FUNCTION__, _resourceName, _resourceType);
1468 if (debugOn) NSLog(@" did not find: %@ / %@", _name, _type);
1472 if (debugOn) NSLog(@" FOUND: %@", bp);
1473 return [self bundleWithPath:bp];
1476 - (NSArray *)bundlesProvidingResource:(id)_resourceName
1477 ofType:(NSString *)_type
1480 NSMutableArray *bundles;
1483 paths = [self pathsForBundlesProvidingResource:_resourceName
1485 resourceSelector:NULL context:nil];
1487 count = [paths count];
1488 if (paths == nil) return nil;
1489 if (count == 0) return paths;
1491 bundles = [NSMutableArray arrayWithCapacity:count];
1492 for (i = 0; i < count; i++) {
1495 if ((bundle = [self bundleWithPath:[paths objectAtIndex:i]]))
1496 [bundles addObject:bundle];
1498 return [[bundles copy] autorelease];
1501 - (NSArray *)providedResourcesOfType:(NSString *)_resourceType
1502 inBundle:(NSBundle *)_bundle
1505 NSDictionary *bundleInfo;
1507 path = [self makeBundleInfoPath:[_bundle bundlePath]];
1508 if (path == nil) return nil;
1510 /* retrieve bundle info dictionary */
1511 if ((bundleInfo = NSMapGet(self->pathToBundleInfo, path)) == nil)
1512 bundleInfo = [self _loadBundleInfoAtExistingPath:path];
1514 return [(NSDictionary *)[bundleInfo objectForKey:@"provides"]
1515 objectForKey:_resourceType];
1518 - (void)_addRegisteredProvidedResourcesOfType:(NSString *)_type
1519 toSet:(NSMutableSet *)_result
1521 NSMapEnumerator menum;
1523 NSDictionary *bundleInfo;
1525 menum = NSEnumerateMapTable(self->pathToBundleInfo);
1526 while (NSNextMapEnumeratorPair(&menum, (void *)&path, (void *)&bundleInfo)) {
1527 NSArray *providedResources;
1530 NSLog(@"check loaded bundle for resource types %@: %@", _type, path);
1533 [(NSDictionary *)[bundleInfo objectForKey:@"provides"]
1534 objectForKey:_type];
1535 if (providedResources == nil) continue;
1537 [_result addObjectsFromArray:providedResources];
1541 - (NSArray *)providedResourcesOfType:(NSString *)_resourceType {
1542 NSMutableSet *result = nil;
1543 NSFileManager *fm = [NSFileManager defaultManager];
1547 result = [NSMutableSet setWithCapacity:128];
1549 /* scan loaded bundles */
1551 [self _addRegisteredProvidedResourcesOfType:_resourceType toSet:result];
1553 /* scan all bundle search paths */
1555 e = [self->bundleSearchPaths objectEnumerator];
1556 while ((path = [e nextObject]) != nil) {
1562 if (![fm fileExistsAtPath:path isDirectory:&isDir])
1564 if (!isDir) continue;
1566 /* check whether an appropriate bundle is contained in 'path' */
1568 // TODO: move to own method
1569 dir = [[fm directoryContentsAtPath:path] objectEnumerator];
1570 while ((tmp = [dir nextObject]) != nil) {
1571 NSDictionary *bundleInfo = nil;
1572 NSArray *providedResources = nil;
1575 tmp = [path stringByAppendingPathComponent:tmp];
1576 infoPath = [self makeBundleInfoPath:tmp];
1579 NSLog(@" info path: %@", tmp);
1582 if ((bundleInfo = NSMapGet(self->pathToBundleInfo, infoPath)) == nil) {
1583 if (![fm fileExistsAtPath:infoPath])
1586 bundleInfo = [self _loadBundleInfoAtExistingPath:infoPath];
1590 [(NSDictionary *)[bundleInfo objectForKey:@"provides"]
1591 objectForKey:_resourceType];
1592 if (providedResources == nil) continue;
1594 [result addObjectsFromArray:providedResources];
1597 /* check for direct match */
1599 tmp = [self makeBundleInfoPath:path];
1601 if ((info = NSMapGet(self->pathToBundleInfo, tmp)) == nil) {
1602 if ([fm fileExistsAtPath:tmp])
1603 info = [self _loadBundleInfoAtExistingPath:tmp];
1607 // direct match (a bundle was specified in the path)
1608 NSArray *providedResources;
1609 NSDictionary *provides;
1611 provides = [(NSDictionary *)info objectForKey:@"provides"];
1612 providedResources = [provides objectForKey:_resourceType];
1614 if (providedResources == nil) continue;
1616 [result addObjectsFromArray:providedResources];
1619 return [result allObjects];
1622 - (NSBundle *)bundleProvidingResourceOfType:(NSString *)_resourceType
1623 matchingQualifier:(EOQualifier *)_qual
1625 NSFileManager *fm = [NSFileManager defaultManager];
1629 /* foreach search path entry */
1631 e = [self->bundleSearchPaths objectEnumerator];
1632 while ((path = [e nextObject])) {
1635 if ([fm fileExistsAtPath:path isDirectory:&isDir]) {
1638 if (!isDir) continue;
1640 /* check whether an appropriate bundle is contained in 'path' */
1644 dir = [[fm directoryContentsAtPath:path] objectEnumerator];
1645 while ((tmp = [dir nextObject])) {
1646 NSDictionary *bundleInfo;
1647 NSArray *providedResources;
1650 tmp = [path stringByAppendingPathComponent:tmp];
1651 infoPath = [self makeBundleInfoPath:tmp];
1653 if ((bundleInfo=NSMapGet(self->pathToBundleInfo, infoPath)) == nil) {
1654 if (![fm fileExistsAtPath:infoPath])
1657 bundleInfo = [self _loadBundleInfoAtExistingPath:infoPath];
1660 bundleInfo = [bundleInfo objectForKey:@"provides"];
1661 providedResources = [bundleInfo objectForKey:_resourceType];
1663 if (providedResources == nil) continue;
1666 [providedResources filteredArrayUsingQualifier:_qual];
1668 if ([providedResources count] > 0)
1669 return [self bundleWithPath:tmp];
1673 /* check for direct match */
1675 tmp = [self makeBundleInfoPath:path];
1677 if ((info = NSMapGet(self->pathToBundleInfo, tmp)) == nil) {
1678 if ([fm fileExistsAtPath:tmp])
1679 info = [self _loadBundleInfoAtExistingPath:tmp];
1683 // direct match (a bundle was specified in the path)
1684 NSArray *providedResources;
1685 NSDictionary *provides;
1687 provides = [(NSDictionary *)info objectForKey:@"provides"];
1688 providedResources = [provides objectForKey:_resourceType];
1690 if (providedResources == nil) continue;
1693 [providedResources filteredArrayUsingQualifier:_qual];
1695 if ([providedResources count] > 0)
1696 return [self bundleWithPath:path];
1703 - (NSBundle *)bundlesProvidingResourcesOfType:(NSString *)_resourceType
1704 matchingQualifier:(EOQualifier *)_qual
1706 NSMutableArray *bundles = nil;
1707 NSFileManager *fm = [NSFileManager defaultManager];
1711 bundles = [NSMutableArray arrayWithCapacity:128];
1713 /* foreach search path entry */
1715 e = [self->bundleSearchPaths objectEnumerator];
1716 while ((path = [e nextObject])) {
1719 if ([fm fileExistsAtPath:path isDirectory:&isDir]) {
1722 if (!isDir) continue;
1724 /* check whether an appropriate bundle is contained in 'path' */
1728 dir = [[fm directoryContentsAtPath:path] objectEnumerator];
1729 while ((tmp = [dir nextObject])) {
1730 NSDictionary *bundleInfo = nil;
1731 NSArray *providedResources = nil;
1734 tmp = [path stringByAppendingPathComponent:tmp];
1735 infoPath = [self makeBundleInfoPath:tmp];
1737 if ((bundleInfo=NSMapGet(self->pathToBundleInfo, infoPath)) == nil) {
1738 if (![fm fileExistsAtPath:infoPath])
1741 bundleInfo = [self _loadBundleInfoAtExistingPath:infoPath];
1744 bundleInfo = [bundleInfo objectForKey:@"provides"];
1745 providedResources = [bundleInfo objectForKey:_resourceType];
1747 if (providedResources == nil) continue;
1750 [providedResources filteredArrayUsingQualifier:_qual];
1752 if ([providedResources count] > 0)
1753 [bundles addObject:[self bundleWithPath:tmp]];
1757 /* check for direct match */
1759 tmp = [self makeBundleInfoPath:path];
1761 if ((info = NSMapGet(self->pathToBundleInfo, tmp)) == nil) {
1762 if ([fm fileExistsAtPath:tmp])
1763 info = [self _loadBundleInfoAtExistingPath:tmp];
1767 // direct match (a bundle was specified in the path)
1768 NSArray *providedResources;
1769 NSDictionary *provides;
1771 provides = [(NSDictionary *)info objectForKey:@"provides"];
1772 providedResources = [provides objectForKey:_resourceType];
1774 if (providedResources == nil) continue;
1777 [providedResources filteredArrayUsingQualifier:_qual];
1779 if ([providedResources count] > 0)
1780 [bundles addObject:[self bundleWithPath:path]];
1784 return [[bundles copy] autorelease];
1789 - (void)_bundleDidLoadNotifcation:(NSNotification *)_notification {
1790 NSDictionary *ui = [_notification userInfo];
1793 NSLog(@"bundle %@ did load with classes %@",
1794 [[_notification object] bundlePath],
1795 [ui objectForKey:@"NSLoadedClasses"]);
1798 [self registerBundle:[_notification object]
1799 classes:[ui objectForKey:@"NSLoadedClasses"]
1800 categories:[ui objectForKey:@"NSLoadedCategories"]];
1805 - (BOOL)isDebuggingEnabled {
1809 @end /* NGBundleManager */
1811 @implementation NSBundle(BundleManagerSupport)
1814 return [NGBundle alloc];
1816 + (id)allocWithZone:(NSZone *)zone {
1817 return [NGBundle allocWithZone:zone];
1820 #if !(NeXT_Foundation_LIBRARY || APPLE_Foundation_LIBRARY)
1821 //#warning remember, bundleForClass is not overridden !
1823 + (NSBundle *)bundleForClass:(Class)aClass {
1824 return [[NGBundleManager defaultBundleManager] bundleForClass:aClass];
1827 + (NSBundle *)bundleWithPath:(NSString*)path {
1828 return [[NGBundleManager defaultBundleManager] bundleWithPath:path];
1832 @end /* NSBundle(BundleManagerSupport) */
1834 @implementation NSBundle(NGBundleManagerExtensions)
1836 - (id)principalObject {
1837 return [[NGBundleManager defaultBundleManager]
1838 principalObjectOfBundle:self];
1841 - (NSArray *)providedResourcesOfType:(NSString *)_resourceType {
1842 return [[NGBundleManager defaultBundleManager]
1843 providedResourcesOfType:_resourceType
1847 - (NSString *)bundleName {
1848 return [[[self bundlePath] lastPathComponent] stringByDeletingPathExtension];
1851 - (NSString *)bundleType {
1852 return [[self bundlePath] pathExtension];
1855 - (NSArray *)providedClasses {
1856 return [[NGBundleManager defaultBundleManager] classesProvidedByBundle:self];
1859 - (NSArray *)requiredClasses {
1860 return [[NGBundleManager defaultBundleManager] classesRequiredByBundle:self];
1863 - (NSArray *)requiredBundles {
1864 return [[NGBundleManager defaultBundleManager] bundlesRequiredByBundle:self];
1867 - (NSDictionary *)configForResource:(id)_resource ofType:(NSString *)_type {
1868 return [[NGBundleManager defaultBundleManager]
1869 configForResource:_resource ofType:_type
1870 providedByBundle:self];
1875 - (BOOL)_loadForBundleManager:(NGBundleManager *)_manager {
1879 @end /* NSBundle(NGBundleManagerExtensions) */
1881 @implementation NSBundle(NGLanguageResourceExtensions)
1883 static BOOL debugLanguageLookup = NO;
1885 // locating resources
1887 - (NSString *)pathForResource:(NSString *)_name ofType:(NSString *)_ext
1888 inDirectory:(NSString *)_directory
1889 languages:(NSArray *)_languages
1892 NSString *path = nil;
1894 id (*objAtIdx)(id,SEL,int);
1896 if (debugLanguageLookup) {
1897 NSLog(@"LOOKUP: %@ | %@ | %@ | %@",
1898 _name, _ext, _directory, [_languages componentsJoinedByString:@","]);
1901 path = [_directory isNotNull]
1902 ? [[self bundlePath] stringByAppendingPathComponent:_directory]
1903 : [self bundlePath];
1905 if (debugLanguageLookup) NSLog(@" BASE: %@", path);
1907 fm = [NSFileManager defaultManager];
1908 if (![fm fileExistsAtPath:path])
1911 if (_ext != nil) _name = [_name stringByAppendingPathExtension:_ext];
1913 langCount = [_languages count];
1914 objAtIdx = (langCount > 0)
1915 ? (void*)[_languages methodForSelector:@selector(objectAtIndex:)]
1918 for (i = 0; i < langCount; i++) {
1923 ? objAtIdx(_languages, @selector(objectAtIndex:), i)
1924 : [_languages objectAtIndex:i];
1926 language = [language stringByAppendingPathExtension:@"lproj"];
1927 lpath = [path stringByAppendingPathComponent:language];
1928 lpath = [lpath stringByAppendingPathComponent:_name];
1930 if ([fm fileExistsAtPath:lpath])
1934 if (debugLanguageLookup)
1935 NSLog(@" no language matched, check base: %@", path);
1937 /* now look into x.bundle/Resources/name.type */
1938 if ([fm fileExistsAtPath:[path stringByAppendingPathComponent:_name]])
1939 return [path stringByAppendingPathComponent:_name];
1944 - (NSString *)pathForResource:(NSString *)_name ofType:(NSString *)_ext
1945 languages:(NSArray *)_languages
1949 path = [self pathForResource:_name ofType:_ext
1950 inDirectory:@"Resources"
1951 languages:_languages];
1952 if (path) return path;
1954 path = [self pathForResource:_name ofType:_ext
1956 languages:_languages];
1960 @end /* NSBundle(NGLanguageResourceExtensions) */
1962 @implementation NGBundle
1965 return [self allocWithZone:NULL];
1967 + (id)allocWithZone:(NSZone*)zone {
1968 return NSAllocateObject(self, 0, zone);
1971 - (id)initWithPath:(NSString *)__path {
1972 return [super initWithPath:__path];
1977 - (BOOL)_loadForBundleManager:(NGBundleManager *)_manager {
1978 return [super load];
1982 NGBundleManager *bm;
1984 bm = [NGBundleManager defaultBundleManager];
1986 return [bm loadBundle:self] ? YES : NO;
1989 + (NSBundle *)bundleForClass:(Class)aClass {
1990 return [[NGBundleManager defaultBundleManager] bundleForClass:aClass];
1992 + (NSBundle *)bundleWithPath:(NSString*)path {
1993 return [[NGBundleManager defaultBundleManager] bundleWithPath:path];
1996 #if GNUSTEP_BASE_LIBRARY
1998 - (Class)principalClass {
2002 if ((c = [super principalClass]))
2005 if ((cname = [[self infoDictionary] objectForKey:@"NSPrincipalClass"]) ==nil)
2008 if ((c = NSClassFromString(cname)))
2011 NSLog(@"%s: did not find principal class named '%@' of bundle %@",
2012 __PRETTY_FUNCTION__, cname, self);
2018 - (NSString *)description {
2022 "<%s %p fullPath: %s infoDictionary: %p loaded=%s>",
2023 (char*)object_get_class_name(self),
2025 [[self bundlePath] cString],
2026 [self infoDictionary],
2027 self->_codeLoaded ? "yes" : "no");
2029 return [NSString stringWithCString:buffer];