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 context] resourceLookupLanguages];
152 childComponents = [self instantiateChildComponentsInTemplate:_template
154 [self setSubComponents:childComponents];
155 [self setTemplate:_template];
160 - (id)initWithComponentDefinition:(WOComponentDefinition *)_cdef
161 inContext:(WOContext *)_ctx
165 We reuse the wocVariables ivar to pass over the component definition to
166 the component which will then call -_finishInitializingComponent: on the
167 definition for applying the .woo.
169 Sideeffects: if a component subclass uses extra vars prior calling
170 WOComponent -init, it will run into "issues".
172 NSAssert(self->wocVariables == nil,
173 @"extra variables dict is already set! cannot transfer component "
174 @"definition in that variable (use the HACK)");
175 self->wocVariables = (id)[_cdef retain];
177 return [self initWithName:[_cdef componentName]
178 template:[_cdef template]
182 @end /* WOComponent(InfoSetup) */
184 @implementation WOComponentDefinition
186 static BOOL debugOn = NO;
187 static BOOL profLoading = NO;
188 static BOOL enableClassLessComponents = NO;
189 static BOOL enableWOOFiles = NO;
190 static NSArray *woxExtensions = nil;
197 static BOOL isInitialized = NO;
199 if (isInitialized) return;
201 ud = [NSUserDefaults standardUserDefaults];
203 StrClass = [NSString class];
204 DictClass = [NSMutableDictionary class];
205 AssocClass = [WOAssociation class];
206 NumberClass = [NSNumber class];
207 DateClass = [NSDate class];
209 yesNum = [[NumberClass numberWithBool:YES] retain];
210 noNum = [[NumberClass numberWithBool:NO] retain];
212 profLoading = [[ud objectForKey:@"WOProfileLoading"] boolValue];
213 enableClassLessComponents =
214 [ud boolForKey:@"WOEnableComponentsWithoutClasses"];
215 enableWOOFiles = [ud boolForKey:@"WOComponentLoadWOOFiles"];
216 debugOn = [ud boolForKey:@"WODebugComponentDefinition"];
217 woxExtensions = [[ud arrayForKey:@"WOxFileExtensions"] copy];
220 - (id)initWithName:(NSString *)_name
221 path:(NSString *)_path
222 baseURL:(NSURL *)_baseUrl
223 frameworkName:(NSString *)_frameworkName
226 this method is usually called by WOResourceManager
227 (_definitionWithName:...)
229 if ((self = [super init])) {
231 'name' is the name of the component
232 'path' contains a string or a NSURL with the location of the directory
233 containing the component - TODO: explain who calculates that!
234 'baseURL' contains a URL like /AppName/FrameworkName/Component/Eng.lProj
235 (the external URL of the component, not sure whether this is
236 actually used somewhere)
238 NSZone *z = [self zone];
240 self->name = [_name copyWithZone:z];
241 self->path = [_path copyWithZone:z];
242 self->baseUrl = [_baseUrl copyWithZone:z];
243 self->frameworkName = [_frameworkName copyWithZone:z];
246 [self debugWithFormat:
247 @"init: '%@' path='%@'\n URL='%@'\n framework='%@'",
248 self->name, self->path, [self->baseUrl absoluteString],
249 self->frameworkName];
252 if (![self load]) { /* TODO: is this really required? */
261 [self errorWithFormat:@"called -init on WOComponentDefinition!"];
267 [self->template release];
268 [self->name release];
269 [self->path release];
270 [self->baseUrl release];
271 [self->frameworkName release];
277 - (Class)componentClassForScript:(WOComponentScript *)_script {
281 - (void)setComponentClass:(Class)_class {
282 self->componentClass = _class;
284 - (Class)componentClass {
285 if (self->componentClass == Nil)
286 self->componentClass = NSClassFromString(self->name);
288 if (self->componentClass != Nil)
289 return self->componentClass;
291 if (self->name == nil)
294 if ([self->name isAbsolutePath])
296 else if ([self->name rangeOfString:@"."].length > 0)
298 else if (enableClassLessComponents)
301 [self logWithFormat:@"Note: did not find component class with name '%@'",
306 - (NSString *)componentName {
310 - (WOTemplate *)template {
311 return self->template;
319 return self->baseUrl;
321 - (NSString *)frameworkName {
322 return self->frameworkName;
328 self->lastTouch = [DateClass timeIntervalSinceReferenceDate];
331 - (NSTimeInterval)lastTouch {
332 return self->lastTouch;
337 - (BOOL)_checkComponentClassValidity:(Class)cClass {
339 /* this make no sense, need -isSubclassOfClass: ..,
340 class instances are never isKindOfClass:WOElement ...
343 static Class WOElementClass = Nil;
344 if (WOElementClass == Nil) WOElementClass = [WOElement class];
345 if (![cClass isKindOfClass:WOElementClass] && cClass != nil) {
346 [self warnWithFormat:@"(%s:%i): "
347 @"component class %@ is not a subclass of WOElement !",
348 __PRETTY_FUNCTION__, __LINE__,
349 NSStringFromClass(cClass)];
356 - (BOOL)_checkComponentValidity:(id)component class:(Class)cClass {
357 if (![component isKindOfClass:cClass] && component != nil) {
358 [self warnWithFormat:@"(%s:%i): component %@ is not a subclass of "
359 @"component class %@ !",
360 __PRETTY_FUNCTION__, __LINE__,
361 component, NSStringFromClass(cClass)];
367 - (void)_applyWOOVariables:(NSDictionary *)_vars
368 onComponent:(WOComponent *)_component
370 EOKeyValueUnarchiver *unarchiver;
371 NSAutoreleasePool *pool;
375 pool = [[NSAutoreleasePool alloc] init];
378 [[[EOKeyValueUnarchiver alloc] initWithDictionary:_vars] autorelease];
379 [unarchiver setDelegate:_component];
381 keys = [_vars keyEnumerator];
382 while ((key = [keys nextObject])) {
385 object = [unarchiver decodeObjectForKey:key];
386 [_component takeValue:object forKey:key];
388 [unarchiver finishInitializationOfObjects];
389 [unarchiver awakeObjects];
394 - (void)_applyWOOVariablesOnComponent:(WOComponent *)_component {
396 Note: we still need this, as components are not required to load the
402 wooPath = [[_component path] stringByAppendingPathExtension:@"woo"];
403 if (![[NSFileManager defaultManager] fileExistsAtPath:wooPath])
406 if ((woo = [NSDictionary dictionaryWithContentsOfFile:wooPath]) == nil) {
407 [self errorWithFormat:@"could not load .woo-file: '%@'", wooPath];
411 [self _applyWOOVariables:[woo objectForKey:@"variables"]
412 onComponent:_component];
415 - (void)_finishInitializingComponent:(WOComponent *)_component {
417 [_component setBaseURL:self->baseUrl];
419 if (enableWOOFiles) {
420 if (self->template) {
421 [self _applyWOOVariables:
422 [self->template keyValueArchivedTemplateVariables]
423 onComponent:_component];
426 [self _applyWOOVariablesOnComponent:_component];
430 - (WOComponent *)instantiateWithResourceManager:(WOResourceManager *)_rm
431 languages:(NSArray *)_languages
433 WOComponent *component = nil;
435 WOComponentScript *script;
437 if ((script = [self->template componentScript]))
438 cClass = NSClassFromString(@"WOScriptedComponent");
440 cClass = [self componentClass];
445 if (enableClassLessComponents) {
446 [self debugWithFormat:@"Note: missing class for component: '%@'",
447 [self componentName]];
450 [self logWithFormat:@"Note: missing class for component: '%@'",
451 [self componentName]];
454 tmpPath = [self->name stringByAppendingPathExtension:@"html"];
455 tmpPath = [self->path stringByAppendingPathComponent:tmpPath];
457 if ([[NSFileManager defaultManager] fileExistsAtPath:tmpPath]) {
458 cClass = [WOComponent class];
461 [self debugWithFormat:@"Note: did not find .html template at path: '%@'",
466 if (![self _checkComponentClassValidity:cClass]) {
467 [self logWithFormat:@"Component Class '%@' is not valid.", cClass];
471 /* instantiate object (this will call _finishInitializingComponent) */
473 component = [[cClass alloc] initWithComponentDefinition:self
474 inContext:[[WOApplication application] context]];
475 component = [component autorelease];
476 if (component == nil)
482 [self _checkComponentValidity:component class:cClass];
485 if (![component isKindOfClass:cClass]) {
486 [self warnWithFormat:
487 @"(%s:%i): component '%@' is not a subclass of "
488 @"component class '%@' !",
489 __PRETTY_FUNCTION__, __LINE__,
490 component, NSStringFromClass(cClass)];
498 - (WOTemplateBuilder *)templateBuilderForPath:(NSString *)_path {
501 if ([_path length] == 0)
504 ext = [_path pathExtension];
505 if ([woxExtensions containsObject:ext]) {
506 static WOTemplateBuilder *woxBuilder = nil;
507 if (woxBuilder == nil)
508 woxBuilder = [[NSClassFromString(@"WOxTemplateBuilder") alloc] init];
513 static WOTemplateBuilder *woBuilder = nil;
514 if (woBuilder == nil) {
516 [[NSClassFromString(@"WOWrapperTemplateBuilder") alloc] init];
522 - (WOTemplateBuilder *)templateBuilderForURL:(NSURL *)_url {
523 if ([_url isFileURL])
524 return [self templateBuilderForPath:[_url path]];
526 [self logWithFormat:@"only supports file URLs: %@", _url];
531 WOTemplateBuilder *builder;
534 if (self->path == nil)
535 /* a pathless component (a component without a template file) */
539 Note: the URL can either point directly to the .wo or .wox file entry or
540 it can point to a .lproj inside a .wo (eg Main.wo/English.lproj)
541 Note: actually the WOTemplateBuilder only supports file URLs in the moment,
542 it just checks the path extension to select the proper builder.
544 url = [self->path isKindOfClass:[NSURL class]]
545 ? (NSURL *)self->path
546 : [[[NSURL alloc] initFileURLWithPath:self->path] autorelease];
548 if (debugOn) [self debugWithFormat:@"url: %@", [url absoluteString]];
550 // TODO: maybe we should move the builder selection to the resource-manager
551 builder = [self templateBuilderForURL:url];
552 if (debugOn) [self debugWithFormat:@"builder: %@", builder];
554 self->template = [[builder buildTemplateAtURL:url] retain];
555 if (debugOn) [self debugWithFormat:@"template: %@", self->template];
557 return self->template ? YES : NO;
562 - (BOOL)isDebuggingEnabled {
568 - (NSString *)description {
569 NSMutableString *ms = [NSMutableString stringWithCapacity:64];
571 [ms appendFormat:@"<0x%08X[%@]:", self, NSStringFromClass([self class])];
573 if (self->name) [ms appendFormat:@" name=%@", self->name];
574 if (self->path) [ms appendFormat:@" path=%@", self->path];
575 if (self->baseUrl) [ms appendFormat:@" base=%@", self->baseUrl];
576 if (self->frameworkName)
577 [ms appendFormat:@" framework=%@", self->frameworkName];
579 if (!self->template) [ms appendString:@" no-template"];
580 [ms appendString:@">"];
584 @end /* WOComponentDefinition */
586 @implementation WONoContentElement
588 - (id)initWithElementName:(NSString *)_elementName
589 attributes:(NSDictionary *)_attributes
590 contentElements:(NSArray *)_subElements
591 componentDefinition:(WOComponentDefinition *)_cdef
593 self->cdef = [_cdef retain];
594 self->element = [_elementName copy];
599 [self->cdef release];
600 [self->element release];
604 - (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
605 [_response appendContentHTMLString:@"<<missing element '"];
606 [_response appendContentHTMLString:self->element];
607 [_response appendContentHTMLString:@"' in component '"];
608 [_response appendContentHTMLString:[self->cdef componentName]];
609 [_response appendContentHTMLString:@"'>>"];
612 @end /* WONoContentElement */
614 @implementation _WOStaticHTMLElement
616 - (id)initWithBuffer:(const char *)_buffer length:(unsigned)_len {
618 StrClass = [NSString class];
620 self->text = [[StrClass alloc] initWithCString:_buffer length:_len];
625 [self->text release];
629 - (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
631 [_response appendContentString:self->text];
634 @end /* _WOStaticHTMLElement */