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 NSLog(@"builder class: %@", cn);
153 if ((clazz = NSClassFromString(cn)) == Nil) {
154 if (missingBuilders == nil)
155 missingBuilders = [NSMutableArray arrayWithCapacity:16];
156 [missingBuilders addObject:cn];
160 if ((nx = [[clazz alloc] init])) {
162 first = current = nx;
166 [current setNextBuilder:nx];
167 current = [nx autorelease];
171 NSLog(@"%s: couldn't allocate builder (class=%@)", cn);
176 if (missingBuilders) {
177 NSLog(@"WOxElemBuilder: could not locate builders: %@",
178 [missingBuilders componentsJoinedByString:@","]);
183 + (WOxElemBuilder *)createBuilderQueueV:(NSString *)_className, ... {
184 // TODO: reimplement using createBuilderQueue:
187 WOxElemBuilder *first, *current;
189 if (_className == nil)
190 return [[[self alloc] init] autorelease];
192 first = [[[NSClassFromString(_className) alloc] init] autorelease];
194 va_start(ap, _className);
195 for (current = first; (cn = va_arg(ap, id)); ) {
198 nx = [[NSClassFromString(cn) alloc] init];
199 [current setNextBuilder:nx];
200 current = [nx autorelease];
208 [self->script release];
209 [self->subcomponentInfos release];
210 [self->nsToAssoc release];
211 [self->nextBuilder release];
215 /* building an element (returns a retained object !!!) */
217 - (WOElement *)buildNode:(id<DOMNode>)_node templateBuilder:(id)_builder {
221 switch ([_node nodeType]) {
222 case DOM_ELEMENT_NODE:
223 return [self buildElement:(id<DOMElement>)_node
224 templateBuilder:_builder];
226 return [self buildText:(id<DOMText>)_node
227 templateBuilder:_builder];
228 case DOM_CDATA_SECTION_NODE:
229 return [self buildCDATASection:(id<DOMCDATASection>)_node
230 templateBuilder:_builder];
231 case DOM_COMMENT_NODE:
232 return [self buildComment:(id<DOMComment>)_node
233 templateBuilder:_builder];
234 case DOM_DOCUMENT_NODE:
235 return [self buildDocument:(id<DOMDocument>)_node
236 templateBuilder:_builder];
239 if (self->nextBuilder)
240 return [self->nextBuilder buildNode:_node templateBuilder:_builder];
242 NSLog(@"unknown node type %i, node %@", [_node nodeType], _node);
248 - (NSArray *)buildNodes:(id<DOMNodeList>)_nodes templateBuilder:(id)_bld {
249 // Note: returns a regular autoreleased array
250 NSMutableArray *children;
253 if ((count = [_nodes length]) == 0)
256 children = [NSMutableArray arrayWithCapacity:(count + 1)];
258 for (i = 0; i < count; i++) {
261 e = [_bld buildNode:[_nodes objectAtIndex:i] templateBuilder:_bld];
263 [children addObject:e];
270 /* building methods specialized on type (return retained objects !!!) */
272 - (WOElement *)buildDocument:(id<DOMDocument>)_node templateBuilder:(id)_bld {
273 return [self buildElement:[_node documentElement] templateBuilder:_bld];
276 - (WOElement *)buildElement:(id<DOMElement>)_node templateBuilder:(id)_bld {
277 if (self->nextBuilder)
278 return [self->nextBuilder buildElement:_node templateBuilder:_bld];
280 [self logWithFormat:@"cannot build node %@ (template builder %@)",
285 - (WOElement *)buildCharacterData:(id<DOMCharacterData>)_text
286 templateBuilder:(id)_builder
288 static Class ValClass = Nil;
289 WOElement *textElement;
295 if ((len = [str length]) == 0) return nil;
298 we use WOValueAssociation directly, because WOAssociation caches all
302 ValClass = NSClassFromString(@"WOValueAssociation");
305 # warning not using ASCII string !
309 // TODO(perf): improve on that
310 /* not very efficient, but only used during template parsing ... */
311 if ([str dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:NO])
317 isASCII = ([str characterAtIndex:0] < 128) ? YES : NO;
321 str = [[ValClass alloc] initWithString:str];
323 [[(isASCII?AStrClass:StrClass) alloc] initWithValue:str escapeHTML:YES];
327 - (WOElement *)buildText:(id<DOMText>)_node
328 templateBuilder:(id)_builder
330 return [self buildCharacterData:_node templateBuilder:_builder];
332 - (WOElement *)buildCDATASection:(id<DOMCDATASection>)_node
333 templateBuilder:(id)_builder
335 return [self buildCharacterData:_node templateBuilder:_builder];
338 - (WOElement *)buildComment:(id<DOMComment>)_node
339 templateBuilder:(id)_builder
341 /* comments aren't delivered ... */
345 /* building the whole template */
347 - (WOElement *)buildTemplateFromDocument:(id<DOMDocument>)_document {
348 NSAutoreleasePool *pool;
351 pool = [[NSAutoreleasePool alloc] init];
352 result = [[self buildNode:_document templateBuilder:self] retain];
354 return [result autorelease];
357 /* association callbacks */
359 - (WOAssociation *)associationForValue:(id)_value {
360 return [WOAssociation associationWithValue:_value];
363 - (WOAssociation *)associationForKeyPath:(NSString *)_path {
364 return [WOAssociation associationWithKeyPath:_path];
367 - (WOAssociation *)associationForJavaScript:(NSString *)_js {
368 WOAssociation *assoc;
370 assoc = [NSClassFromString(@"WOScriptAssociation") alloc];
371 assoc = [(id)assoc initWithScript:_js language:@"javascript"];
372 return [assoc autorelease];
375 - (WOAssociation *)associationForAttribute:(id<DOMAttr>)_attribute {
378 WOAssociation *assoc;
381 nsuri = [_attribute namespaceURI];
382 value = [_attribute nodeValue];
384 c = [self associationClassForNamespaceURI:[_attribute namespaceURI]];
387 @"WARNING, found no association class for "
388 @"attribute %@ (namespace=%@)",
389 _attribute, [_attribute namespaceURI]];
393 [self logWithFormat:@"use class %@ for namespaceURI %@ (attribute %@)",
394 c, [_attribute namespaceURI], [_attribute name]];
397 assoc = [[c alloc] initWithString:value];
398 if (logAssocCreation) {
399 [self logWithFormat:@"created assoc %@ for attribute %@",
400 assoc, [_attribute name]];
403 return [assoc autorelease];
406 - (NSMutableDictionary *)associationsForAttributes:(id<DOMNamedNodeMap>)_attrs{
407 NSMutableDictionary *assocs;
410 if ((count = [_attrs length]) == 0)
413 assocs = [NSMutableDictionary dictionaryWithCapacity:(count + 1)];
415 for (i = 0; i < count; i++) {
417 WOAssociation *assoc;
419 attr = [_attrs objectAtIndex:i];
421 if ((assoc = [self associationForAttribute:attr])) {
425 if ([key characterAtIndex:0] == '_')
426 key = [@"?" stringByAppendingString:[key substringFromIndex:1]];
428 [assocs setObject:assoc forKey:key];
434 - (void)_ensureDefaultAssocMappings {
441 self->nsToAssoc = [[NSMutableDictionary alloc] initWithCapacity:8];
442 e = [defaultAssocMap keyEnumerator];
443 while ((ns = [e nextObject])) {
447 className = [defaultAssocMap objectForKey:ns];
448 clazz = NSClassFromString(className);
451 [self logWithFormat:@"WARNING: did not find association class: '%@'",
457 [self->nsToAssoc setObject:clazz forKey:ns];
460 - (void)registerAssociationClass:(Class)_class forNamespaceURI:(NSString *)_ns{
461 if (_ns == nil) return;
462 if (_class == Nil) return;
464 [self _ensureDefaultAssocMappings];
465 [self->nsToAssoc setObject:_class forKey:_ns];
467 - (Class)associationClassForNamespaceURI:(NSString *)_ns {
470 [self _ensureDefaultAssocMappings];
472 if ((c = [self->nsToAssoc objectForKey:_ns]) == nil)
473 /* if we have no class mapped for a namespace, we treat it as a value */
477 [self debugWithFormat:@"using class %@ for namespace %@", c, _ns];
481 /* creating unique IDs */
483 - (NSString *)uniqueIDForNode:(id)_node {
484 NSMutableArray *nodePath;
485 NSMutableString *uid;
486 NSEnumerator *topDown;
490 if (_node == nil) return nil;
492 nodePath = [NSMutableArray arrayWithCapacity:16];
494 /* collect all parent nodes in bottom-up form */
496 for (node = _node; node; node = [node parentNode])
497 [nodePath addObject:node];
501 uid = [NSMutableString stringWithCapacity:64];
502 topDown = [nodePath reverseObjectEnumerator];
506 for (isFirst = YES; (node = [topDown nextObject]); parent = node) {
511 [uid appendString:@"."];
513 /* determine index of _node */
515 children = (NSArray *)[parent childNodes];
516 for (i = 0, count = [children count]; i < count; i++) {
517 if ([children objectAtIndex:i] == node)
520 [uid appendFormat:@"%d", i];
523 [uid appendString:@"R"];
528 return [[uid copy] autorelease];
533 - (void)logWithFormat:(NSString *)_format, ... {
534 NSString *value = nil;
537 va_start(ap, _format);
538 value = [[NSString alloc] initWithFormat:_format arguments:ap];
541 NSLog(@"|%@| %@", self, value);
544 - (void)debugWithFormat:(NSString *)_format, ... {
545 static char showDebug = 2;
546 NSString *value = nil;
549 if (showDebug == 2) {
550 showDebug = [WOApplication isDebuggingEnabled] ? 1 : 0;
554 va_start(ap, _format);
555 value = [[NSString alloc] initWithFormat:_format arguments:ap];
558 NSLog(@"|%@|D %@", self, value);
563 /* managing builder queues */
565 - (void)setNextBuilder:(WOxElemBuilder *)_builder {
566 ASSIGN(self->nextBuilder, _builder);
568 - (WOxElemBuilder *)nextBuilder {
569 return self->nextBuilder;
572 /* component script parts */
574 - (void)addComponentScriptPart:(WOComponentScriptPart *)_part {
575 if (self->script == nil)
576 self->script = [[WOComponentScript alloc] init];
578 [self->script addScriptPart:_part];
580 - (void)addComponentScript:(NSString *)_script line:(unsigned)_line {
581 WOComponentScriptPart *part;
583 part = [[WOComponentScriptPart alloc] initWithURL:nil startLine:_line
585 [self addComponentScriptPart:part];
589 - (WOComponentScript *)componentScript {
593 /* subcomponent registry, created during parsing ... */
595 - (void)registerSubComponentWithId:(NSString *)_cid
596 componentName:(NSString *)_name
597 bindings:(NSMutableDictionary *)_bindings
599 WOxElemBuilderComponentInfo *info;
601 info = [[WOxElemBuilderComponentInfo alloc] initWithComponentId:_cid
605 if (self->subcomponentInfos == nil)
606 self->subcomponentInfos = [[NSMutableArray alloc] initWithCapacity:16];
607 [self->subcomponentInfos addObject:info];
611 - (NSArray *)subcomponentInfos {
612 return self->subcomponentInfos;
616 [self->subcomponentInfos removeAllObjects];
617 ASSIGN(self->script, (id)nil);
620 @end /* WOxElemBuilder */