2 Copyright (C) 2000-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 "WOComponentDefinition.h"
24 #include "WOComponent+private.h"
25 #include "WOComponentFault.h"
26 #include "WOScriptedComponent.h"
27 #include <NGObjWeb/WOAssociation.h>
28 #include <NGObjWeb/WOApplication.h>
29 #include <NGObjWeb/WOElement.h>
30 #include <NGObjWeb/WOResponse.h>
31 #include <NGObjWeb/WOResourceManager.h>
33 #include <EOControl/EOControl.h>
35 #include <NGObjWeb/WOTemplateBuilder.h>
37 static Class StrClass = Nil;
38 static Class DictClass = Nil;
39 static Class AssocClass = Nil;
40 static Class NumberClass = Nil;
41 static Class DateClass = Nil;
42 static NSNumber *yesNum = nil;
43 static NSNumber *noNum = nil;
45 @interface WOComponent(UsedPrivates)
46 - (void)setBaseURL:(id)_url;
49 @interface WONoContentElement : WOElement
51 WOComponentDefinition *cdef;
54 - (id)initWithElementName:(NSString *)_elementName
55 attributes:(NSDictionary *)_attributes
56 contentElements:(NSArray *)_subElements
57 componentDefinition:(WOComponentDefinition *)_cdef;
60 @interface _WOStaticHTMLElement : WOElement
64 - (id)initWithBuffer:(const char *)_buffer length:(unsigned)_len;
67 @interface WOComponentDefinition(PrivateMethods)
73 #include <NGObjWeb/WOContext.h>
74 #include <NGObjWeb/WORequest.h>
75 #include <NGObjWeb/WOSession.h>
80 WO's instantiation method is
81 - componentInstanceInContext:forComponentReference:
82 with the primary being
83 - _componentInstanceInContext:forComponentReference:
85 Maybe we should change to that. Currently this flow is a bit broken because
86 the resourcemanager sits in the middle, though I'm pretty sure that some
87 older WO used WOResourceManager just like we do in the moment.
90 @implementation WOComponent(InfoSetup)
92 - (Class)componentFaultClass {
93 return [WOComponentFault class];
96 - (NSMutableDictionary *)instantiateChildComponentsInTemplate:(WOTemplate *)_t
97 languages:(NSArray *)_languages
99 NSMutableDictionary *childComponents = nil;
100 WOResourceManager *_rm;
105 if ((tmpl = _t) == nil)
108 _rm = [[WOApplication application] resourceManager];
110 if ([tmpl hasSubcomponentInfos] == 0)
113 keys = [tmpl infoKeyEnumerator];
114 while ((key = [keys nextObject])) {
115 WOSubcomponentInfo *childInfo = nil;
116 WOComponentFault *child = nil;
118 childInfo = [tmpl subcomponentInfoForKey:key];
120 child = [[WOComponentFault alloc]
121 initWithResourceManager:nil //_rm
122 pageName:[childInfo componentName]
124 bindings:[childInfo bindings]];
127 if (childComponents == nil)
128 childComponents = [NSMutableDictionary dictionaryWithCapacity:16];
130 [childComponents setObject:child forKey:key];
136 @"Could not instantiate child fault %@, component: '%@'",
137 __PRETTY_FUNCTION__, key, [childInfo componentName]];
140 return childComponents;
143 - (id)initWithName:(NSString *)_cname
144 template:(WOTemplate *)_template
145 inContext:(WOContext *)_ctx
147 // Note: the _template can be nil and will then get looked up dynamically!
148 [self setName:_cname];
149 if ((self = [self initWithContext:_ctx])) {
150 NSMutableDictionary *childComponents;
153 langs = [self hasSession]
154 ? [[self session] languages]
155 : [[_ctx request] browserLanguages];
157 childComponents = [self instantiateChildComponentsInTemplate:_template
159 [self setSubComponents:childComponents];
160 [self setTemplate:_template];
165 - (id)initWithComponentDefinition:(WOComponentDefinition *)_cdef
166 inContext:(WOContext *)_ctx
170 We reuse the wocVariables ivar to pass over the component definition to
171 the component which will then call -_finishInitializingComponent: on the
172 definition for applying the .woo.
174 Sideeffects: if a component subclass uses extra vars prior calling
175 WOComponent -init, it will run into "issues".
177 NSAssert(self->wocVariables == nil,
178 @"extra variables dict is already set! cannot transfer component "
179 @"definition in that variable (use the HACK)");
180 self->wocVariables = (id)[_cdef retain];
182 return [self initWithName:[_cdef componentName]
183 template:[_cdef template]
187 @end /* WOComponent(InfoSetup) */
189 @implementation WOComponentDefinition
191 static BOOL debugOn = NO;
192 static BOOL profLoading = NO;
193 static BOOL enableClassLessComponents = NO;
194 static BOOL enableWOOFiles = NO;
201 static BOOL isInitialized = NO;
203 if (isInitialized) return;
205 ud = [NSUserDefaults standardUserDefaults];
207 StrClass = [NSString class];
208 DictClass = [NSMutableDictionary class];
209 AssocClass = [WOAssociation class];
210 NumberClass = [NSNumber class];
211 DateClass = [NSDate class];
213 yesNum = [[NumberClass numberWithBool:YES] retain];
214 noNum = [[NumberClass numberWithBool:NO] retain];
216 profLoading = [[ud objectForKey:@"WOProfileLoading"] boolValue];
217 enableClassLessComponents =
218 [ud boolForKey:@"WOEnableComponentsWithoutClasses"];
219 enableWOOFiles = [ud boolForKey:@"WOComponentLoadWOOFiles"];
220 debugOn = [ud boolForKey:@"WODebugComponentDefinition"];
223 - (id)initWithName:(NSString *)_name
224 path:(NSString *)_path
225 baseURL:(NSURL *)_baseUrl
226 frameworkName:(NSString *)_frameworkName
229 this method is usually called by WOResourceManager
230 (_definitionWithName:...)
232 if ((self = [super init])) {
234 'name' is the name of the component
235 'path' contains a string or a NSURL with the location of the directory
236 containing the component - TODO: explain who calculates that!
237 'baseURL' contains a URL like /AppName/FrameworkName/Component/Eng.lProj
238 (the external URL of the component, not sure whether this is
239 actually used somewhere)
241 NSZone *z = [self zone];
243 self->name = [_name copyWithZone:z];
244 self->path = [_path copyWithZone:z];
245 self->baseUrl = [_baseUrl copyWithZone:z];
246 self->frameworkName = [_frameworkName copyWithZone:z];
249 [self debugWithFormat:
250 @"init: '%@' path='%@'\n URL='%@'\n framework='%@'",
251 self->name, self->path, [self->baseUrl absoluteString],
252 self->frameworkName];
255 if (![self load]) { /* TODO: is this really required? */
264 [self debugWithFormat:@"ERROR: called -init on WOComponentDefinition!"];
270 [self->template release];
271 [self->name release];
272 [self->path release];
273 [self->baseUrl release];
274 [self->frameworkName release];
280 - (Class)componentClassForScript:(WOComponentScript *)_script {
284 - (void)setComponentClass:(Class)_class {
285 self->componentClass = _class;
287 - (Class)componentClass {
288 if (self->componentClass == Nil)
289 self->componentClass = NSClassFromString(self->name);
291 if (self->componentClass != Nil)
292 return self->componentClass;
294 if (self->name == nil)
297 if ([self->name isAbsolutePath])
299 else if ([self->name rangeOfString:@"."].length > 0)
301 else if (enableClassLessComponents)
304 [self logWithFormat:@"Note: did not find component class with name '%@'",
309 - (NSString *)componentName {
313 - (WOTemplate *)template {
314 return self->template;
322 return self->baseUrl;
324 - (NSString *)frameworkName {
325 return self->frameworkName;
331 self->lastTouch = [DateClass timeIntervalSinceReferenceDate];
334 - (NSTimeInterval)lastTouch {
335 return self->lastTouch;
340 - (BOOL)_checkComponentClassValidity:(Class)cClass {
342 /* this make no sense, need -isSubclassOfClass: ..,
343 class instances are never isKindOfClass:WOElement ...
346 static Class WOElementClass = Nil;
347 if (WOElementClass == Nil) WOElementClass = [WOElement class];
348 if (![cClass isKindOfClass:WOElementClass] && cClass != nil) {
349 [self logWithFormat:@"WARNING(%s:%i): "
350 @"component class %@ is not a subclass of WOElement !",
351 __PRETTY_FUNCTION__, __LINE__,
352 NSStringFromClass(cClass)];
359 - (BOOL)_checkComponentValidity:(id)component class:(Class)cClass {
360 if (![component isKindOfClass:cClass] && component != nil) {
361 NSLog(@"WARNING2(%s:%i): component %@ is not a subclass of "
362 @"component class %@ !",
363 __PRETTY_FUNCTION__, __LINE__,
364 component, NSStringFromClass(cClass));
370 - (void)_applyWOOVariables:(NSDictionary *)_vars
371 onComponent:(WOComponent *)_component
373 EOKeyValueUnarchiver *unarchiver;
374 NSAutoreleasePool *pool;
378 pool = [[NSAutoreleasePool alloc] init];
381 [[[EOKeyValueUnarchiver alloc] initWithDictionary:_vars] autorelease];
382 [unarchiver setDelegate:_component];
384 keys = [_vars keyEnumerator];
385 while ((key = [keys nextObject])) {
388 object = [unarchiver decodeObjectForKey:key];
389 [_component takeValue:object forKey:key];
391 [unarchiver finishInitializationOfObjects];
392 [unarchiver awakeObjects];
397 - (void)_applyWOOVariablesOnComponent:(WOComponent *)_component {
399 Note: we still need this, as components are not required to load the
405 wooPath = [[_component path] stringByAppendingPathExtension:@"woo"];
406 if (![[NSFileManager defaultManager] fileExistsAtPath:wooPath])
409 if ((woo = [NSDictionary dictionaryWithContentsOfFile:wooPath]) == nil) {
410 [self logWithFormat:@"ERROR: could not load .woo-file: '%@'", wooPath];
414 [self _applyWOOVariables:[woo objectForKey:@"variables"]
415 onComponent:_component];
418 - (void)_finishInitializingComponent:(WOComponent *)_component {
420 [_component setBaseURL:self->baseUrl];
422 if (enableWOOFiles) {
423 if (self->template) {
424 [self _applyWOOVariables:
425 [self->template keyValueArchivedTemplateVariables]
426 onComponent:_component];
429 [self _applyWOOVariablesOnComponent:_component];
433 - (WOComponent *)instantiateWithResourceManager:(WOResourceManager *)_rm
434 languages:(NSArray *)_languages
436 WOComponent *component = nil;
438 WOComponentScript *script;
440 if ((script = [self->template componentScript]))
441 cClass = [WOScriptedComponent class];
443 cClass = [self componentClass];
448 if (enableClassLessComponents) {
449 [self debugWithFormat:@"Note: missing class for component: '%@'",
450 [self componentName]];
453 [self logWithFormat:@"Note: missing class for component: '%@'",
454 [self componentName]];
457 tmpPath = [self->name stringByAppendingPathExtension:@"html"];
458 tmpPath = [self->path stringByAppendingPathComponent:tmpPath];
460 if ([[NSFileManager defaultManager] fileExistsAtPath:tmpPath]) {
461 cClass = [WOComponent class];
464 [self debugWithFormat:@"Note: did not find .html template at path: '%@'",
469 if (![self _checkComponentClassValidity:cClass]) {
470 [self logWithFormat:@"Component Class '%@' is not valid.", cClass];
474 /* instantiate object (this will call _finishInitializingComponent) */
476 component = [[cClass alloc] initWithComponentDefinition:self
477 inContext:[[WOApplication application] context]];
478 component = [component autorelease];
479 if (component == nil)
485 [self _checkComponentValidity:component class:cClass];
488 if (![component isKindOfClass:cClass]) {
489 [self debugWithFormat:
490 @"WARNING(%s:%i): component '%@' is not a subclass of "
491 @"component class '%@' !",
492 __PRETTY_FUNCTION__, __LINE__,
493 component, NSStringFromClass(cClass)];
502 WOTemplateBuilder *builder;
505 if (self->path == nil)
506 /* a pathless component (a component without a template file) */
510 Note: the URL can either point directly to the .wo or .wox file entry or
511 it can point to a .lproj inside a .wo (eg Main.wo/English.lproj)
512 Note: actually the WOTemplateBuilder only supports file URLs in the moment,
513 it just checks the path extension to select the proper builder.
515 url = [self->path isKindOfClass:[NSURL class]]
516 ? (NSURL *)self->path
517 : [[[NSURL alloc] initFileURLWithPath:self->path] autorelease];
519 if (debugOn) [self debugWithFormat:@"url: %@", [url absoluteString]];
521 // TODO: maybe we should move the builder selection to the resource-manager
522 builder = [WOTemplateBuilder templateBuilderForURL:url];
523 if (debugOn) [self debugWithFormat:@"builder: %@", builder];
525 self->template = [[builder buildTemplateAtURL:url] retain];
526 if (debugOn) [self debugWithFormat:@"template: %@", self->template];
528 return self->template ? YES : NO;
533 - (BOOL)isDebuggingEnabled {
539 - (NSString *)description {
540 NSMutableString *ms = [NSMutableString stringWithCapacity:64];
542 [ms appendFormat:@"<0x%08X[%@]:", self, NSStringFromClass([self class])];
544 if (self->name) [ms appendFormat:@" name=%@", self->name];
545 if (self->path) [ms appendFormat:@" path=%@", self->path];
546 if (self->baseUrl) [ms appendFormat:@" base=%@", self->baseUrl];
547 if (self->frameworkName)
548 [ms appendFormat:@" framework=%@", self->frameworkName];
550 if (!self->template) [ms appendString:@" no-template"];
551 [ms appendString:@">"];
555 @end /* WOComponentDefinition */
557 @implementation WONoContentElement
559 - (id)initWithElementName:(NSString *)_elementName
560 attributes:(NSDictionary *)_attributes
561 contentElements:(NSArray *)_subElements
562 componentDefinition:(WOComponentDefinition *)_cdef
564 self->cdef = [_cdef retain];
565 self->element = [_elementName copy];
570 [self->cdef release];
571 [self->element release];
575 - (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
576 [_response appendContentHTMLString:@"<<missing element '"];
577 [_response appendContentHTMLString:self->element];
578 [_response appendContentHTMLString:@"' in component '"];
579 [_response appendContentHTMLString:[self->cdef componentName]];
580 [_response appendContentHTMLString:@"'>>"];
583 @end /* WONoContentElement */
585 @implementation _WOStaticHTMLElement
587 - (id)initWithBuffer:(const char *)_buffer length:(unsigned)_len {
589 StrClass = [NSString class];
591 self->text = [[StrClass alloc] initWithCString:_buffer length:_len];
596 [self->text release];
600 - (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
602 [_response appendContentString:self->text];
605 @end /* _WOStaticHTMLElement */