2 Copyright (C) 2002-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 "SoProductRegistry.h"
23 #include "SoProduct.h"
25 #include "SoClassSecurityInfo.h"
27 #include <NGObjWeb/WOResponse.h>
28 #include <NGObjWeb/WOContext.h>
30 @implementation SoProductRegistry
32 static int debugOn = 0;
35 static BOOL didInit = NO;
37 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
39 debugOn = [ud boolForKey:@"SoProductRegistryDebugEnabled"] ? 1 : 0;
43 + (id)sharedProductRegistry {
44 static SoProductRegistry *reg = nil; // THREAD
46 reg = [[SoProductRegistry alloc] init];
51 if ((self = [super init])) {
52 [self scanForAvailableProducts];
54 [[NSNotificationCenter defaultCenter]
55 addObserver:self selector:@selector(_bundleDidLoad:)
56 name:@"NSBundleDidLoadNotification" object:nil];
62 [[NSNotificationCenter defaultCenter] removeObserver:self];
63 [self->bundlePathToFirstName release];
64 [self->products release];
70 - (void)_bundleDidLoad:(NSNotification *)_notification {
72 If bundles are loaded by some other code, check whether they contain
75 [self registerProductBundle:[_notification object]];
80 - (NSFileManager *)fileManager {
81 return [NSFileManager defaultManager];
84 - (void)registerProductBundle:(NSBundle *)_bundle {
85 NSString *productName, *firstProductName, *bundlePath;
89 if (![_bundle isNotNull])
92 bundlePath = [_bundle bundlePath];
94 if (_bundle != [NSBundle mainBundle]) {
96 [[bundlePath lastPathComponent] stringByDeletingPathExtension];
99 productName = @"MAIN";
101 if ((product = [self->products objectForKey:productName])) {
102 [self debugWithFormat:@"product '%@' already registered.", productName];
103 [product reloadIfPossible];
108 [self->bundlePathToFirstName objectForKey:bundlePath];
109 if (firstProductName != nil) {
110 [self debugWithFormat:
111 @"Note: register bundle with a different name '%@': '%@'",
112 productName, bundlePath];
113 if ((product = [self->products objectForKey:firstProductName]) != nil) {
114 [self debugWithFormat:
115 @"add additional name '%@' (first %@) for product '%@'",
116 productName, firstProductName, product];
117 [self->products setObject:product forKey:productName];
122 @"WARNING: no product object for first name '%@' "
123 @"(name=%@,bundle=%@)",
124 firstProductName, productName, bundlePath];
128 manifest = [_bundle pathForResource:@"product" ofType:@"plist"];
129 if ([manifest length] == 0) {
130 if ([productName isEqualToString:@"MAIN"])
131 [self debugWithFormat:@" main bundle has no manifest."];
137 if (self->products == nil)
138 self->products = [[NSMutableDictionary alloc] initWithCapacity:32];
139 if (self->bundlePathToFirstName == nil) {
140 self->bundlePathToFirstName =
141 [[NSMutableDictionary alloc] initWithCapacity:32];
146 [self debugWithFormat:@"register product bundle: '%@' (0x%08X[%@])",
147 bundlePath, _bundle, NSStringFromClass([_bundle class])];
149 [self debugWithFormat:@" register as product: %@", productName];
151 if ((product = [[SoProduct alloc] initWithBundle:_bundle]) == nil) {
152 [self debugWithFormat:@" could not init product from bundle: %@",
157 [self->bundlePathToFirstName setObject:productName forKey:bundlePath];
158 [self->products setObject:product forKey:productName];
162 - (void)registerProductAtPath:(NSString *)_path {
163 static NGBundleManager *bm = nil;
166 if (![_path isNotNull])
169 if (bm == nil) bm = [[NGBundleManager defaultBundleManager] retain];
170 bundle = [bm bundleWithPath:_path];
173 [self logWithFormat:@"could not init bundle object for path: %@", _path];
176 [self registerProductBundle:bundle];
179 - (void)scanForProductsInDirectory:(NSString *)_path {
181 NSEnumerator *pathes;
184 fm = [self fileManager];
185 pathes = [[fm directoryContentsAtPath:_path] objectEnumerator];
186 while ((lPath = [pathes nextObject])) {
189 lPath = [_path stringByAppendingPathComponent:lPath];
191 if (![fm fileExistsAtPath:lPath isDirectory:&isDir])
196 [self registerProductAtPath:lPath];
200 - (void)scanForAvailableProducts {
207 /* scan mail bundle & frameworks */
209 if ((bundle = [NSBundle mainBundle]))
210 [self registerProductBundle:bundle];
212 NSLog(@"%s: missing main bundle ...", __PRETTY_FUNCTION__);
214 pathes = [NSBundle allFrameworks];
215 for (i = 0; i < [pathes count]; i++)
216 [self registerProductBundle:[pathes objectAtIndex:i]];
218 pathes = [NSBundle allBundles];
219 for (i = 0; i < [pathes count]; i++)
220 [self registerProductBundle:[pathes objectAtIndex:i]];
222 /* scan library pathes */
224 fm = [NSFileManager defaultManager];
225 pi = [NSProcessInfo processInfo];
227 #if COCOA_Foundation_LIBRARY
229 TODO: (like COMPILE_FOR_GNUSTEP)
230 This should actually check whether we are compiling in the
231 GNUstep environment since this modifies the location of bundles.
233 pathes = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,
237 pathes = [[pi environment] objectForKey:@"GNUSTEP_PATHPREFIX_LIST"];
239 pathes = [[pi environment] objectForKey:@"GNUSTEP_PATHLIST"];
241 pathes = [[pathes stringValue] componentsSeparatedByString:@":"];
244 if ([pathes count] == 0) {
245 [self debugWithFormat:@"found no product pathes."];
249 [self debugWithFormat:@"scanning for products ..."];
251 for (i = 0; i < [pathes count]; i++) {
255 lPath = [pathes objectAtIndex:i];
256 #if !COCOA_Foundation_LIBRARY
257 lPath = [lPath stringByAppendingPathComponent:@"Library"];
259 lPath = [lPath stringByAppendingPathComponent:@"SoProducts-4.3"];
261 if (![fm fileExistsAtPath:lPath isDirectory:&isDir])
266 [self debugWithFormat:@" directory %@", lPath];
267 [self scanForProductsInDirectory:lPath];
270 /* look into FHS pathes */
272 pathes = [NSArray arrayWithObjects:
273 @"/usr/local/lib/sope-4.3/products/",
274 @"/usr/lib/sope-4.3/products/",
276 for (i = 0; i < [pathes count]; i++) {
280 lPath = [pathes objectAtIndex:i];
281 if (![fm fileExistsAtPath:lPath isDirectory:&isDir])
286 [self debugWithFormat:@" directory %@", lPath];
287 [self scanForProductsInDirectory:lPath];
292 [self debugWithFormat:
293 @"finished scan for products (%i products registered).",
294 [self->products count]];
297 /* registering products */
299 - (BOOL)loadProductNamed:(NSString *)_name {
301 NSEnumerator *requiredProducts;
304 if ((product = [self->products objectForKey:_name]) == nil) {
305 [self debugWithFormat:@"did not find product: %@", _name];
309 /* load dependencies (TODO: should detect cycles) */
310 requiredProducts = [[product requiredProducts] objectEnumerator];
311 while ((rqname = [requiredProducts nextObject])) {
312 if (![self loadProductNamed:rqname]) {
313 if ([rqname isEqualToString:@"MAIN"]) continue;
314 [self logWithFormat:@"ERROR: failed to load product %@ required by %@.",
320 return [product load];
323 - (BOOL)loadAllProducts {
327 e = [self->products keyEnumerator];
328 while ((p = [e nextObject])) {
329 if (![self loadProductNamed:p])
330 [self logWithFormat:@"could not load product: %@", p];
335 /* lookup products */
337 - (NSArray *)registeredProductNames {
338 return [self->products allKeys];
340 - (SoProduct *)productWithName:(NSString *)_name {
341 return [self->products objectForKey:_name];
346 - (SoProduct *)productForBundle:(NSBundle *)_bundle {
347 /* TODO: add a registry based on path ... */
348 NSString *pname, *bpath;
351 bpath = [_bundle bundlePath];
353 /* check whether a name is cached for the bundle .. */
355 pname = [self->bundlePathToFirstName objectForKey:bpath];
356 if ((product = [self productWithName:pname]) != nil)
359 /* 'calculate' name of bundle */
361 pname = [[bpath lastPathComponent] stringByDeletingPathExtension];
362 if ((product = [self productWithName:pname]) != nil)
365 /* load missing product */
368 @"product '%@' not yet registered, attempting to load ...", pname];
369 if (![self loadProductNamed:pname])
371 return [self productWithName:pname];
374 /* product registry as a SoObject */
376 - (NSArray *)allKeys {
377 return [self registeredProductNames];
380 - (BOOL)hasName:(NSString *)_key inContext:(id)_ctx {
381 if ([self->products objectForKey:_key])
383 return [super hasName:_key inContext:_ctx];
386 - (id)lookupName:(NSString *)_key inContext:(id)_ctx acquire:(BOOL)_flag {
389 if ((product = [self productWithName:_key]))
392 return [super lookupName:_key inContext:_ctx acquire:_flag];
395 - (NSArray *)toOneRelationshipKeys {
399 if ((root = [super toOneRelationshipKeys]) == nil)
400 return [self->products allKeys];
402 ma = [[NSMutableSet alloc] initWithArray:root];
403 [ma addObjectsFromArray:[self->products allKeys]];
404 root = [ma allObjects];
411 - (NSString *)loggingPrefix {
412 return @"[so-product-registry]";
414 - (BOOL)isDebuggingEnabled {
415 return debugOn ? YES : NO;
418 /* web representation */
420 - (void)appendToResponse:(WOResponse *)_r inContext:(WOContext *)_ctx {
424 [_r appendContentString:@"<h3>SOPE Product Registry</h3>"];
426 e = [[self toOneRelationshipKeys] objectEnumerator];
427 while ((name = [e nextObject])) {
428 [_r appendContentString:@"<li><a href=\""];
429 [_r appendContentHTMLAttributeValue:name];
430 [_r appendContentString:@"\">"];
431 [_r appendContentHTMLString:name];
432 [_r appendContentString:@"</a></li>"];
436 @end /* SoProductRegistry */