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
21 // $Id: SoProductClassInfo.m 1 2004-08-20 10:08:27Z znek $
23 #include "SoProductClassInfo.h"
24 #include "SoPageInvocation.h"
25 #include "SoSelectorInvocation.h"
26 #include "SoClassSecurityInfo.h"
28 #include "SoClassRegistry.h"
29 #include "SoProduct.h"
32 static int debugOn = 1;
33 static int loadDebugOn = 0;
35 @interface SoProductSlotSetInfo(ManifestLoading)
36 - (BOOL)_loadManifest:(NSDictionary *)_m;
39 @interface SoProductSlotSetInfo(Privates)
43 @interface NSObject(PListInit)
44 - (id)initWithPropertyList:(id)_plist;
45 - (id)initWithPropertyList:(id)_plist owner:(id)_owner;
48 @implementation SoProductSlotSetInfo
51 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
52 static BOOL didInit = NO;
56 loadDebugOn = [ud boolForKey:@"SoDebugProductLoading"] ? 1 : 0;
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];
67 - (id)initWithName:(NSString *)_name manifest:(NSDictionary *)_dict
68 product:(SoProduct *)_product
70 if ((self = [super init])) {
71 self->product = _product; // non-retained
72 self->className = [_name copy];
73 [self _loadManifest:_dict];
79 [[self->slotValues allValues]
80 makeObjectsPerformSelector:@selector(detachFromContainer)];
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];
94 - (NSString *)className {
95 return self->className;
98 return NSClassFromString([self className]);
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];
110 [_security declareObjectProtected:self->protectedBy];
113 if (self->defaultAccess)
114 [_security setDefaultAccess:self->defaultAccess];
116 if (self->roleInfo) {
120 perms = [self->roleInfo keyEnumerator];
121 while ((perm = [perms nextObject])) {
122 NSString *role = [self->roleInfo objectForKey:perm];
124 [_security declareRole:role asDefaultForPermission:perm];
129 - (void)applySlotSecurity:(SoClassSecurityInfo *)_security {
133 names = [self->slotProtections keyEnumerator];
134 while ((slotName = [names nextObject])) {
137 if ((perm = [self->slotProtections objectForKey:slotName]))
138 [_security declareProtected:perm:slotName,nil];
142 - (void)applySlotValues:(SoClass *)_soClass {
147 [self debugWithFormat:@" applying %i slots on class %@ ...",
148 [self->slotValues count], [self className]];
151 names = [self->slotValues keyEnumerator];
152 while ((slotName = [names nextObject])) {
155 slot = [self->slotValues objectForKey:slotName];
160 [self debugWithFormat:@" register slot named %@ on class %@",
161 slotName, [_soClass className]];
164 /* if an implementation was provided, register it with the class */
166 if ([_soClass valueForSlot:slotName]) {
167 [self logWithFormat:@"WARNING: redefining slot '%@' of class '%@'",
171 [_soClass setValue:slot forSlot:slotName];
176 - (void)applyExtensionsForSoClass:(SoClass *)_soClass
177 onRegistry:(SoClassRegistry *)_registry
183 if (_soClass == nil) {
184 [self logWithFormat:@"ERROR(%s): missing soClass parameter?!",
185 __PRETTY_FUNCTION__];
188 if (_registry == nil) {
189 [self logWithFormat:@"ERROR: missing registry ?!"];
193 e = [self->extensions objectEnumerator];
194 while ((ext = [e nextObject])) {
195 if ((error = [_registry registerSoClass:_soClass forExtension:ext])) {
197 @"ERROR: failed to register class %@ for extension %@: %@",
198 [_soClass className], ext, error];
200 else if (loadDebugOn) {
201 [self debugWithFormat:@" registered class %@ for extension %@",
202 [_soClass className], ext];
206 e = [self->exactFilenames objectEnumerator];
207 while ((ext = [e nextObject])) {
208 if ((error = [_registry registerSoClass:_soClass forExactName:ext])) {
210 @"ERROR: failed to register class %@ for name %@: %@",
211 [_soClass className], ext, error];
213 else if (loadDebugOn) {
214 [self debugWithFormat:@" registered class %@ for name %@",
215 [_soClass className], ext];
220 - (void)applyOnRegistry:(SoClassRegistry *)_registry {
224 if (_registry == nil) {
225 [self logWithFormat:@"WARNING(%s): did not pass a registry?!",
226 __PRETTY_FUNCTION__];
230 if ((soClass = [_registry soClassWithName:[self className]]) == nil) {
232 @"ERROR: did not find exported SoClass '%@' in product %@!",
233 [self className], self->product];
237 security = [soClass soClassSecurityInfo];
239 [self debugWithFormat:@"loading info for class %@: %@",
240 [self className], soClass];
243 [self applyClassSecurity:security];
244 [self applySlotSecurity:security];
245 [self applySlotValues:soClass];
247 /* filename extensions for OFS */
248 [self applyExtensionsForSoClass:soClass onRegistry:_registry];
251 [self debugWithFormat:@"info for class %@ loaded.", [soClass className]];
256 - (BOOL)isDebuggingEnabled {
257 return debugOn ? YES : NO;
262 - (NSString *)description {
266 ms = [NSMutableString stringWithCapacity:64];
267 [ms appendFormat:@"<0x%08X[%@]:", self, NSStringFromClass([self class])];
269 [ms appendFormat:@" name=%@", self->className];
271 if ((cnt = [self->extensions count]) > 0)
272 [ms appendFormat:@" #extensions=%d", cnt];
273 if ((cnt = [self->slotValues count]) > 0)
274 [ms appendFormat:@" #slotvals=%d", cnt];
275 if ((cnt = [self->slotProtections count]) > 0)
276 [ms appendFormat:@" #slotperms=%d", cnt];
278 [ms appendString:@">"];
282 @end /* SoProductSlotSetInfo */
284 @implementation SoProductClassInfo
288 - (NSString *)loggingPrefix {
289 return @"[so-class-info]";
292 @end /* SoProductClassInfo */
294 @implementation SoProductCategoryInfo
298 - (NSString *)loggingPrefix {
299 return @"[so-category-info]";
302 @end /* SoProductCategoryInfo */
304 @implementation SoProductSlotSetInfo(ManifestLoading)
306 - (BOOL)isPageInvocationManifest:(NSDictionary *)_m {
307 return [_m objectForKey:@"pageName"] ? YES : NO;
310 - (id)makePageInvocationForMethodNamed:(NSString *)_name
311 manifest:(NSDictionary *)_m
320 login = "loginRequest/auth/username/!textValue";
321 password = "loginRequest/auth/password/!textValue";
326 SoPageInvocation *method;
328 NSDictionary *argspecs;
330 pageName = [_m objectForKey:@"pageName"];
331 argspecs = [_m objectForKey:@"arguments"];
333 method = [[SoPageInvocation alloc]
334 initWithPageName:pageName
335 actionName:[_m objectForKey:@"actionName"]
336 product:self->product];
337 [method setArgumentSpecifications:argspecs];
341 - (id)makeInvocationForMethodNamed:(NSString *)_name selector:(id)_config {
342 SoSelectorInvocation *method;
344 if (_config == nil) {
346 @"ERROR: missing config for selector invocation method: '%@'",
351 if ([_config isKindOfClass:[NSString class]]) {
352 /* form: selector = "doItInContext:" */
353 method = [[SoSelectorInvocation alloc] initWithSelectorNamed:_config
354 addContextParameter:YES];
356 else if ([_config isKindOfClass:[NSDictionary class]]) {
360 name = "doItInContext:";
361 addContextParameter = YES;
362 // TODO: positionalArgumentBindings = ( );
363 // TODO: names (for mapping different argcounts)
366 "loginRequest/auth/username/!textValue",
367 "loginRequest/auth/password/!textValue"
372 NSDictionary *config;
373 NSDictionary *argspecs;
377 config = (NSDictionary *)_config;
379 selector = [config objectForKey:@"name"];
380 if ([selector length] == 0) {
382 @"ERROR: missing 'name' in selector config of method '%@': %@",
387 argspecs = [config objectForKey:@"arguments"];
389 ctxParameter = [[config objectForKey:@"addContextParameter"]boolValue];
391 method = [[SoSelectorInvocation alloc] init];
392 [method addSelectorNamed:selector];
393 [method setDoesAddContextParameter:ctxParameter];
394 [method setArgumentSpecifications:argspecs];
397 [self logWithFormat:@"ERROR: cannot handle selector configuration: %@",
404 - (id)cannotHandleManifest:(NSDictionary *)_m ofMethodNamed:(NSString *)_name {
405 /* no implementation provided */
408 note, a manifest does not need to contain the actual implementation
409 info, eg it can be used to just define the protections
412 @"Note: missing implemention info for method '%@' !", _name];
417 - (BOOL)_loadManifest:(NSDictionary *)_m ofMethodNamed:(NSString *)_name {
424 if ((mp = [_m objectForKey:@"protectedBy"]))
425 [self->slotProtections setObject:mp forKey:_name];
429 if ([self isPageInvocationManifest:_m])
430 method = [self makePageInvocationForMethodNamed:_name manifest:_m];
431 else if ((selector = [_m objectForKey:@"selector"]))
432 method = [self makeInvocationForMethodNamed:_name selector:selector];
434 method = [self cannotHandleManifest:_m ofMethodNamed:_name];
437 [self->slotValues setObject:method forKey:_name];
444 - (id)instantiateObjectOfClass:(Class)clazz withPlist:(id)value {
445 /* returns a retained instance */
447 if ([value isKindOfClass:[NSDictionary class]]) {
448 if ([clazz instancesRespondToSelector:@selector(initWithDictionary:)])
449 return [[clazz alloc] initWithDictionary:value];
451 else if ([value isKindOfClass:[NSArray class]]) {
452 if ([clazz instancesRespondToSelector:@selector(initWithArray:)])
453 return [[clazz alloc] initWithArray:value];
455 else if ([value isKindOfClass:[NSData class]]) {
456 if ([clazz instancesRespondToSelector:@selector(initWithData:)])
457 return [[clazz alloc] initWithData:value];
460 if ([clazz instancesRespondToSelector:@selector(initWithString:)])
461 return [[clazz alloc] initWithString:[value stringValue]];
464 if ([clazz instancesRespondToSelector:
465 @selector(initWithPropertyList:owner:)])
466 return [[clazz alloc] initWithPropertyList:value owner:nil];
467 if ([clazz instancesRespondToSelector:@selector(initWithPropertyList:)])
468 return [[clazz alloc] initWithPropertyList:value];
473 - (BOOL)_loadManifest:(NSDictionary *)_m ofSlotNamed:(NSString *)_name {
475 NSString *valueClassName;
481 if ((mp = [_m objectForKey:@"protectedBy"]))
482 [self->slotProtections setObject:mp forKey:_name];
484 if ((valueClassName = [[_m objectForKey:@"valueClass"] stringValue])) {
485 // TODO: hack, we need to load the bundle of the product to have the
486 // contained classes available as valueClasses (But: shouldn't
487 // that be already done by NGBundleManager?)
488 [[self->product bundle] load];
490 // TODO: should we allow/use SoClasses here?
491 if ((valueClass = NSClassFromString(valueClassName)) == Nil) {
493 @"ERROR: did not find value class '%@' for slot: '%@'",
494 valueClassName, _name];
501 if ((value = [_m objectForKey:@"value"])) {
503 value = [self instantiateObjectOfClass:valueClass withPlist:value];
507 @"ERROR: could not initialize value of slot %@ with class %@",
508 _name, valueClassName];
511 value = [value autorelease];
514 /* pass through property list */;
516 else if (valueClass) {
518 Note: a manifest does not need to contain the actual value, eg it can be
519 used to just define the protections
522 [self logWithFormat:@"Note: slot no value: '%@'", _name];
524 value = [[[valueClass alloc] init] autorelease];
527 @"ERROR: could not initialize value of slot '%@' with class: %@",
528 _name, valueClassName];
534 [self->slotValues setObject:value forKey:_name];
539 - (BOOL)_loadManifest:(NSDictionary *)_m {
544 self->protectedBy = [[_m objectForKey:@"protectedBy"] copy];
545 self->defaultAccess = [[_m objectForKey:@"defaultAccess"] copy];
546 self->roleInfo = [[_m objectForKey:@"defaultRoles"] copy];
548 self->exactFilenames = [[_m objectForKey:@"exactFilenames"] copy];
550 self->extensions = [[_m objectForKey:@"extensions"] copy];
551 if ((tmp = [_m objectForKey:@"extension"])) {
552 if (self->extensions == nil) {
553 self->extensions = [tmp isKindOfClass:[NSArray class]]
555 : [[NSArray alloc] initWithObjects:&tmp count:1];
558 tmp = [tmp isKindOfClass:[NSArray class]]
559 ? [[self->extensions arrayByAddingObjectsFromArray:tmp] retain]
560 : [[self->extensions arrayByAddingObject:tmp] retain];
561 [self->extensions autorelease];
562 self->extensions = tmp;
566 if (self->slotValues == nil)
567 self->slotValues = [[NSMutableDictionary alloc] init];
568 if (self->slotProtections == nil)
569 self->slotProtections = [[NSMutableDictionary alloc] init];
571 if ((slots = [_m objectForKey:@"methods"])) {
573 NSString *methodName;
575 names = [slots keyEnumerator];
576 while ((methodName = [names nextObject])) {
579 info = [slots objectForKey:methodName];
581 if (![self _loadManifest:info ofMethodNamed:methodName])
582 [self logWithFormat:@"manifest of method %@ is broken.", methodName];
585 if ((slots = [_m objectForKey:@"slots"])) {
589 names = [slots keyEnumerator];
590 while ((slotName = [names nextObject])) {
593 info = [slots objectForKey:slotName];
595 if (![self _loadManifest:info ofSlotNamed:slotName])
596 [self logWithFormat:@"manifest of slot %@ is broken.", slotName];
602 @end /* SoProductSlotSetInfo(ManifestLoading) */