]> err.no Git - sope/blob - sope-appserver/NGObjWeb/SoObjects/SoProduct.m
use %p for pointer formats, fixed gcc 4.1 warnings, minor code improvs
[sope] / sope-appserver / NGObjWeb / SoObjects / SoProduct.m
1 /*
2   Copyright (C) 2002-2005 SKYRIX Software AG
3
4   This file is part of SOPE.
5
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
9   later version.
10
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.
15
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
19   02111-1307, USA.
20 */
21
22 #include "SoProduct.h"
23 #include "SoProductClassInfo.h"
24 #include "SoProductResourceManager.h"
25 #include "SoClassRegistry.h"
26 #include "SoClassSecurityInfo.h"
27 #include "SoObject.h"
28 #include "SoSecurityManager.h"
29 #include <NGObjWeb/WOApplication.h>
30 #include <NGObjWeb/WOResourceManager.h>
31 #include <NGObjWeb/WOResponse.h>
32 #include "common.h"
33
34 @interface SoProduct(Privates)
35 - (void)registerClassesFromDictionary:(NSDictionary *)_classToInfo;
36 - (void)registerCategoriesFromDictionary:(NSDictionary *)_classToInfo;
37 @end
38
39 @implementation SoProduct
40
41 static int debugOn     = 1;
42 static int regDebugOn  = 0;
43 static int loadDebugOn = 0;
44
45 + (void)initialize {
46   static BOOL didInit = NO;
47   if (!didInit) {
48     NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
49     didInit = YES;
50     regDebugOn  = [ud boolForKey:@"SoDebugProductRegistry"] ? 1 : 0;
51     loadDebugOn = [ud boolForKey:@"SoDebugProductLoading"] ? 1 : 0;
52   }
53 }
54
55 - (id)initWithDictionary:(NSDictionary *)_dict {
56   if ((self = [super init])) {
57     self->requiredProducts = [[_dict objectForKey:@"requires"] copy];
58     self->publicResources  = [[_dict objectForKey:@"publicResources"] copy];
59     
60     [self registerClassesFromDictionary:[_dict objectForKey:@"classes"]];
61     [self registerCategoriesFromDictionary:[_dict objectForKey:@"categories"]];
62   }
63   return self;
64 }
65
66 - (id)initWithBundle:(NSBundle *)_bundle {
67   NSString     *manifestPath;
68   NSDictionary *manifest;
69   
70   if (_bundle == nil) {
71     [self release];
72     return nil;
73   }
74   self->bundle = [_bundle retain];
75   manifestPath = [self->bundle pathForResource:@"product" ofType:@"plist"];
76   if ([manifestPath length] == 0) {
77     [self release];
78     return nil;
79   }
80   
81   manifest = [NSDictionary dictionaryWithContentsOfFile:manifestPath];
82   if (manifest == nil) {
83     [self logWithFormat:@"could not parse manifest: %@", manifestPath];
84     [self release];
85     return nil;
86   }
87   
88   self->resourceManager =
89     [[SoProductResourceManager alloc] initWithProduct:self];
90   if (self->resourceManager == nil)
91     [self logWithFormat:@"failed to instantiate resourcemanager for bundle"];
92   
93   return [self initWithDictionary:manifest];
94 }
95
96 - (void)dealloc {
97   [self->resourceManager detachFromContainer];
98   
99   [self->resourceManager  release];
100   [self->publicResources  release];
101   [self->requiredProducts release];
102   [self->categories       release];
103   [self->classes          release];
104   [self->bundle           release];
105   [super dealloc];
106 }
107
108 /* accessors */
109
110 - (NSArray *)requiredProducts {
111   return self->requiredProducts;
112 }
113
114 - (NSBundle *)bundle {
115   return self->bundle;
116 }
117
118 - (BOOL)isMainProduct {
119   if (self->bundle == nil) return YES;
120   if (self->bundle == [NSBundle mainBundle]) return YES;
121   return NO;
122 }
123
124 - (NSString *)productName {
125   if ([self isMainProduct])
126     return @"MAIN";
127   
128   return [[[self->bundle bundlePath]
129             lastPathComponent] stringByDeletingPathExtension];
130 }
131
132 - (BOOL)isPublicResource:(NSString *)_key {
133   return [self->publicResources containsObject:_key] ? YES : NO;
134 }
135
136 /* parsing manifest */
137
138 - (void)registerCategoryNamed:(NSString *)_name info:(NSDictionary *)_info {
139   SoProductCategoryInfo *catInfo;
140   
141   if (regDebugOn)
142     [self logWithFormat:@"  register category on '%@'", _name];
143   
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];
148     return;
149   }
150   if ([self->categories objectForKey:_name]) {
151     [self errorWithFormat:
152             @"duplicate declaration of category on '%@' in product.",
153             _name];
154     [catInfo release];
155     return;
156   }
157   
158   if (self->categories == nil)
159     self->categories = [[NSMutableDictionary alloc] init];
160   
161   [self->categories setObject:catInfo forKey:_name];
162   [catInfo autorelease];
163 }
164
165 - (void)registerClassNamed:(NSString *)_name info:(NSDictionary *)_info {
166   SoProductClassInfo *classInfo;
167   
168   if (regDebugOn)
169     [self logWithFormat:@"  register class: %@", _name];
170   
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];
175     return;
176   }
177   if ([self->classes objectForKey:_name]) {
178     [self errorWithFormat:@"duplicate declaration of class %@ in product "
179             @"(registering as category)",
180             _name];
181     [classInfo release];
182     [self registerCategoryNamed:_name info:_info];
183     return;
184   }
185   
186   if (self->classes == nil)
187     self->classes = [[NSMutableDictionary alloc] init];
188   
189   [self->classes setObject:classInfo forKey:_name];
190   [classInfo autorelease];
191 }
192
193 - (void)registerClassesFromDictionary:(NSDictionary *)_classToInfo {
194   NSEnumerator *names;
195   NSMutableSet *regClasses;
196   NSString *className;
197   
198   regClasses = [NSMutableSet setWithCapacity:16];
199   names = [_classToInfo keyEnumerator];
200   while ((className = [names nextObject])) {
201     NSDictionary *info;
202     
203     if ([regClasses containsObject:className])
204       continue;
205     
206     info = [_classToInfo objectForKey:className];
207     [self registerClassNamed:className info:info];
208     [regClasses addObject:className];
209   }
210 }
211 - (void)registerCategoriesFromDictionary:(NSDictionary *)_classToInfo {
212   NSEnumerator *names;
213   NSMutableSet *regCats;
214   NSString *className;
215   
216   regCats = [NSMutableSet setWithCapacity:16];
217   names = [_classToInfo keyEnumerator];
218   while ((className = [names nextObject])) {
219     NSDictionary *info;
220     
221     if ([regCats containsObject:className])
222       continue;
223     
224     info = [_classToInfo objectForKey:className];
225     [self registerCategoryNamed:className info:info];
226     [regCats addObject:className];
227   }
228 }
229
230 /* loading */
231
232 - (BOOL)load {
233   SoClassRegistry *registry;
234   
235   if (self->flags.isLoaded) {
236     if (loadDebugOn)
237       [self logWithFormat:@"product already loaded: %@", self];
238     return YES;
239   }
240   
241   if (loadDebugOn)
242     [self logWithFormat:@"loading product: %@", self];
243   self->flags.isLoaded = 1;
244   
245   /* check whether bundle is binary ! */
246   
247   if ((self->bundle != nil) && (self->bundle != [NSBundle mainBundle])) {
248     if (loadDebugOn) {
249       [self logWithFormat:@"  loading bundle of product: %@", 
250               [self->bundle bundlePath]];
251     }
252     
253     if (![self->bundle load]) {
254       if (loadDebugOn) [self logWithFormat:@"  failed to load bundle."];
255       return NO;
256     }
257     self->flags.isCodeLoaded = 1;
258   }
259   
260   registry = [SoClassRegistry sharedClassRegistry];
261   
262   if (loadDebugOn) {
263     [self logWithFormat:@"  registering %i classes ...", 
264             [self->classes count]];
265   }
266   
267   [[self->classes allValues] 
268     makeObjectsPerformSelector:@selector(applyOnRegistry:)
269     withObject:registry];
270   
271   if (loadDebugOn) {
272     [self logWithFormat:@"  registering %i categories ...", 
273             [self->categories count]];
274   }
275   
276   [[self->categories allValues] 
277     makeObjectsPerformSelector:@selector(applyOnRegistry:)
278     withObject:registry];
279   
280   if (loadDebugOn)
281     [self logWithFormat:@"done loading product."];
282   return YES;
283 }
284
285 - (BOOL)reloadIfPossible {
286   /* only possible if no product ObjC code is loaded */
287   if (self->flags.isCodeLoaded) return NO;
288   
289   return NO;
290 }
291
292 /* product as a SoObject */
293
294 - (NSString *)baseURLInContext:(id)_ctx {
295   /* Note: cannot use -stringByAppendingPathComponent: on OSX ! */
296   NSString *baseURL, *cname;
297   
298   baseURL = [self rootURLInContext:_ctx];
299   if (![baseURL hasSuffix:@"/"]) 
300     baseURL = [baseURL stringByAppendingString:@"/"];
301   
302   baseURL = [baseURL stringByAppendingString:@"ControlPanel/Products/"];
303   cname   = [[self productName] stringByEscapingURL];
304   baseURL = [baseURL stringByAppendingString:cname];
305   return baseURL;
306 }
307
308 - (NSArray *)allKeys {
309   return [NSArray arrayWithObject:@"Resources"];
310 }
311
312 - (BOOL)hasName:(NSString *)_key inContext:(id)_ctx {
313   if ([_key isEqualToString:@"Resources"])
314     return YES;
315   return [super hasName:_key inContext:_ctx];
316 }
317
318 - (id)lookupName:(NSString *)_key inContext:(id)_ctx acquire:(BOOL)_flag {
319   if ([_key isEqualToString:@"Resources"])
320     return [self resourceManager];
321   
322   return [super lookupName:_key inContext:_ctx acquire:_flag];
323 }
324
325 /* resource manager */
326
327 - (WOResourceManager *)resourceManager {
328   if ([self isMainProduct])
329     return [[WOApplication application] resourceManager];
330   
331   if (self->resourceManager == nil) {
332     [self warnWithFormat:@"resource-manager was nil ..."];
333     self->resourceManager =
334       [[SoProductResourceManager alloc] initWithProduct:self];
335   }
336   return self->resourceManager;
337 }
338
339 /* HTML representation */
340
341 - (void)appendToResponse:(WOResponse *)_response inContext:(id)_ctx {
342   [_response appendContentString:@"<h3>SOPE Product: "];
343   [_response appendContentHTMLString:[self productName]];
344   [_response appendContentString:@"</h3>"];
345   
346   [_response appendContentString:
347                @"<li><a href=\"Resources/\">Resources</a></li>"];
348 }
349
350 /* debugging */
351
352 - (NSString *)loggingPrefix {
353   return [NSString stringWithFormat:@"[so-product:%@]",
354                      [[[self bundle] bundlePath] lastPathComponent]];
355 }
356 - (BOOL)isDebuggingEnabled {
357   return debugOn ? YES : NO;
358 }
359
360 /* description */
361
362 - (NSString *)description {
363   NSMutableString *ms;
364   unsigned cnt;
365
366   ms = [NSMutableString stringWithCapacity:64];
367   [ms appendFormat:@"<0x%p[%@]:", self, NSStringFromClass([self class])];
368
369   if (self->flags.isLoaded)
370     [ms appendFormat:@" loaded"];
371   if (self->flags.isCodeLoaded)
372     [ms appendFormat:@" code-loaded"];
373
374   if (self->bundle)
375     [ms appendFormat:@" bundle=%@", [self->bundle bundlePath]];
376   
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];
383   
384   if (self->resourceManager)
385     [ms appendFormat:@" rm=0x%p", self->resourceManager];
386   
387   [ms appendString:@">"];
388   return ms;
389 }
390
391 @end /* SoProduct */