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
22 #include <NGObjWeb/WOComponentDefinition.h>
23 #include "WOComponent+private.h"
24 #include "WOComponentFault.h"
25 #include <NGObjWeb/WOAssociation.h>
26 #include <NGObjWeb/WOApplication.h>
27 #include <NGObjWeb/WOElement.h>
28 #include <NGObjWeb/WOResponse.h>
29 #include <NGObjWeb/WOResourceManager.h>
31 #include <EOControl/EOControl.h>
33 #include <NGObjWeb/WOTemplateBuilder.h>
35 static Class StrClass = Nil;
36 static Class DictClass = Nil;
37 static Class AssocClass = Nil;
38 static Class NumberClass = Nil;
39 static Class DateClass = Nil;
40 static NSNumber *yesNum = nil;
41 static NSNumber *noNum = nil;
43 @interface WOComponent(UsedPrivates)
44 - (void)setBaseURL:(id)_url;
47 @interface WONoContentElement : WOElement
49 WOComponentDefinition *cdef;
52 - (id)initWithElementName:(NSString *)_elementName
53 attributes:(NSDictionary *)_attributes
54 contentElements:(NSArray *)_subElements
55 componentDefinition:(WOComponentDefinition *)_cdef;
58 @interface _WOStaticHTMLElement : WOElement
62 - (id)initWithBuffer:(const char *)_buffer length:(unsigned)_len;
65 @interface WOComponentDefinition(PrivateMethods)
71 #include <NGObjWeb/WOContext.h>
72 #include <NGObjWeb/WORequest.h>
73 #include <NGObjWeb/WOSession.h>
78 WO's instantiation method is
79 - componentInstanceInContext:forComponentReference:
80 with the primary being
81 - _componentInstanceInContext:forComponentReference:
83 Maybe we should change to that. Currently this flow is a bit broken because
84 the resourcemanager sits in the middle, though I'm pretty sure that some
85 older WO used WOResourceManager just like we do in the moment.
88 @implementation WOComponent(InfoSetup)
90 - (Class)componentFaultClass {
91 return [WOComponentFault class];
94 - (NSMutableDictionary *)instantiateChildComponentsInTemplate:(WOTemplate *)_t
95 languages:(NSArray *)_languages
97 NSMutableDictionary *childComponents = nil;
98 WOResourceManager *_rm;
103 if ((tmpl = _t) == nil)
106 _rm = [[WOApplication application] resourceManager];
108 if ([tmpl hasSubcomponentInfos] == 0)
111 keys = [tmpl infoKeyEnumerator];
112 while ((key = [keys nextObject])) {
113 WOSubcomponentInfo *childInfo = nil;
114 WOComponentFault *child = nil;
116 childInfo = [tmpl subcomponentInfoForKey:key];
118 child = [[WOComponentFault alloc]
119 initWithResourceManager:nil //_rm
120 pageName:[childInfo componentName]
122 bindings:[childInfo bindings]];
125 if (childComponents == nil)
126 childComponents = [NSMutableDictionary dictionaryWithCapacity:16];
128 [childComponents setObject:child forKey:key];
134 @"Could not instantiate child fault %@, component: '%@'",
135 __PRETTY_FUNCTION__, key, [childInfo componentName]];
138 return childComponents;
141 - (id)initWithName:(NSString *)_cname
142 template:(WOTemplate *)_template
143 inContext:(WOContext *)_ctx
145 // Note: the _template can be nil and will then get looked up dynamically!
146 [self setName:_cname];
147 if ((self = [self initWithContext:_ctx])) {
148 NSMutableDictionary *childComponents;
151 langs = [self hasSession]
152 ? [[self session] languages]
153 : [[_ctx request] browserLanguages];
155 childComponents = [self instantiateChildComponentsInTemplate:_template
157 [self setSubComponents:childComponents];
158 [self setTemplate:_template];
163 - (id)initWithComponentDefinition:(WOComponentDefinition *)_cdef
164 inContext:(WOContext *)_ctx
168 We reuse the wocVariables ivar to pass over the component definition to
169 the component which will then call -_finishInitializingComponent: on the
170 definition for applying the .woo.
172 Sideeffects: if a component subclass uses extra vars prior calling
173 WOComponent -init, it will run into "issues".
175 NSAssert(self->wocVariables == nil,
176 @"extra variables dict is already set! cannot transfer component "
177 @"definition in that variable (use the HACK)");
178 self->wocVariables = (id)[_cdef retain];
180 return [self initWithName:[_cdef componentName]
181 template:[_cdef template]
185 @end /* WOComponent(InfoSetup) */
187 @implementation WOComponentDefinition
189 static BOOL debugOn = NO;
190 static BOOL profLoading = NO;
191 static BOOL enableClassLessComponents = NO;
192 static BOOL enableWOOFiles = NO;
193 static NSArray *woxExtensions = nil;
200 static BOOL isInitialized = NO;
202 if (isInitialized) return;
204 ud = [NSUserDefaults standardUserDefaults];
206 StrClass = [NSString class];
207 DictClass = [NSMutableDictionary class];
208 AssocClass = [WOAssociation class];
209 NumberClass = [NSNumber class];
210 DateClass = [NSDate class];
212 yesNum = [[NumberClass numberWithBool:YES] retain];
213 noNum = [[NumberClass numberWithBool:NO] retain];
215 profLoading = [[ud objectForKey:@"WOProfileLoading"] boolValue];
216 enableClassLessComponents =
217 [ud boolForKey:@"WOEnableComponentsWithoutClasses"];
218 enableWOOFiles = [ud boolForKey:@"WOComponentLoadWOOFiles"];
219 debugOn = [ud boolForKey:@"WODebugComponentDefinition"];
220 woxExtensions = [[ud arrayForKey:@"WOxFileExtensions"] copy];
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 = NSClassFromString(@"WOScriptedComponent");
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)];
501 - (WOTemplateBuilder *)templateBuilderForPath:(NSString *)_path {
504 if ([_path length] == 0)
507 ext = [_path pathExtension];
508 if ([woxExtensions containsObject:ext]) {
509 static WOTemplateBuilder *woxBuilder = nil;
510 if (woxBuilder == nil)
511 woxBuilder = [[NSClassFromString(@"WOxTemplateBuilder") alloc] init];
516 static WOTemplateBuilder *woBuilder = nil;
517 if (woBuilder == nil) {
519 [[NSClassFromString(@"WOWrapperTemplateBuilder") alloc] init];
525 - (WOTemplateBuilder *)templateBuilderForURL:(NSURL *)_url {
526 if ([_url isFileURL])
527 return [self templateBuilderForPath:[_url path]];
529 [self logWithFormat:@"only supports file URLs: %@", _url];
534 WOTemplateBuilder *builder;
537 if (self->path == nil)
538 /* a pathless component (a component without a template file) */
542 Note: the URL can either point directly to the .wo or .wox file entry or
543 it can point to a .lproj inside a .wo (eg Main.wo/English.lproj)
544 Note: actually the WOTemplateBuilder only supports file URLs in the moment,
545 it just checks the path extension to select the proper builder.
547 url = [self->path isKindOfClass:[NSURL class]]
548 ? (NSURL *)self->path
549 : [[[NSURL alloc] initFileURLWithPath:self->path] autorelease];
551 if (debugOn) [self debugWithFormat:@"url: %@", [url absoluteString]];
553 // TODO: maybe we should move the builder selection to the resource-manager
554 builder = [self templateBuilderForURL:url];
555 if (debugOn) [self debugWithFormat:@"builder: %@", builder];
557 self->template = [[builder buildTemplateAtURL:url] retain];
558 if (debugOn) [self debugWithFormat:@"template: %@", self->template];
560 return self->template ? YES : NO;
565 - (BOOL)isDebuggingEnabled {
571 - (NSString *)description {
572 NSMutableString *ms = [NSMutableString stringWithCapacity:64];
574 [ms appendFormat:@"<0x%08X[%@]:", self, NSStringFromClass([self class])];
576 if (self->name) [ms appendFormat:@" name=%@", self->name];
577 if (self->path) [ms appendFormat:@" path=%@", self->path];
578 if (self->baseUrl) [ms appendFormat:@" base=%@", self->baseUrl];
579 if (self->frameworkName)
580 [ms appendFormat:@" framework=%@", self->frameworkName];
582 if (!self->template) [ms appendString:@" no-template"];
583 [ms appendString:@">"];
587 @end /* WOComponentDefinition */
589 @implementation WONoContentElement
591 - (id)initWithElementName:(NSString *)_elementName
592 attributes:(NSDictionary *)_attributes
593 contentElements:(NSArray *)_subElements
594 componentDefinition:(WOComponentDefinition *)_cdef
596 self->cdef = [_cdef retain];
597 self->element = [_elementName copy];
602 [self->cdef release];
603 [self->element release];
607 - (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
608 [_response appendContentHTMLString:@"<<missing element '"];
609 [_response appendContentHTMLString:self->element];
610 [_response appendContentHTMLString:@"' in component '"];
611 [_response appendContentHTMLString:[self->cdef componentName]];
612 [_response appendContentHTMLString:@"'>>"];
615 @end /* WONoContentElement */
617 @implementation _WOStaticHTMLElement
619 - (id)initWithBuffer:(const char *)_buffer length:(unsigned)_len {
621 StrClass = [NSString class];
623 self->text = [[StrClass alloc] initWithCString:_buffer length:_len];
628 [self->text release];
632 - (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
634 [_response appendContentString:self->text];
637 @end /* _WOStaticHTMLElement */