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
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 ((soClass = [_registry soClassWithName:[self className]]) == nil) {
226 @"ERROR: did not find exported SoClass '%@' in product %@!",
227 [self className], self->product];
231 security = [soClass soClassSecurityInfo];
233 [self debugWithFormat:@"loading info for class %@: %@",
234 [self className], soClass];
237 [self applyClassSecurity:security];
238 [self applySlotSecurity:security];
239 [self applySlotValues:soClass];
241 /* filename extensions for OFS */
242 [self applyExtensionsForSoClass:soClass onRegistry:_registry];
245 [self debugWithFormat:@"info for class %@ loaded.", [soClass className]];
250 - (BOOL)isDebuggingEnabled {
251 return debugOn ? YES : NO;
256 - (NSString *)description {
260 ms = [NSMutableString stringWithCapacity:64];
261 [ms appendFormat:@"<0x%08X[%@]:", self, NSStringFromClass([self class])];
263 [ms appendFormat:@" name=%@", self->className];
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];
272 [ms appendString:@">"];
276 @end /* SoProductSlotSetInfo */
278 @implementation SoProductClassInfo
282 - (NSString *)loggingPrefix {
283 return @"[so-class-info]";
286 @end /* SoProductClassInfo */
288 @implementation SoProductCategoryInfo
292 - (NSString *)loggingPrefix {
293 return @"[so-category-info]";
296 @end /* SoProductCategoryInfo */
298 @implementation SoProductSlotSetInfo(ManifestLoading)
300 - (BOOL)isPageInvocationManifest:(NSDictionary *)_m {
301 return [_m objectForKey:@"pageName"] ? YES : NO;
304 - (id)makePageInvocationForMethodNamed:(NSString *)_name
305 manifest:(NSDictionary *)_m
314 login = "loginRequest/auth/username/!textValue";
315 password = "loginRequest/auth/password/!textValue";
320 SoPageInvocation *method;
322 NSDictionary *argspecs;
324 pageName = [_m objectForKey:@"pageName"];
325 argspecs = [_m objectForKey:@"arguments"];
327 method = [[SoPageInvocation alloc]
328 initWithPageName:pageName
329 actionName:[_m objectForKey:@"actionName"]
330 product:self->product];
331 [method setArgumentSpecifications:argspecs];
335 - (id)makeInvocationForMethodNamed:(NSString *)_name selector:(id)_config {
336 SoSelectorInvocation *method;
338 if (_config == nil) {
340 @"ERROR: missing config for selector invocation method: '%@'",
345 if ([_config isKindOfClass:[NSString class]]) {
346 /* form: selector = "doItInContext:" */
347 method = [[SoSelectorInvocation alloc] initWithSelectorNamed:_config
348 addContextParameter:YES];
350 else if ([_config isKindOfClass:[NSDictionary class]]) {
354 name = "doItInContext:";
355 addContextParameter = YES;
356 // TODO: positionalArgumentBindings = ( );
357 // TODO: names (for mapping different argcounts)
360 "loginRequest/auth/username/!textValue",
361 "loginRequest/auth/password/!textValue"
366 NSDictionary *config;
367 NSDictionary *argspecs;
371 config = (NSDictionary *)_config;
373 selector = [config objectForKey:@"name"];
374 if ([selector length] == 0) {
376 @"ERROR: missing 'name' in selector config of method '%@': %@",
381 argspecs = [config objectForKey:@"arguments"];
383 ctxParameter = [[config objectForKey:@"addContextParameter"]boolValue];
385 method = [[SoSelectorInvocation alloc] init];
386 [method addSelectorNamed:selector];
387 [method setDoesAddContextParameter:ctxParameter];
388 [method setArgumentSpecifications:argspecs];
391 [self logWithFormat:@"ERROR: cannot handle selector configuration: %@",
398 - (id)cannotHandleManifest:(NSDictionary *)_m ofMethodNamed:(NSString *)_name {
399 /* no implementation provided */
402 note, a manifest does not need to contain the actual implementation
403 info, eg it can be used to just define the protections
406 @"Note: missing implemention info for method '%@' !", _name];
411 - (BOOL)_loadManifest:(NSDictionary *)_m ofMethodNamed:(NSString *)_name {
418 if ((mp = [_m objectForKey:@"protectedBy"]))
419 [self->slotProtections setObject:mp forKey:_name];
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];
428 method = [self cannotHandleManifest:_m ofMethodNamed:_name];
431 [self->slotValues setObject:method forKey:_name];
438 - (id)instantiateObjectOfClass:(Class)clazz withPlist:(id)value {
439 /* returns a retained instance */
441 if ([value isKindOfClass:[NSDictionary class]]) {
442 if ([clazz instancesRespondToSelector:@selector(initWithDictionary:)])
443 return [[clazz alloc] initWithDictionary:value];
445 else if ([value isKindOfClass:[NSArray class]]) {
446 if ([clazz instancesRespondToSelector:@selector(initWithArray:)])
447 return [[clazz alloc] initWithArray:value];
449 else if ([value isKindOfClass:[NSData class]]) {
450 if ([clazz instancesRespondToSelector:@selector(initWithData:)])
451 return [[clazz alloc] initWithData:value];
454 if ([clazz instancesRespondToSelector:@selector(initWithString:)])
455 return [[clazz alloc] initWithString:[value stringValue]];
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];
467 - (BOOL)_loadManifest:(NSDictionary *)_m ofSlotNamed:(NSString *)_name {
469 NSString *valueClassName;
475 if ((mp = [_m objectForKey:@"protectedBy"]))
476 [self->slotProtections setObject:mp forKey:_name];
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];
484 // TODO: should we allow/use SoClasses here?
485 if ((valueClass = NSClassFromString(valueClassName)) == Nil) {
487 @"ERROR: did not find value class '%@' for slot: '%@'",
488 valueClassName, _name];
495 if ((value = [_m objectForKey:@"value"])) {
497 value = [self instantiateObjectOfClass:valueClass withPlist:value];
501 @"ERROR: could not initialize value of slot %@ with class %@",
502 _name, valueClassName];
505 value = [value autorelease];
508 /* pass through property list */;
510 else if (valueClass) {
512 Note: a manifest does not need to contain the actual value, eg it can be
513 used to just define the protections
516 [self logWithFormat:@"Note: slot no value: '%@'", _name];
518 value = [[[valueClass alloc] init] autorelease];
521 @"ERROR: could not initialize value of slot '%@' with class: %@",
522 _name, valueClassName];
528 [self->slotValues setObject:value forKey:_name];
533 - (BOOL)_loadManifest:(NSDictionary *)_m {
538 self->protectedBy = [[_m objectForKey:@"protectedBy"] copy];
539 self->defaultAccess = [[_m objectForKey:@"defaultAccess"] copy];
540 self->roleInfo = [[_m objectForKey:@"defaultRoles"] copy];
542 self->exactFilenames = [[_m objectForKey:@"exactFilenames"] copy];
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]]
549 : [[NSArray alloc] initWithObjects:&tmp count:1];
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;
560 if (self->slotValues == nil)
561 self->slotValues = [[NSMutableDictionary alloc] init];
562 if (self->slotProtections == nil)
563 self->slotProtections = [[NSMutableDictionary alloc] init];
565 if ((slots = [_m objectForKey:@"methods"])) {
567 NSString *methodName;
569 names = [slots keyEnumerator];
570 while ((methodName = [names nextObject])) {
573 info = [slots objectForKey:methodName];
575 if (![self _loadManifest:info ofMethodNamed:methodName])
576 [self logWithFormat:@"manifest of method %@ is broken.", methodName];
579 if ((slots = [_m objectForKey:@"slots"])) {
583 names = [slots keyEnumerator];
584 while ((slotName = [names nextObject])) {
587 info = [slots objectForKey:slotName];
589 if (![self _loadManifest:info ofSlotNamed:slotName])
590 [self logWithFormat:@"manifest of slot %@ is broken.", slotName];
596 @end /* SoProductSlotSetInfo(ManifestLoading) */