]> err.no Git - sope/blob - sope-appserver/NGObjWeb/SoObjects/SoProductClassInfo.m
renamed packages as discussed in the developer list
[sope] / sope-appserver / NGObjWeb / SoObjects / SoProductClassInfo.m
1 /*
2   Copyright (C) 2002-2004 SKYRIX Software AG
3
4   This file is part of OpenGroupware.org.
5
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
9   later version.
10
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.
15
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
19   02111-1307, USA.
20 */
21 // $Id$
22
23 #include "SoProductClassInfo.h"
24 #include "SoPageInvocation.h"
25 #include "SoSelectorInvocation.h"
26 #include "SoClassSecurityInfo.h"
27 #include "SoClass.h"
28 #include "SoClassRegistry.h"
29 #include "SoProduct.h"
30 #include "common.h"
31
32 static int debugOn     = 1;
33 static int loadDebugOn = 0;
34
35 @interface SoProductSlotSetInfo(ManifestLoading)
36 - (BOOL)_loadManifest:(NSDictionary *)_m;
37 @end
38
39 @interface SoProductSlotSetInfo(Privates)
40 - (void)reset;
41 @end
42
43 @interface NSObject(PListInit)
44 - (id)initWithPropertyList:(id)_plist;
45 - (id)initWithPropertyList:(id)_plist owner:(id)_owner;
46 @end
47
48 @implementation SoProductSlotSetInfo
49
50 + (void)initialize {
51   NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
52   static BOOL didInit = NO;
53   if (didInit) return;
54   didInit = YES;
55   
56   loadDebugOn = [ud boolForKey:@"SoDebugProductLoading"] ? 1 : 0;
57 }
58
59 - (void)reset {
60   [self->protectedBy   release]; self->protectedBy   = nil;
61   [self->defaultAccess release]; self->defaultAccess = nil;
62   [self->roleInfo      release]; self->roleInfo      = nil;
63   [self->slotValues      removeAllObjects];
64   [self->slotProtections removeAllObjects];
65 }
66
67 - (id)initWithName:(NSString *)_name manifest:(NSDictionary *)_dict
68   product:(SoProduct *)_product
69 {
70   if ((self = [super init])) {
71     self->product   = _product; // non-retained
72     self->className = [_name copy];
73     [self _loadManifest:_dict];
74   }
75   return self;
76 }
77
78 - (void)dealloc {
79   [[self->slotValues allValues]
80     makeObjectsPerformSelector:@selector(detachFromContainer)];
81   
82   [self->roleInfo        release];
83   [self->protectedBy     release];
84   [self->extensions      release];
85   [self->exactFilenames  release];
86   [self->className       release];
87   [self->slotValues      release];
88   [self->slotProtections release];
89   [super dealloc];
90 }
91
92 /* accessors */
93
94 - (NSString *)className {
95   return self->className;
96 }
97 - (Class)objcClass {
98   return NSClassFromString([self className]);
99 }
100
101 /* apply */
102
103 - (void)applyClassSecurity:(SoClassSecurityInfo *)_security {
104   if (self->protectedBy) {
105     if ([self->protectedBy isEqualToString:@"<public>"])
106       [_security declareObjectPublic];
107     else if ([self->protectedBy isEqualToString:@"<private>"])
108       [_security declareObjectPrivate];
109     else
110       [_security declareObjectProtected:self->protectedBy];
111   }
112   
113   if (self->defaultAccess)
114     [_security setDefaultAccess:self->defaultAccess];
115   
116   if (self->roleInfo) {
117     NSEnumerator *perms;
118     NSString *perm;
119     
120     perms = [self->roleInfo keyEnumerator];
121     while ((perm = [perms nextObject])) {
122       NSString *role = [self->roleInfo objectForKey:perm];
123       
124       [_security declareRole:role asDefaultForPermission:perm];
125     }
126   }
127 }
128
129 - (void)applySlotSecurity:(SoClassSecurityInfo *)_security {
130   NSEnumerator *names;
131   NSString *slotName;
132   
133   names = [self->slotProtections keyEnumerator];
134   while ((slotName = [names nextObject])) {
135     NSString *perm;
136     
137     if ((perm = [self->slotProtections objectForKey:slotName]))
138       [_security declareProtected:perm:slotName,nil];
139   }
140 }
141
142 - (void)applySlotValues:(SoClass *)_soClass {
143   NSEnumerator *names;
144   NSString *slotName;
145
146   if (loadDebugOn) {
147     [self debugWithFormat:@"  applying %i slots on class %@ ...", 
148             [self->slotValues count], [self className]];
149   }
150   
151   names = [self->slotValues keyEnumerator];
152   while ((slotName = [names nextObject])) {
153     id slot;
154     
155     slot = [self->slotValues objectForKey:slotName];
156     if (slot == nil)
157       continue;
158     
159     if (loadDebugOn) {
160       [self debugWithFormat:@"  register slot named %@ on class %@", 
161               slotName, [_soClass className]];
162     }
163     
164     /* if an implementation was provided, register it with the class */
165       
166     if ([_soClass valueForSlot:slotName]) {
167         [self logWithFormat:@"WARNING: redefining slot '%@' of class '%@'",
168               slotName, _soClass];
169     }
170         
171     [_soClass setValue:slot forSlot:slotName];
172     [slot release];
173   }
174 }
175
176 - (void)applyExtensionsForSoClass:(SoClass *)_soClass
177   onRegistry:(SoClassRegistry *)_registry
178 {
179   NSEnumerator *e;
180   NSString     *ext;
181   NSException *error;
182   
183   if (_soClass == nil) {
184     [self logWithFormat:@"ERROR(%s): missing soClass parameter?!",
185             __PRETTY_FUNCTION__];
186     return;
187   }
188   if (_registry == nil) {
189     [self logWithFormat:@"ERROR: missing registry ?!"];
190     return;
191   }
192   
193   e = [self->extensions objectEnumerator];
194   while ((ext = [e nextObject])) {
195     if ((error = [_registry registerSoClass:_soClass forExtension:ext])) {
196       [self logWithFormat:
197               @"ERROR: failed to register class %@ for extension %@: %@", 
198               [_soClass className], ext, error];
199     }
200     else if (loadDebugOn) {
201       [self debugWithFormat:@"  registered class %@ for extension %@", 
202               [_soClass className], ext];
203     }
204   }
205   
206   e = [self->exactFilenames objectEnumerator];
207   while ((ext = [e nextObject])) {
208     if ((error = [_registry registerSoClass:_soClass forExactName:ext])) {
209       [self logWithFormat:
210               @"ERROR: failed to register class %@ for name %@: %@", 
211               [_soClass className], ext, error];
212     }
213     else if (loadDebugOn) {
214       [self debugWithFormat:@"  registered class %@ for name %@", 
215               [_soClass className], ext];
216     }
217   }
218 }
219
220 - (void)applyOnRegistry:(SoClassRegistry *)_registry {
221   SoClass *soClass;
222   id security;
223   
224   if ((soClass = [_registry soClassWithName:[self className]]) == nil) {
225     [self logWithFormat:
226             @"ERROR: did not find exported SoClass '%@' in product %@!", 
227             [self className], self->product];
228     return;
229   }
230   
231   security = [soClass soClassSecurityInfo];
232   if (loadDebugOn) {
233     [self debugWithFormat:@"loading info for class %@: %@", 
234             [self className], soClass];
235   }
236   
237   [self applyClassSecurity:security];
238   [self applySlotSecurity:security];
239   [self applySlotValues:soClass];
240
241   /* filename extensions for OFS */
242   [self applyExtensionsForSoClass:soClass onRegistry:_registry];
243   
244   if (loadDebugOn)
245     [self debugWithFormat:@"info for class %@ loaded.", [soClass className]];
246 }
247
248 /* debugging */
249
250 - (BOOL)isDebuggingEnabled {
251   return debugOn ? YES : NO;
252 }
253
254 /* description */
255
256 - (NSString *)description {
257   NSMutableString *ms;
258   unsigned cnt;
259
260   ms = [NSMutableString stringWithCapacity:64];
261   [ms appendFormat:@"<0x%08X[%@]:", self, NSStringFromClass([self class])];
262
263   [ms appendFormat:@" name=%@", self->className];
264   
265   if ((cnt = [self->extensions count]) > 0)
266     [ms appendFormat:@" #extensions=%d", cnt];
267   if ((cnt = [self->slotValues count]) > 0)
268     [ms appendFormat:@" #slotvals=%d", cnt];
269   if ((cnt = [self->slotProtections count]) > 0)
270     [ms appendFormat:@" #slotperms=%d", cnt];
271   
272   [ms appendString:@">"];
273   return ms;
274 }
275
276 @end /* SoProductSlotSetInfo */
277
278 @implementation SoProductClassInfo
279
280 /* debugging */
281
282 - (NSString *)loggingPrefix {
283   return @"[so-class-info]";
284 }
285
286 @end /* SoProductClassInfo */
287
288 @implementation SoProductCategoryInfo
289
290 /* debugging */
291
292 - (NSString *)loggingPrefix {
293   return @"[so-category-info]";
294 }
295
296 @end /* SoProductCategoryInfo */
297
298 @implementation SoProductSlotSetInfo(ManifestLoading)
299
300 - (BOOL)isPageInvocationManifest:(NSDictionary *)_m {
301   return [_m objectForKey:@"pageName"] ? YES : NO;
302 }
303
304 - (id)makePageInvocationForMethodNamed:(NSString *)_name 
305   manifest:(NSDictionary *)_m 
306 {
307   /*
308     Page invocation:
309       {
310         pageName   = "Main";
311         actionName = "doIt";
312         arguments = {
313           SOAP = {
314            login    = "loginRequest/auth/username/!textValue";
315            password = "loginRequest/auth/password/!textValue";
316           };
317         }
318       }
319   */
320   SoPageInvocation *method;
321   NSString     *pageName;
322   NSDictionary *argspecs;
323   
324   pageName = [_m objectForKey:@"pageName"];
325   argspecs = [_m objectForKey:@"arguments"];
326   
327   method = [[SoPageInvocation alloc]
328              initWithPageName:pageName
329              actionName:[_m objectForKey:@"actionName"]
330              product:self->product];
331   [method setArgumentSpecifications:argspecs];
332   return method;
333 }
334
335 - (id)makeInvocationForMethodNamed:(NSString *)_name selector:(id)_config {
336   SoSelectorInvocation *method;
337   
338   if (_config == nil) {
339     [self logWithFormat:
340             @"ERROR: missing config for selector invocation method: '%@'",
341             _name];
342     return nil;
343   }
344   
345   if ([_config isKindOfClass:[NSString class]]) {
346     /* form: selector = "doItInContext:" */
347     method = [[SoSelectorInvocation alloc] initWithSelectorNamed:_config 
348                                            addContextParameter:YES];
349   }
350   else if ([_config isKindOfClass:[NSDictionary class]]) {
351     /*
352       Selector Invocation:
353         selector = {
354           name                = "doItInContext:";
355           addContextParameter = YES;
356           // TODO: positionalArgumentBindings = ( );
357           // TODO: names (for mapping different argcounts)
358           arguments = {
359             SOAP = (
360               "loginRequest/auth/username/!textValue",
361               "loginRequest/auth/password/!textValue"
362             );
363           }
364         };
365     */
366     NSDictionary *config;
367     NSDictionary *argspecs;
368     NSString     *selector;
369     BOOL         ctxParameter;
370
371     config = (NSDictionary *)_config;
372     
373     selector = [config objectForKey:@"name"];
374     if ([selector length] == 0) {
375       [self logWithFormat:
376               @"ERROR: missing 'name' in selector config of method '%@': %@",
377               _name, _config];
378       return nil;
379     }
380     
381     argspecs = [config objectForKey:@"arguments"];
382     
383     ctxParameter = [[config objectForKey:@"addContextParameter"]boolValue];
384     
385     method = [[SoSelectorInvocation alloc] init];
386     [method addSelectorNamed:selector];
387     [method setDoesAddContextParameter:ctxParameter];
388     [method setArgumentSpecifications:argspecs];
389   }
390   else {
391     [self logWithFormat:@"ERROR: cannot handle selector configuration: %@",
392             _config];
393     return nil;
394   }
395   return method;
396 }
397
398 - (id)cannotHandleManifest:(NSDictionary *)_m ofMethodNamed:(NSString *)_name {
399   /* no implementation provided */
400   if (loadDebugOn) {
401     /* 
402        note, a manifest does not need to contain the actual implementation
403        info, eg it can be used to just define the protections
404     */
405     [self logWithFormat:
406             @"Note: missing implemention info for method '%@' !", _name];
407   }
408   return nil;
409 }
410
411 - (BOOL)_loadManifest:(NSDictionary *)_m ofMethodNamed:(NSString *)_name {
412   NSString *mp;
413   NSString *selector;
414   id       method;
415   
416   /* security */
417   
418   if ((mp = [_m objectForKey:@"protectedBy"]))
419     [self->slotProtections setObject:mp forKey:_name];
420   
421   /* implementation */
422   
423   if ([self isPageInvocationManifest:_m])
424     method = [self makePageInvocationForMethodNamed:_name manifest:_m];
425   else if ((selector = [_m objectForKey:@"selector"]))
426     method = [self makeInvocationForMethodNamed:_name selector:selector];
427   else
428     method = [self cannotHandleManifest:_m ofMethodNamed:_name];
429   
430   if (method) {
431     [self->slotValues setObject:method forKey:_name];
432     [method release];
433   }
434   
435   return YES;
436 }
437
438 - (id)instantiateObjectOfClass:(Class)clazz withPlist:(id)value {
439   /* returns a retained instance */
440   
441   if ([value isKindOfClass:[NSDictionary class]]) {
442     if ([clazz instancesRespondToSelector:@selector(initWithDictionary:)])
443       return [[clazz alloc] initWithDictionary:value];
444   }
445   else if ([value isKindOfClass:[NSArray class]]) {
446     if ([clazz instancesRespondToSelector:@selector(initWithArray:)])
447       return [[clazz alloc] initWithArray:value];
448   }
449   else if ([value isKindOfClass:[NSData class]]) {
450     if ([clazz instancesRespondToSelector:@selector(initWithData:)])
451       return [[clazz alloc] initWithData:value];
452   }
453   else {
454     if ([clazz instancesRespondToSelector:@selector(initWithString:)])
455       return [[clazz alloc] initWithString:[value stringValue]];
456   }
457   
458   if ([clazz instancesRespondToSelector:
459                @selector(initWithPropertyList:owner:)])
460     return [[clazz alloc] initWithPropertyList:value owner:nil];
461   if ([clazz instancesRespondToSelector:@selector(initWithPropertyList:)])
462     return [[clazz alloc] initWithPropertyList:value];
463   
464   return nil;
465 }
466
467 - (BOOL)_loadManifest:(NSDictionary *)_m ofSlotNamed:(NSString *)_name {
468   NSString *mp;
469   NSString *valueClassName;
470   Class    valueClass;
471   id value;
472   
473   /* security */
474   
475   if ((mp = [_m objectForKey:@"protectedBy"]))
476     [self->slotProtections setObject:mp forKey:_name];
477   
478   if ((valueClassName = [[_m objectForKey:@"valueClass"] stringValue])) {
479     // TODO: hack, we need to load the bundle of the product to have the
480     //       contained classes available as valueClasses (But: shouldn't
481     //       that be already done by NGBundleManager?)
482     [[self->product bundle] load];
483     
484     // TODO: should we allow/use SoClasses here?
485     if ((valueClass = NSClassFromString(valueClassName)) == Nil) {
486       [self logWithFormat:
487               @"ERROR: did not find value class '%@' for slot: '%@'",
488               valueClassName, _name];
489       return NO;
490     }
491   }
492   else
493     valueClass = Nil;
494   
495   if ((value = [_m objectForKey:@"value"])) {
496     if (valueClass) {
497       value = [self instantiateObjectOfClass:valueClass withPlist:value];
498       
499       if (value == nil) {
500         [self logWithFormat:
501                 @"ERROR: could not initialize value of slot %@ with class %@",
502                 _name, valueClassName];
503         return NO;
504       }
505       value = [value autorelease];
506     }
507     else
508       /* pass through property list */;
509   }
510   else if (valueClass) {
511     /* 
512        Note: a manifest does not need to contain the actual value, eg it can be
513        used to just define the protections
514     */
515     if (loadDebugOn) 
516       [self logWithFormat:@"Note: slot no value: '%@'", _name];
517     
518     value = [[[valueClass alloc] init] autorelease];
519     if (value == nil) {
520       [self logWithFormat:
521               @"ERROR: could not initialize value of slot '%@' with class: %@",
522               _name, valueClassName];
523       return NO;
524     }
525   }
526   
527   if (value)
528     [self->slotValues setObject:value forKey:_name];
529   
530   return YES;
531 }
532
533 - (BOOL)_loadManifest:(NSDictionary *)_m {
534   NSDictionary *slots;
535   id tmp;
536   
537   [self reset];
538   self->protectedBy   = [[_m objectForKey:@"protectedBy"] copy];
539   self->defaultAccess = [[_m objectForKey:@"defaultAccess"] copy];
540   self->roleInfo      = [[_m objectForKey:@"defaultRoles"] copy];
541   
542   self->exactFilenames = [[_m objectForKey:@"exactFilenames"] copy];
543   
544   self->extensions = [[_m objectForKey:@"extensions"] copy];
545   if ((tmp = [_m objectForKey:@"extension"])) {
546     if (self->extensions == nil) {
547       self->extensions = [tmp isKindOfClass:[NSArray class]]
548         ? [tmp copy]
549         : [[NSArray alloc] initWithObjects:&tmp count:1];
550     }
551     else {
552       tmp = [tmp isKindOfClass:[NSArray class]]
553         ? [[self->extensions arrayByAddingObjectsFromArray:tmp] retain]
554         : [[self->extensions arrayByAddingObject:tmp] retain];
555       [self->extensions autorelease];
556       self->extensions = tmp;
557     }
558   }
559   
560   if (self->slotValues == nil)
561     self->slotValues = [[NSMutableDictionary alloc] init];
562   if (self->slotProtections == nil)
563     self->slotProtections = [[NSMutableDictionary alloc] init];
564   
565   if ((slots = [_m objectForKey:@"methods"])) {
566     NSEnumerator *names;
567     NSString *methodName;
568     
569     names = [slots keyEnumerator];
570     while ((methodName = [names nextObject])) {
571       NSDictionary *info;
572       
573       info = [slots objectForKey:methodName];
574       
575       if (![self _loadManifest:info ofMethodNamed:methodName])
576         [self logWithFormat:@"manifest of method %@ is broken.", methodName];
577     }
578   }
579   if ((slots = [_m objectForKey:@"slots"])) {
580     NSEnumerator *names;
581     NSString *slotName;
582     
583     names = [slots keyEnumerator];
584     while ((slotName = [names nextObject])) {
585       NSDictionary *info;
586       
587       info = [slots objectForKey:slotName];
588       
589       if (![self _loadManifest:info ofSlotNamed:slotName])
590         [self logWithFormat:@"manifest of slot %@ is broken.", slotName];
591     }
592   }
593   return YES;
594 }
595
596 @end /* SoProductSlotSetInfo(ManifestLoading) */