2 Copyright (C) 2000-2005 SKYRIX Software AG
4 This file is part of SOPE.
6 SOPE 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 SOPE 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 SOPE; 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];
132 [self errorWithFormat:
133 @"(%s): Could not instantiate child fault %@, component: '%@'",
134 __PRETTY_FUNCTION__, key, [childInfo componentName]];
137 return childComponents;
140 - (id)initWithName:(NSString *)_cname
141 template:(WOTemplate *)_template
142 inContext:(WOContext *)_ctx
144 // Note: the _template can be nil and will then get looked up dynamically!
145 [self setName:_cname];
146 if ((self = [self initWithContext:_ctx])) {
147 NSMutableDictionary *childComponents;
150 langs = [self hasSession]
151 ? [[self session] languages]
152 : [[_ctx request] browserLanguages];
154 childComponents = [self instantiateChildComponentsInTemplate:_template
156 [self setSubComponents:childComponents];
157 [self setTemplate:_template];
162 - (id)initWithComponentDefinition:(WOComponentDefinition *)_cdef
163 inContext:(WOContext *)_ctx
167 We reuse the wocVariables ivar to pass over the component definition to
168 the component which will then call -_finishInitializingComponent: on the
169 definition for applying the .woo.
171 Sideeffects: if a component subclass uses extra vars prior calling
172 WOComponent -init, it will run into "issues".
174 NSAssert(self->wocVariables == nil,
175 @"extra variables dict is already set! cannot transfer component "
176 @"definition in that variable (use the HACK)");
177 self->wocVariables = (id)[_cdef retain];
179 return [self initWithName:[_cdef componentName]
180 template:[_cdef template]
184 @end /* WOComponent(InfoSetup) */
186 @implementation WOComponentDefinition
188 static BOOL debugOn = NO;
189 static BOOL profLoading = NO;
190 static BOOL enableClassLessComponents = NO;
191 static BOOL enableWOOFiles = NO;
192 static NSArray *woxExtensions = nil;
199 static BOOL isInitialized = NO;
201 if (isInitialized) return;
203 ud = [NSUserDefaults standardUserDefaults];
205 StrClass = [NSString class];
206 DictClass = [NSMutableDictionary class];
207 AssocClass = [WOAssociation class];
208 NumberClass = [NSNumber class];
209 DateClass = [NSDate class];
211 yesNum = [[NumberClass numberWithBool:YES] retain];
212 noNum = [[NumberClass numberWithBool:NO] retain];
214 profLoading = [[ud objectForKey:@"WOProfileLoading"] boolValue];
215 enableClassLessComponents =
216 [ud boolForKey:@"WOEnableComponentsWithoutClasses"];
217 enableWOOFiles = [ud boolForKey:@"WOComponentLoadWOOFiles"];
218 debugOn = [ud boolForKey:@"WODebugComponentDefinition"];
219 woxExtensions = [[ud arrayForKey:@"WOxFileExtensions"] copy];
222 - (id)initWithName:(NSString *)_name
223 path:(NSString *)_path
224 baseURL:(NSURL *)_baseUrl
225 frameworkName:(NSString *)_frameworkName
228 this method is usually called by WOResourceManager
229 (_definitionWithName:...)
231 if ((self = [super init])) {
233 'name' is the name of the component
234 'path' contains a string or a NSURL with the location of the directory
235 containing the component - TODO: explain who calculates that!
236 'baseURL' contains a URL like /AppName/FrameworkName/Component/Eng.lProj
237 (the external URL of the component, not sure whether this is
238 actually used somewhere)
240 NSZone *z = [self zone];
242 self->name = [_name copyWithZone:z];
243 self->path = [_path copyWithZone:z];
244 self->baseUrl = [_baseUrl copyWithZone:z];
245 self->frameworkName = [_frameworkName copyWithZone:z];
248 [self debugWithFormat:
249 @"init: '%@' path='%@'\n URL='%@'\n framework='%@'",
250 self->name, self->path, [self->baseUrl absoluteString],
251 self->frameworkName];
254 if (![self load]) { /* TODO: is this really required? */
263 [self errorWithFormat:@"called -init on WOComponentDefinition!"];
269 [self->template release];
270 [self->name release];
271 [self->path release];
272 [self->baseUrl release];
273 [self->frameworkName release];
279 - (Class)componentClassForScript:(WOComponentScript *)_script {
283 - (void)setComponentClass:(Class)_class {
284 self->componentClass = _class;
286 - (Class)componentClass {
287 if (self->componentClass == Nil)
288 self->componentClass = NSClassFromString(self->name);
290 if (self->componentClass != Nil)
291 return self->componentClass;
293 if (self->name == nil)
296 if ([self->name isAbsolutePath])
298 else if ([self->name rangeOfString:@"."].length > 0)
300 else if (enableClassLessComponents)
303 [self logWithFormat:@"Note: did not find component class with name '%@'",
308 - (NSString *)componentName {
312 - (WOTemplate *)template {
313 return self->template;
321 return self->baseUrl;
323 - (NSString *)frameworkName {
324 return self->frameworkName;
330 self->lastTouch = [DateClass timeIntervalSinceReferenceDate];
333 - (NSTimeInterval)lastTouch {
334 return self->lastTouch;
339 - (BOOL)_checkComponentClassValidity:(Class)cClass {
341 /* this make no sense, need -isSubclassOfClass: ..,
342 class instances are never isKindOfClass:WOElement ...
345 static Class WOElementClass = Nil;
346 if (WOElementClass == Nil) WOElementClass = [WOElement class];
347 if (![cClass isKindOfClass:WOElementClass] && cClass != nil) {
348 [self warnWithFormat:@"(%s:%i): "
349 @"component class %@ is not a subclass of WOElement !",
350 __PRETTY_FUNCTION__, __LINE__,
351 NSStringFromClass(cClass)];
358 - (BOOL)_checkComponentValidity:(id)component class:(Class)cClass {
359 if (![component isKindOfClass:cClass] && component != nil) {
360 [self warnWithFormat:@"(%s:%i): component %@ is not a subclass of "
361 @"component class %@ !",
362 __PRETTY_FUNCTION__, __LINE__,
363 component, NSStringFromClass(cClass)];
369 - (void)_applyWOOVariables:(NSDictionary *)_vars
370 onComponent:(WOComponent *)_component
372 EOKeyValueUnarchiver *unarchiver;
373 NSAutoreleasePool *pool;
377 pool = [[NSAutoreleasePool alloc] init];
380 [[[EOKeyValueUnarchiver alloc] initWithDictionary:_vars] autorelease];
381 [unarchiver setDelegate:_component];
383 keys = [_vars keyEnumerator];
384 while ((key = [keys nextObject])) {
387 object = [unarchiver decodeObjectForKey:key];
388 [_component takeValue:object forKey:key];
390 [unarchiver finishInitializationOfObjects];
391 [unarchiver awakeObjects];
396 - (void)_applyWOOVariablesOnComponent:(WOComponent *)_component {
398 Note: we still need this, as components are not required to load the
404 wooPath = [[_component path] stringByAppendingPathExtension:@"woo"];
405 if (![[NSFileManager defaultManager] fileExistsAtPath:wooPath])
408 if ((woo = [NSDictionary dictionaryWithContentsOfFile:wooPath]) == nil) {
409 [self errorWithFormat:@"could not load .woo-file: '%@'", wooPath];
413 [self _applyWOOVariables:[woo objectForKey:@"variables"]
414 onComponent:_component];
417 - (void)_finishInitializingComponent:(WOComponent *)_component {
419 [_component setBaseURL:self->baseUrl];
421 if (enableWOOFiles) {
422 if (self->template) {
423 [self _applyWOOVariables:
424 [self->template keyValueArchivedTemplateVariables]
425 onComponent:_component];
428 [self _applyWOOVariablesOnComponent:_component];
432 - (WOComponent *)instantiateWithResourceManager:(WOResourceManager *)_rm
433 languages:(NSArray *)_languages
435 WOComponent *component = nil;
437 WOComponentScript *script;
439 if ((script = [self->template componentScript]))
440 cClass = NSClassFromString(@"WOScriptedComponent");
442 cClass = [self componentClass];
447 if (enableClassLessComponents) {
448 [self debugWithFormat:@"Note: missing class for component: '%@'",
449 [self componentName]];
452 [self logWithFormat:@"Note: missing class for component: '%@'",
453 [self componentName]];
456 tmpPath = [self->name stringByAppendingPathExtension:@"html"];
457 tmpPath = [self->path stringByAppendingPathComponent:tmpPath];
459 if ([[NSFileManager defaultManager] fileExistsAtPath:tmpPath]) {
460 cClass = [WOComponent class];
463 [self debugWithFormat:@"Note: did not find .html template at path: '%@'",
468 if (![self _checkComponentClassValidity:cClass]) {
469 [self logWithFormat:@"Component Class '%@' is not valid.", cClass];
473 /* instantiate object (this will call _finishInitializingComponent) */
475 component = [[cClass alloc] initWithComponentDefinition:self
476 inContext:[[WOApplication application] context]];
477 component = [component autorelease];
478 if (component == nil)
484 [self _checkComponentValidity:component class:cClass];
487 if (![component isKindOfClass:cClass]) {
488 [self warnWithFormat:
489 @"(%s:%i): component '%@' is not a subclass of "
490 @"component class '%@' !",
491 __PRETTY_FUNCTION__, __LINE__,
492 component, NSStringFromClass(cClass)];
500 - (WOTemplateBuilder *)templateBuilderForPath:(NSString *)_path {
503 if ([_path length] == 0)
506 ext = [_path pathExtension];
507 if ([woxExtensions containsObject:ext]) {
508 static WOTemplateBuilder *woxBuilder = nil;
509 if (woxBuilder == nil)
510 woxBuilder = [[NSClassFromString(@"WOxTemplateBuilder") alloc] init];
515 static WOTemplateBuilder *woBuilder = nil;
516 if (woBuilder == nil) {
518 [[NSClassFromString(@"WOWrapperTemplateBuilder") alloc] init];
524 - (WOTemplateBuilder *)templateBuilderForURL:(NSURL *)_url {
525 if ([_url isFileURL])
526 return [self templateBuilderForPath:[_url path]];
528 [self logWithFormat:@"only supports file URLs: %@", _url];
533 WOTemplateBuilder *builder;
536 if (self->path == nil)
537 /* a pathless component (a component without a template file) */
541 Note: the URL can either point directly to the .wo or .wox file entry or
542 it can point to a .lproj inside a .wo (eg Main.wo/English.lproj)
543 Note: actually the WOTemplateBuilder only supports file URLs in the moment,
544 it just checks the path extension to select the proper builder.
546 url = [self->path isKindOfClass:[NSURL class]]
547 ? (NSURL *)self->path
548 : [[[NSURL alloc] initFileURLWithPath:self->path] autorelease];
550 if (debugOn) [self debugWithFormat:@"url: %@", [url absoluteString]];
552 // TODO: maybe we should move the builder selection to the resource-manager
553 builder = [self templateBuilderForURL:url];
554 if (debugOn) [self debugWithFormat:@"builder: %@", builder];
556 self->template = [[builder buildTemplateAtURL:url] retain];
557 if (debugOn) [self debugWithFormat:@"template: %@", self->template];
559 return self->template ? YES : NO;
564 - (BOOL)isDebuggingEnabled {
570 - (NSString *)description {
571 NSMutableString *ms = [NSMutableString stringWithCapacity:64];
573 [ms appendFormat:@"<0x%08X[%@]:", self, NSStringFromClass([self class])];
575 if (self->name) [ms appendFormat:@" name=%@", self->name];
576 if (self->path) [ms appendFormat:@" path=%@", self->path];
577 if (self->baseUrl) [ms appendFormat:@" base=%@", self->baseUrl];
578 if (self->frameworkName)
579 [ms appendFormat:@" framework=%@", self->frameworkName];
581 if (!self->template) [ms appendString:@" no-template"];
582 [ms appendString:@">"];
586 @end /* WOComponentDefinition */
588 @implementation WONoContentElement
590 - (id)initWithElementName:(NSString *)_elementName
591 attributes:(NSDictionary *)_attributes
592 contentElements:(NSArray *)_subElements
593 componentDefinition:(WOComponentDefinition *)_cdef
595 self->cdef = [_cdef retain];
596 self->element = [_elementName copy];
601 [self->cdef release];
602 [self->element release];
606 - (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
607 [_response appendContentHTMLString:@"<<missing element '"];
608 [_response appendContentHTMLString:self->element];
609 [_response appendContentHTMLString:@"' in component '"];
610 [_response appendContentHTMLString:[self->cdef componentName]];
611 [_response appendContentHTMLString:@"'>>"];
614 @end /* WONoContentElement */
616 @implementation _WOStaticHTMLElement
618 - (id)initWithBuffer:(const char *)_buffer length:(unsigned)_len {
620 StrClass = [NSString class];
622 self->text = [[StrClass alloc] initWithCString:_buffer length:_len];
627 [self->text release];
631 - (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
633 [_response appendContentString:self->text];
636 @end /* _WOStaticHTMLElement */