2 Copyright (C) 2002-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 "SoProduct.h"
23 #include "SoProductClassInfo.h"
24 #include "SoProductResourceManager.h"
25 #include "SoClassRegistry.h"
26 #include "SoClassSecurityInfo.h"
28 #include "SoSecurityManager.h"
29 #include <NGObjWeb/WOApplication.h>
30 #include <NGObjWeb/WOResourceManager.h>
31 #include <NGObjWeb/WOResponse.h>
34 @interface SoProduct(Privates)
35 - (void)registerClassesFromDictionary:(NSDictionary *)_classToInfo;
36 - (void)registerCategoriesFromDictionary:(NSDictionary *)_classToInfo;
39 @implementation SoProduct
41 static int debugOn = 1;
42 static int regDebugOn = 0;
43 static int loadDebugOn = 0;
46 static BOOL didInit = NO;
48 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
50 regDebugOn = [ud boolForKey:@"SoDebugProductRegistry"] ? 1 : 0;
51 loadDebugOn = [ud boolForKey:@"SoDebugProductLoading"] ? 1 : 0;
55 - (id)initWithDictionary:(NSDictionary *)_dict {
56 if ((self = [super init])) {
57 self->requiredProducts = [[_dict objectForKey:@"requires"] copy];
58 self->publicResources = [[_dict objectForKey:@"publicResources"] copy];
60 [self registerClassesFromDictionary:[_dict objectForKey:@"classes"]];
61 [self registerCategoriesFromDictionary:[_dict objectForKey:@"categories"]];
66 - (id)initWithBundle:(NSBundle *)_bundle {
67 NSString *manifestPath;
68 NSDictionary *manifest;
74 self->bundle = [_bundle retain];
75 manifestPath = [self->bundle pathForResource:@"product" ofType:@"plist"];
76 if ([manifestPath length] == 0) {
81 manifest = [NSDictionary dictionaryWithContentsOfFile:manifestPath];
82 if (manifest == nil) {
83 [self logWithFormat:@"could not parse manifest: %@", manifestPath];
88 self->resourceManager =
89 [[SoProductResourceManager alloc] initWithProduct:self];
90 if (self->resourceManager == nil)
91 [self logWithFormat:@"failed to instantiate resourcemanager for bundle"];
93 return [self initWithDictionary:manifest];
97 [self->resourceManager detachFromContainer];
99 [self->resourceManager release];
100 [self->publicResources release];
101 [self->requiredProducts release];
102 [self->categories release];
103 [self->classes release];
104 [self->bundle release];
110 - (NSArray *)requiredProducts {
111 return self->requiredProducts;
114 - (NSBundle *)bundle {
118 - (BOOL)isMainProduct {
119 if (self->bundle == nil) return YES;
120 if (self->bundle == [NSBundle mainBundle]) return YES;
124 - (NSString *)productName {
125 if ([self isMainProduct])
128 return [[[self->bundle bundlePath]
129 lastPathComponent] stringByDeletingPathExtension];
132 - (BOOL)isPublicResource:(NSString *)_key {
133 return [self->publicResources containsObject:_key] ? YES : NO;
136 /* parsing manifest */
138 - (void)registerCategoryNamed:(NSString *)_name info:(NSDictionary *)_info {
139 SoProductCategoryInfo *catInfo;
142 [self logWithFormat:@" register category on '%@'", _name];
144 catInfo = [[SoProductCategoryInfo alloc]
145 initWithName:_name manifest:_info product:self];
146 if (catInfo == nil) {
147 [self logWithFormat:@" could not init category info for '%@'", _name];
150 if ([self->categories objectForKey:_name]) {
151 [self errorWithFormat:
152 @"duplicate declaration of category on '%@' in product.",
158 if (self->categories == nil)
159 self->categories = [[NSMutableDictionary alloc] init];
161 [self->categories setObject:catInfo forKey:_name];
162 [catInfo autorelease];
165 - (void)registerClassNamed:(NSString *)_name info:(NSDictionary *)_info {
166 SoProductClassInfo *classInfo;
169 [self logWithFormat:@" register class: %@", _name];
171 classInfo = [[SoProductClassInfo alloc]
172 initWithName:_name manifest:_info product:self];
173 if (classInfo == nil) {
174 [self debugWithFormat:@" could not init class info for '%@'", _name];
177 if ([self->classes objectForKey:_name]) {
178 [self errorWithFormat:@"duplicate declaration of class %@ in product "
179 @"(registering as category)",
182 [self registerCategoryNamed:_name info:_info];
186 if (self->classes == nil)
187 self->classes = [[NSMutableDictionary alloc] init];
189 [self->classes setObject:classInfo forKey:_name];
190 [classInfo autorelease];
193 - (void)registerClassesFromDictionary:(NSDictionary *)_classToInfo {
195 NSMutableSet *regClasses;
198 regClasses = [NSMutableSet setWithCapacity:16];
199 names = [_classToInfo keyEnumerator];
200 while ((className = [names nextObject])) {
203 if ([regClasses containsObject:className])
206 info = [_classToInfo objectForKey:className];
207 [self registerClassNamed:className info:info];
208 [regClasses addObject:className];
211 - (void)registerCategoriesFromDictionary:(NSDictionary *)_classToInfo {
213 NSMutableSet *regCats;
216 regCats = [NSMutableSet setWithCapacity:16];
217 names = [_classToInfo keyEnumerator];
218 while ((className = [names nextObject])) {
221 if ([regCats containsObject:className])
224 info = [_classToInfo objectForKey:className];
225 [self registerCategoryNamed:className info:info];
226 [regCats addObject:className];
233 SoClassRegistry *registry;
235 if (self->flags.isLoaded) {
237 [self logWithFormat:@"product already loaded: %@", self];
242 [self logWithFormat:@"loading product: %@", self];
243 self->flags.isLoaded = 1;
245 /* check whether bundle is binary ! */
247 if ((self->bundle != nil) && (self->bundle != [NSBundle mainBundle])) {
249 [self logWithFormat:@" loading bundle of product: %@",
250 [self->bundle bundlePath]];
253 if (![self->bundle load]) {
254 if (loadDebugOn) [self logWithFormat:@" failed to load bundle."];
257 self->flags.isCodeLoaded = 1;
260 registry = [SoClassRegistry sharedClassRegistry];
263 [self logWithFormat:@" registering %i classes ...",
264 [self->classes count]];
267 [[self->classes allValues]
268 makeObjectsPerformSelector:@selector(applyOnRegistry:)
269 withObject:registry];
272 [self logWithFormat:@" registering %i categories ...",
273 [self->categories count]];
276 [[self->categories allValues]
277 makeObjectsPerformSelector:@selector(applyOnRegistry:)
278 withObject:registry];
281 [self logWithFormat:@"done loading product."];
285 - (BOOL)reloadIfPossible {
286 /* only possible if no product ObjC code is loaded */
287 if (self->flags.isCodeLoaded) return NO;
292 /* product as a SoObject */
294 - (NSString *)baseURLInContext:(id)_ctx {
295 /* Note: cannot use -stringByAppendingPathComponent: on OSX ! */
296 NSString *baseURL, *cname;
298 baseURL = [self rootURLInContext:_ctx];
299 if (![baseURL hasSuffix:@"/"])
300 baseURL = [baseURL stringByAppendingString:@"/"];
302 baseURL = [baseURL stringByAppendingString:@"ControlPanel/Products/"];
303 cname = [[self productName] stringByEscapingURL];
304 baseURL = [baseURL stringByAppendingString:cname];
308 - (NSArray *)allKeys {
309 return [NSArray arrayWithObject:@"Resources"];
312 - (BOOL)hasName:(NSString *)_key inContext:(id)_ctx {
313 if ([_key isEqualToString:@"Resources"])
315 return [super hasName:_key inContext:_ctx];
318 - (id)lookupName:(NSString *)_key inContext:(id)_ctx acquire:(BOOL)_flag {
319 if ([_key isEqualToString:@"Resources"])
320 return [self resourceManager];
322 return [super lookupName:_key inContext:_ctx acquire:_flag];
325 /* resource manager */
327 - (WOResourceManager *)resourceManager {
328 if ([self isMainProduct])
329 return [[WOApplication application] resourceManager];
331 if (self->resourceManager == nil) {
332 [self warnWithFormat:@"resource-manager was nil ..."];
333 self->resourceManager =
334 [[SoProductResourceManager alloc] initWithProduct:self];
336 return self->resourceManager;
339 /* HTML representation */
341 - (void)appendToResponse:(WOResponse *)_response inContext:(id)_ctx {
342 [_response appendContentString:@"<h3>SOPE Product: "];
343 [_response appendContentHTMLString:[self productName]];
344 [_response appendContentString:@"</h3>"];
346 [_response appendContentString:
347 @"<li><a href=\"Resources/\">Resources</a></li>"];
352 - (NSString *)loggingPrefix {
353 return [NSString stringWithFormat:@"[so-product:%@]",
354 [[[self bundle] bundlePath] lastPathComponent]];
356 - (BOOL)isDebuggingEnabled {
357 return debugOn ? YES : NO;
362 - (NSString *)description {
366 ms = [NSMutableString stringWithCapacity:64];
367 [ms appendFormat:@"<0x%p[%@]:", self, NSStringFromClass([self class])];
369 if (self->flags.isLoaded)
370 [ms appendFormat:@" loaded"];
371 if (self->flags.isCodeLoaded)
372 [ms appendFormat:@" code-loaded"];
375 [ms appendFormat:@" bundle=%@", [self->bundle bundlePath]];
377 if ((cnt = [self->classes count]) > 0)
378 [ms appendFormat:@" #classes=%d", cnt];
379 if ((cnt = [self->categories count]) > 0)
380 [ms appendFormat:@" #categories=%d", cnt];
381 if ((cnt = [self->publicResources count]) > 0)
382 [ms appendFormat:@" #pubrsrc=%d", cnt];
384 if (self->resourceManager)
385 [ms appendFormat:@" rm=0x%p", self->resourceManager];
387 [ms appendString:@">"];