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/WOxElemBuilder.h>
24 #include <SaxObjC/XMLNamespaces.h>
25 #include <NGObjWeb/WOApplication.h>
26 #include <NGObjWeb/WOElement.h>
27 #include <NGObjWeb/WOAssociation.h>
28 #include <NGObjWeb/WOComponentScript.h>
29 #include "WOComponentFault.h"
32 @interface WOElement(UsedPrivates)
33 - (id)initWithValue:(id)_value escapeHTML:(BOOL)_flag;
36 @interface WOAssociation(misc)
37 - (id)initWithScript:(NSString *)_script language:(NSString *)_lang;
40 @implementation WOxElemBuilderComponentInfo
42 - (id)initWithComponentId:(NSString *)_cid
43 componentName:(NSString *)_name
44 bindings:(NSMutableDictionary *)_bindings
46 self->cid = [_cid copy];
47 self->pageName = [_name copy];
48 self->bindings = [_bindings retain];
53 [self->pageName release];
54 [self->bindings release];
60 - (NSString *)componentId {
64 - (NSString *)pageName {
65 return self->pageName;
68 - (NSMutableDictionary *)bindings {
69 return self->bindings;
74 - (WOComponent *)instantiateWithResourceManager:(WOResourceManager *)_rm
75 languages:(NSArray *)_languages
77 static Class FaultClass = Nil;
78 WOComponentFault *fault;
80 if (FaultClass == Nil)
81 FaultClass = [WOComponentFault class];
83 fault = [FaultClass alloc];
84 NSAssert1(fault, @"couldn't allocated object of class '%@' ..", FaultClass);
86 fault = [fault initWithResourceManager:_rm
87 pageName:self->pageName
89 bindings:self->bindings];
93 @end /* SxElementBuilderComponentInfo */
95 @implementation WOxElemBuilder
97 static Class StrClass = Nil;
98 static Class AStrClass = Nil;
99 static NSDictionary *defaultAssocMap = nil;
100 static Class ValAssoc = Nil;
101 static BOOL logAssocMap = NO;
102 static BOOL logAssocCreation = NO;
103 static BOOL debugOn = NO;
109 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
110 static BOOL didInit = NO;
114 StrClass = NSClassFromString(@"_WOSimpleStaticString");
116 NSLog(@"ERROR: missing class _WOSimpleStaticString !");
117 AStrClass = NSClassFromString(@"_WOSimpleStaticASCIIString");
118 if (AStrClass == Nil)
119 NSLog(@"ERROR: missing class _WOSimpleStaticASCIIString !");
121 logAssocMap = [ud boolForKey:@"WOxElemBuilder_LogAssociationMapping"];
123 [ud boolForKey:@"WOxElemBuilder_LogAssociationCreation"];
124 if (logAssocMap) NSLog(@"Note: association mapping is logged!");
125 if (logAssocCreation) NSLog(@"Note: association creation is logged!");
127 defaultAssocMap = [[ud dictionaryForKey:@"WOxAssociationClassMapping"] copy];
128 if (defaultAssocMap == nil)
129 NSLog(@"WARNING: WOxAssociationClassMapping default is not set!");
132 ValAssoc = NSClassFromString(@"WOValueAssociation");
135 + (WOxElemBuilder *)createBuilderQueue:(NSArray *)_classNames {
137 WOxElemBuilder *first, *current = nil;
138 NSMutableArray *missingBuilders = nil;
140 if ((count = [_classNames count]) == 0)
143 for (first = nil, i = 0; i < count; i++) {
148 cn = [_classNames objectAtIndex:i];
150 if ((clazz = NSClassFromString(cn)) == Nil) {
151 if (missingBuilders == nil)
152 missingBuilders = [NSMutableArray arrayWithCapacity:16];
153 [missingBuilders addObject:cn];
157 if ((nx = [[clazz alloc] init])) {
159 first = current = nx;
163 [current setNextBuilder:nx];
164 current = [nx autorelease];
168 NSLog(@"%s: couldn't allocate builder (class=%@)", cn);
173 if (missingBuilders) {
174 NSLog(@"WOxElemBuilder: could not locate builders: %@",
175 [missingBuilders componentsJoinedByString:@","]);
180 + (WOxElemBuilder *)createBuilderQueueV:(NSString *)_className, ... {
181 // TODO: reimplement using createBuilderQueue:
184 WOxElemBuilder *first, *current;
186 if (_className == nil)
187 return [[[self alloc] init] autorelease];
189 first = [[[NSClassFromString(_className) alloc] init] autorelease];
191 va_start(ap, _className);
192 for (current = first; (cn = va_arg(ap, id)); ) {
195 nx = [[NSClassFromString(cn) alloc] init];
196 [current setNextBuilder:nx];
197 current = [nx autorelease];
205 [self->script release];
206 [self->subcomponentInfos release];
207 [self->nsToAssoc release];
208 [self->nextBuilder release];
212 /* building an element (returns a retained object !!!) */
214 - (WOElement *)buildNode:(id<DOMNode>)_node templateBuilder:(id)_builder {
218 switch ([_node nodeType]) {
219 case DOM_ELEMENT_NODE:
220 return [self buildElement:(id<DOMElement>)_node
221 templateBuilder:_builder];
223 return [self buildText:(id<DOMText>)_node
224 templateBuilder:_builder];
225 case DOM_CDATA_SECTION_NODE:
226 return [self buildCDATASection:(id<DOMCDATASection>)_node
227 templateBuilder:_builder];
228 case DOM_COMMENT_NODE:
229 return [self buildComment:(id<DOMComment>)_node
230 templateBuilder:_builder];
231 case DOM_DOCUMENT_NODE:
232 return [self buildDocument:(id<DOMDocument>)_node
233 templateBuilder:_builder];
236 if (self->nextBuilder)
237 return [self->nextBuilder buildNode:_node templateBuilder:_builder];
239 NSLog(@"unknown node type %i, node %@", [_node nodeType], _node);
245 - (NSArray *)buildNodes:(id<DOMNodeList>)_nodes templateBuilder:(id)_bld {
246 // Note: returns a regular autoreleased array
247 NSMutableArray *children;
250 if ((count = [_nodes length]) == 0)
253 children = [NSMutableArray arrayWithCapacity:(count + 1)];
255 for (i = 0; i < count; i++) {
258 e = [_bld buildNode:[_nodes objectAtIndex:i] templateBuilder:_bld];
260 [children addObject:e];
267 /* building methods specialized on type (return retained objects !!!) */
269 - (WOElement *)buildDocument:(id<DOMDocument>)_node templateBuilder:(id)_bld {
270 return [self buildElement:[_node documentElement] templateBuilder:_bld];
273 - (WOElement *)buildElement:(id<DOMElement>)_node templateBuilder:(id)_bld {
274 if (self->nextBuilder)
275 return [self->nextBuilder buildElement:_node templateBuilder:_bld];
277 [self logWithFormat:@"cannot build node %@ (template builder %@)",
282 - (WOElement *)buildCharacterData:(id<DOMCharacterData>)_text
283 templateBuilder:(id)_builder
285 static Class ValClass = Nil;
286 WOElement *textElement;
292 if ((len = [str length]) == 0) return nil;
295 we use WOValueAssociation directly, because WOAssociation caches all
299 ValClass = NSClassFromString(@"WOValueAssociation");
302 # warning not using ASCII string !
306 // TODO(perf): improve on that
307 /* not very efficient, but only used during template parsing ... */
308 if ([str dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:NO])
314 isASCII = ([str characterAtIndex:0] < 128) ? YES : NO;
318 str = [[ValClass alloc] initWithString:str];
320 [[(isASCII?AStrClass:StrClass) alloc] initWithValue:str escapeHTML:YES];
324 - (WOElement *)buildText:(id<DOMText>)_node
325 templateBuilder:(id)_builder
327 return [self buildCharacterData:_node templateBuilder:_builder];
329 - (WOElement *)buildCDATASection:(id<DOMCDATASection>)_node
330 templateBuilder:(id)_builder
332 return [self buildCharacterData:_node templateBuilder:_builder];
335 - (WOElement *)buildComment:(id<DOMComment>)_node
336 templateBuilder:(id)_builder
338 /* comments aren't delivered ... */
342 /* building the whole template */
344 - (WOElement *)buildTemplateFromDocument:(id<DOMDocument>)_document {
345 NSAutoreleasePool *pool;
348 pool = [[NSAutoreleasePool alloc] init];
349 result = [[self buildNode:_document templateBuilder:self] retain];
351 return [result autorelease];
354 /* association callbacks */
356 - (WOAssociation *)associationForValue:(id)_value {
357 return [WOAssociation associationWithValue:_value];
360 - (WOAssociation *)associationForKeyPath:(NSString *)_path {
361 return [WOAssociation associationWithKeyPath:_path];
364 - (WOAssociation *)associationForJavaScript:(NSString *)_js {
365 WOAssociation *assoc;
367 assoc = [NSClassFromString(@"WOScriptAssociation") alloc];
368 assoc = [(id)assoc initWithScript:_js language:@"javascript"];
369 return [assoc autorelease];
372 - (WOAssociation *)associationForAttribute:(id<DOMAttr>)_attribute {
375 WOAssociation *assoc;
378 nsuri = [_attribute namespaceURI];
379 value = [_attribute nodeValue];
381 c = [self associationClassForNamespaceURI:[_attribute namespaceURI]];
384 @"WARNING, found no association class for "
385 @"attribute %@ (namespace=%@)",
386 _attribute, [_attribute namespaceURI]];
390 [self logWithFormat:@"use class %@ for namespaceURI %@ (attribute %@)",
391 c, [_attribute namespaceURI], [_attribute name]];
394 assoc = [[c alloc] initWithString:value];
395 if (logAssocCreation) {
396 [self logWithFormat:@"created assoc %@ for attribute %@",
397 assoc, [_attribute name]];
400 return [assoc autorelease];
403 - (NSMutableDictionary *)associationsForAttributes:(id<DOMNamedNodeMap>)_attrs{
404 NSMutableDictionary *assocs;
407 if ((count = [_attrs length]) == 0)
410 assocs = [NSMutableDictionary dictionaryWithCapacity:(count + 1)];
412 for (i = 0; i < count; i++) {
414 WOAssociation *assoc;
416 attr = [_attrs objectAtIndex:i];
418 if ((assoc = [self associationForAttribute:attr])) {
422 if ([key characterAtIndex:0] == '_')
423 key = [@"?" stringByAppendingString:[key substringFromIndex:1]];
425 [assocs setObject:assoc forKey:key];
431 - (void)_ensureDefaultAssocMappings {
438 self->nsToAssoc = [[NSMutableDictionary alloc] initWithCapacity:8];
439 e = [defaultAssocMap keyEnumerator];
440 while ((ns = [e nextObject])) {
444 className = [defaultAssocMap objectForKey:ns];
445 clazz = NSClassFromString(className);
448 [self logWithFormat:@"WARNING: did not find association class: '%@'",
454 [self->nsToAssoc setObject:clazz forKey:ns];
457 - (void)registerAssociationClass:(Class)_class forNamespaceURI:(NSString *)_ns{
458 if (_ns == nil) return;
459 if (_class == Nil) return;
461 [self _ensureDefaultAssocMappings];
462 [self->nsToAssoc setObject:_class forKey:_ns];
464 - (Class)associationClassForNamespaceURI:(NSString *)_ns {
467 [self _ensureDefaultAssocMappings];
469 if ((c = [self->nsToAssoc objectForKey:_ns]) == nil)
470 /* if we have no class mapped for a namespace, we treat it as a value */
474 [self debugWithFormat:@"using class %@ for namespace %@", c, _ns];
478 /* creating unique IDs */
480 - (NSString *)uniqueIDForNode:(id)_node {
481 NSMutableArray *nodePath;
482 NSMutableString *uid;
483 NSEnumerator *topDown;
487 if (_node == nil) return nil;
489 nodePath = [NSMutableArray arrayWithCapacity:16];
491 /* collect all parent nodes in bottom-up form */
493 for (node = _node; node; node = [node parentNode])
494 [nodePath addObject:node];
498 uid = [NSMutableString stringWithCapacity:64];
499 topDown = [nodePath reverseObjectEnumerator];
503 for (isFirst = YES; (node = [topDown nextObject]); parent = node) {
508 [uid appendString:@"."];
510 /* determine index of _node */
512 children = (NSArray *)[parent childNodes];
513 for (i = 0, count = [children count]; i < count; i++) {
514 if ([children objectAtIndex:i] == node)
517 [uid appendFormat:@"%d", i];
520 [uid appendString:@"R"];
525 return [[uid copy] autorelease];
530 - (void)logWithFormat:(NSString *)_format, ... {
531 NSString *value = nil;
534 va_start(ap, _format);
535 value = [[NSString alloc] initWithFormat:_format arguments:ap];
538 NSLog(@"|%@| %@", self, value);
541 - (void)debugWithFormat:(NSString *)_format, ... {
542 static char showDebug = 2;
543 NSString *value = nil;
546 if (showDebug == 2) {
547 showDebug = [WOApplication isDebuggingEnabled] ? 1 : 0;
551 va_start(ap, _format);
552 value = [[NSString alloc] initWithFormat:_format arguments:ap];
555 NSLog(@"|%@|D %@", self, value);
560 /* managing builder queues */
562 - (void)setNextBuilder:(WOxElemBuilder *)_builder {
563 ASSIGN(self->nextBuilder, _builder);
565 - (WOxElemBuilder *)nextBuilder {
566 return self->nextBuilder;
569 /* component script parts */
571 - (void)addComponentScriptPart:(WOComponentScriptPart *)_part {
572 if (self->script == nil)
573 self->script = [[WOComponentScript alloc] init];
575 [self->script addScriptPart:_part];
577 - (void)addComponentScript:(NSString *)_script line:(unsigned)_line {
578 WOComponentScriptPart *part;
580 part = [[WOComponentScriptPart alloc] initWithURL:nil startLine:_line
582 [self addComponentScriptPart:part];
586 - (WOComponentScript *)componentScript {
590 /* subcomponent registry, created during parsing ... */
592 - (void)registerSubComponentWithId:(NSString *)_cid
593 componentName:(NSString *)_name
594 bindings:(NSMutableDictionary *)_bindings
596 WOxElemBuilderComponentInfo *info;
598 info = [[WOxElemBuilderComponentInfo alloc] initWithComponentId:_cid
602 if (self->subcomponentInfos == nil)
603 self->subcomponentInfos = [[NSMutableArray alloc] initWithCapacity:16];
604 [self->subcomponentInfos addObject:info];
608 - (NSArray *)subcomponentInfos {
609 return self->subcomponentInfos;
613 [self->subcomponentInfos removeAllObjects];
614 ASSIGN(self->script, (id)nil);
617 @end /* WOxElemBuilder */
619 @implementation WOxTagClassElemBuilder
621 - (Class)classForElement:(id<DOMElement>)_element {
625 - (WOElement *)buildElement:(id<DOMElement>)_element templateBuilder:(id)_b {
628 if ((clazz = [self classForElement:_element]) == Nil) {
629 if (self->nextBuilder)
630 return [self->nextBuilder buildElement:_element templateBuilder:_b];
633 @"did not find dynamic element class for DOM element %@",
638 return [[clazz alloc] initWithElement:_element templateBuilder:_b];
641 @end /* SxTagClassElemBuilder */