mirror of
https://github.com/gnustep/apps-projectcenter.git
synced 2025-02-15 08:00:56 +00:00
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/apps/projectcenter/trunk@39586 72102866-910b-0410-8b05-ffd578937521
421 lines
11 KiB
Objective-C
421 lines
11 KiB
Objective-C
/*
|
|
GNUstep ProjectCenter - http://www.gnustep.org/experience/ProjectCenter.html
|
|
|
|
Copyright (C) 2000-2004 Free Software Foundation
|
|
|
|
Authors: Philippe C.D. Robert
|
|
Serg Stoyan
|
|
|
|
This file is part of GNUstep.
|
|
|
|
This application is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
This application is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public
|
|
License along with this library; if not, write to the Free
|
|
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
|
|
*/
|
|
|
|
// TODO: Finish support for third party bundles.
|
|
// It involves support for user defined bundle directories
|
|
// through preferences. Now supported are:
|
|
// - PC application resource dir
|
|
// (GNUSTEP_SYSTEM_APPS/ProjectCenter.app/Resources)
|
|
// - GNUSTEP_SYSTEM_LIBRARY/Bundles/ProjectCenter
|
|
// (NSApplicationSupportDirectory)
|
|
|
|
#import <ProjectCenter/PCBundleManager.h>
|
|
#import <ProjectCenter/PCDefines.h>
|
|
|
|
#import <ProjectCenter/PCLogController.h>
|
|
|
|
@implementation PCBundleManager
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Init and free methods
|
|
//----------------------------------------------------------------------------
|
|
|
|
- (id)init
|
|
{
|
|
if ((self = [super init]))
|
|
{
|
|
loadedBundles = [[NSMutableDictionary alloc] init];
|
|
bundlesInfo = [[NSMutableDictionary alloc] init];
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void)dealloc
|
|
{
|
|
RELEASE(loadedBundles);
|
|
RELEASE(bundlesInfo);
|
|
|
|
[super dealloc];
|
|
}
|
|
|
|
//
|
|
- (NSString *)resourcePath
|
|
{
|
|
NSString *path = [[NSBundle mainBundle] resourcePath];
|
|
|
|
if (![[NSFileManager defaultManager] fileExistsAtPath:path])
|
|
{
|
|
[NSException raise:@"PCBundleManagerPathException"
|
|
format:@"ProjectCenter installed incorrectly"];
|
|
return nil;
|
|
}
|
|
|
|
return path;
|
|
}
|
|
|
|
// --- Handling of bundles' Info.table dictionaries without actual
|
|
// --- bundles loading
|
|
|
|
// bundlesInfo is a dictionary. key/value pair is the following:
|
|
// (NSString *) (NSDictionary *)
|
|
// "full path of a bundle" = "Info.table contents"
|
|
- (NSDictionary *)infoForBundlesType:(NSString *)extension
|
|
{
|
|
NSArray *bundles;
|
|
NSEnumerator *enumerator;
|
|
NSString *bundlePath;
|
|
NSString *infoTablePath;
|
|
NSDictionary *infoTable;
|
|
NSMutableDictionary *reqBundlesInfo;
|
|
|
|
reqBundlesInfo = [NSMutableDictionary dictionary];
|
|
bundles = [NSBundle pathsForResourcesOfType:extension
|
|
inDirectory:[self resourcePath]];
|
|
enumerator = [bundles objectEnumerator];
|
|
|
|
while ((bundlePath = [enumerator nextObject]))
|
|
{
|
|
infoTablePath = [NSString
|
|
stringWithFormat:@"%@/Resources/Info.table", bundlePath];
|
|
// TODO: fill 'reqBundlesInfo' with element from 'bundlesInfo' if
|
|
// exists
|
|
infoTable = [NSDictionary dictionaryWithContentsOfFile:infoTablePath];
|
|
[reqBundlesInfo setObject:infoTable forKey:bundlePath];
|
|
[bundlesInfo setObject:infoTable forKey:bundlePath];
|
|
}
|
|
|
|
return reqBundlesInfo;
|
|
}
|
|
|
|
// Key value can be checked against NSString and NSArray values only.
|
|
- (NSDictionary *)infoForBundleType:(NSString *)extension
|
|
keyName:(NSString *)key
|
|
keyContains:(NSString *)value
|
|
{
|
|
NSDictionary *reqBundlesInfo;
|
|
NSEnumerator *enumerator;
|
|
NSString *bundlePath;
|
|
id keyValue;
|
|
NSDictionary *infoTable;
|
|
|
|
if (extension == nil)
|
|
{
|
|
return nil;
|
|
}
|
|
|
|
reqBundlesInfo = [self infoForBundlesType:extension];
|
|
enumerator = [[reqBundlesInfo allKeys] objectEnumerator];
|
|
|
|
infoTable = nil;
|
|
while ((bundlePath = [enumerator nextObject]))
|
|
{
|
|
infoTable = [reqBundlesInfo objectForKey:bundlePath];
|
|
|
|
if (key == nil || value == nil)
|
|
{
|
|
break;
|
|
}
|
|
|
|
keyValue = [infoTable objectForKey:key];
|
|
|
|
if ([keyValue isKindOfClass:[NSString class]] &&
|
|
[keyValue isEqualToString:value])
|
|
{
|
|
break;
|
|
}
|
|
else if ([keyValue isKindOfClass:[NSArray class]] &&
|
|
[keyValue containsObject:value])
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
infoTable = nil;
|
|
}
|
|
}
|
|
|
|
return infoTable;
|
|
}
|
|
|
|
- (NSDictionary *)infoForBundleName:(NSString *)name
|
|
type:(NSString *)type
|
|
{
|
|
NSDictionary *reqBundlesInfo = [self infoForBundlesType:type];
|
|
NSEnumerator *enumerator = [[reqBundlesInfo allKeys] objectEnumerator];
|
|
NSString *bundlePath;
|
|
NSDictionary *infoTable;
|
|
|
|
infoTable = nil;
|
|
while ((bundlePath = [enumerator nextObject]))
|
|
{
|
|
infoTable = [reqBundlesInfo objectForKey:bundlePath];
|
|
if ([[infoTable objectForKey:@"Name"] isEqualToString:name])
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
infoTable = nil;
|
|
}
|
|
}
|
|
|
|
return infoTable;
|
|
}
|
|
|
|
- (NSString *)classNameForBundleType:(NSString*)type
|
|
fileName:(NSString *)fileName
|
|
{
|
|
NSString *fileExtension = [fileName pathExtension];
|
|
NSDictionary *infoTable = nil;
|
|
NSString *className = nil;
|
|
|
|
infoTable = [self infoForBundleType:type
|
|
keyName:@"FileTypes"
|
|
keyContains:fileExtension];
|
|
|
|
className = [infoTable objectForKey:@"PrincipalClassName"];
|
|
|
|
return className;
|
|
}
|
|
|
|
- (NSString *)bundlePathWithName:(NSString *)bundleName
|
|
{
|
|
NSArray *bundlePaths = nil;
|
|
NSString *bundleFullPath = nil;
|
|
NSEnumerator *enumerator = nil;
|
|
|
|
// Search for bundle full path in bundlesInfo dictionary
|
|
bundlePaths = [bundlesInfo allKeys];
|
|
enumerator = [bundlePaths objectEnumerator];
|
|
|
|
// NSLog(@"Bundle fullpath method #1: %@",
|
|
// [[self resourcePath] stringByAppendingPathComponent:bundleName]);
|
|
|
|
while ((bundleFullPath = [enumerator nextObject]))
|
|
{
|
|
if ([[bundleFullPath lastPathComponent] isEqualToString:bundleName])
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
// NSLog(@"Bundle fullpath method #2: %@", bundleFullPath);
|
|
|
|
return bundleFullPath;
|
|
}
|
|
|
|
// --- Invokes loading of bundle
|
|
|
|
- (id)objectForClassName:(NSString *)className
|
|
bundleType:(NSString *)bundleExtension
|
|
protocol:(Protocol *)proto
|
|
{
|
|
Class objectClass;
|
|
|
|
if (!className)
|
|
{
|
|
NSLog(@"Bundle for class called with empty className");
|
|
return nil;
|
|
}
|
|
|
|
if ([self bundleOfType:bundleExtension withClassName:className] == nil)
|
|
{
|
|
NSLog(@"Bundle for class %@ NOT FOUND!", className);
|
|
return nil;
|
|
}
|
|
|
|
objectClass = NSClassFromString(className);
|
|
|
|
if (proto != nil && ![objectClass conformsToProtocol:proto])
|
|
{
|
|
[NSException raise:NOT_A_PROJECT_TYPE_EXCEPTION
|
|
format:@"%@ does not conform to protocol!", className];
|
|
return nil;
|
|
}
|
|
|
|
return [[objectClass alloc] init];
|
|
}
|
|
|
|
- (id)objectForBundleWithName:(NSString *)name
|
|
type:(NSString *)extension
|
|
protocol:(Protocol *)proto
|
|
{
|
|
NSDictionary *infoTable;
|
|
NSString *className;
|
|
|
|
infoTable = [self infoForBundleName:name type:extension];
|
|
className = [infoTable objectForKey:@"PrincipalClassName"];
|
|
|
|
return [self objectForClassName:className
|
|
bundleType:extension
|
|
protocol:proto];
|
|
}
|
|
|
|
- (id)objectForBundleType:(NSString *)extension
|
|
protocol:(Protocol *)proto
|
|
fileName:(NSString *)fileName
|
|
{
|
|
NSString *className;
|
|
|
|
className = [self classNameForBundleType:extension fileName:fileName];
|
|
|
|
return [self objectForClassName:className
|
|
bundleType:extension
|
|
protocol:proto];
|
|
}
|
|
|
|
// --- Bundle loading
|
|
|
|
- (NSBundle *)bundleOfType:(NSString *)type
|
|
withClassName:(NSString *)className
|
|
{
|
|
NSArray *bundlePaths = nil;
|
|
NSString *bundleFullPath = nil;
|
|
NSDictionary *infoTable = nil;
|
|
NSEnumerator *enumerator = nil;
|
|
NSString *bundleName = nil;
|
|
NSString *principalClass;
|
|
|
|
// Search for bundle full path in bundlesInfo dictionary
|
|
bundlePaths = [bundlesInfo allKeys];
|
|
enumerator = [bundlePaths objectEnumerator];
|
|
|
|
while ((bundleFullPath = [enumerator nextObject]))
|
|
{
|
|
|
|
if ([[bundleFullPath pathExtension] isEqualToString:type])
|
|
{
|
|
infoTable = [bundlesInfo objectForKey:bundleFullPath];
|
|
principalClass = [infoTable objectForKey:@"PrincipalClassName"];
|
|
if ([className isEqualToString:principalClass])
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// NSLog(@"bundleForClassName: %@ path %@", className, bundleFullPath);
|
|
|
|
// Extract from full bundle path it's name and extension
|
|
bundleName = [bundleFullPath lastPathComponent];
|
|
if (![self loadBundleIfNeededWithName:bundleName])
|
|
{
|
|
return nil;
|
|
}
|
|
|
|
return [loadedBundles objectForKey:bundleFullPath];
|
|
}
|
|
|
|
- (BOOL)loadBundleIfNeededWithName:(NSString *)bundleName
|
|
{
|
|
NSString *bundleFullPath = [self bundlePathWithName:bundleName];
|
|
|
|
// Check if bundle allready loaded
|
|
if ([[loadedBundles allKeys] containsObject:bundleFullPath] == NO)
|
|
{
|
|
return [self loadBundleWithFullPath:bundleFullPath];
|
|
}
|
|
|
|
return YES;
|
|
}
|
|
|
|
- (void)loadBundlesWithExtension:(NSString *)extension
|
|
{
|
|
NSEnumerator *enumerator;
|
|
NSFileManager *fileManager = [NSFileManager defaultManager];
|
|
BOOL isDir;
|
|
NSString *path = [self resourcePath];
|
|
|
|
if (path)
|
|
{
|
|
[self loadBundlesAtPath:path withExtension:extension];
|
|
}
|
|
|
|
// Load third party bundles
|
|
enumerator = [NSSearchPathForDirectoriesInDomains
|
|
(NSApplicationSupportDirectory, NSAllDomainsMask, YES)
|
|
objectEnumerator];
|
|
while ((path = [enumerator nextObject]) != nil)
|
|
{
|
|
path = [path stringByAppendingPathComponent:@"ProjectCenter"];
|
|
|
|
if ([fileManager fileExistsAtPath:path isDirectory:&isDir] && isDir)
|
|
{
|
|
PCLogInfo(self, @"Loading bundles at %@", path);
|
|
[self loadBundlesAtPath:path withExtension:extension];
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)loadBundlesAtPath:(NSString *)path withExtension:(NSString *)extension
|
|
{
|
|
NSEnumerator *enumerator;
|
|
NSString *bundleName;
|
|
NSArray *dir;
|
|
|
|
dir = [[NSFileManager defaultManager] directoryContentsAtPath:path];
|
|
enumerator = [dir objectEnumerator];
|
|
|
|
while ((bundleName = [enumerator nextObject]))
|
|
{
|
|
if ([[bundleName pathExtension] isEqualToString:extension])
|
|
{
|
|
NSString *fullPath = nil;
|
|
|
|
fullPath = [NSString stringWithFormat:@"%@/%@", path, bundleName];
|
|
|
|
[self loadBundleWithFullPath:fullPath];
|
|
}
|
|
}
|
|
}
|
|
|
|
- (BOOL)loadBundleWithFullPath:(NSString *)path
|
|
{
|
|
NSBundle *bundle = nil;
|
|
|
|
if ((bundle = [NSBundle bundleWithPath:path]) && [bundle load])
|
|
{
|
|
[loadedBundles setObject:bundle forKey:path];
|
|
|
|
PCLogInfo(self, @"Bundle %@ successfully loaded!", path);
|
|
}
|
|
else
|
|
{
|
|
NSRunAlertPanel(@"Load Bundle",
|
|
@"Could not load bundle %@!",
|
|
@"OK", nil, nil, path);
|
|
return NO;
|
|
}
|
|
|
|
return YES;
|
|
}
|
|
|
|
- (NSDictionary *)loadedBundles
|
|
{
|
|
return loadedBundles;
|
|
}
|
|
|
|
@end
|