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/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;
104 static NGLogger *logger = nil;
112 static BOOL didInit = NO;
117 ud = [NSUserDefaults standardUserDefaults];
118 lm = [NGLoggerManager defaultLoggerManager];
120 logger = [lm loggerForClass:self];
121 [logger setLogLevel:[WOApplication isDebuggingEnabled] ? NGLogLevelDebug
124 StrClass = NSClassFromString(@"_WOSimpleStaticString");
126 [logger errorWithFormat:@"missing class _WOSimpleStaticString !"];
127 AStrClass = NSClassFromString(@"_WOSimpleStaticASCIIString");
128 if (AStrClass == Nil)
129 [logger errorWithFormat:@"missing class _WOSimpleStaticASCIIString !"];
131 logAssocMap = [ud boolForKey:@"WOxElemBuilder_LogAssociationMapping"];
133 [ud boolForKey:@"WOxElemBuilder_LogAssociationCreation"];
135 [logger logWithFormat:@"association mapping is logged!"];
136 if (logAssocCreation)
137 [logger logWithFormat:@"association creation is logged!"];
139 defaultAssocMap = [[ud dictionaryForKey:@"WOxAssociationClassMapping"] copy];
140 if (defaultAssocMap == nil)
141 [logger warnWithFormat:
142 @"WOxAssociationClassMapping default is not set!"];
145 ValAssoc = NSClassFromString(@"WOValueAssociation");
148 + (WOxElemBuilder *)createBuilderQueue:(NSArray *)_classNames {
150 WOxElemBuilder *first, *current = nil;
151 NSMutableArray *missingBuilders = nil;
153 if ((count = [_classNames count]) == 0)
156 for (first = nil, i = 0; i < count; i++) {
161 cn = [_classNames objectAtIndex:i];
163 NSLog(@"builder class: %@", cn);
166 if ((clazz = NSClassFromString(cn)) == Nil) {
167 if (missingBuilders == nil)
168 missingBuilders = [NSMutableArray arrayWithCapacity:16];
169 [missingBuilders addObject:cn];
173 if ((nx = [[clazz alloc] init])) {
175 first = current = nx;
179 [current setNextBuilder:nx];
180 current = [nx autorelease];
184 NSLog(@"%s: couldn't allocate builder (class=%@)", cn);
189 if (missingBuilders) {
190 NSLog(@"WOxElemBuilder: could not locate builders: %@",
191 [missingBuilders componentsJoinedByString:@","]);
196 + (WOxElemBuilder *)createBuilderQueueV:(NSString *)_className, ... {
197 // TODO: reimplement using createBuilderQueue:
200 WOxElemBuilder *first, *current;
202 if (_className == nil)
203 return [[[self alloc] init] autorelease];
205 first = [[[NSClassFromString(_className) alloc] init] autorelease];
207 va_start(ap, _className);
208 for (current = first; (cn = va_arg(ap, id)); ) {
211 nx = [[NSClassFromString(cn) alloc] init];
212 [current setNextBuilder:nx];
213 current = [nx autorelease];
221 [self->script release];
222 [self->subcomponentInfos release];
223 [self->nsToAssoc release];
224 [self->nextBuilder release];
228 /* building an element (returns a retained object !!!) */
230 - (WOElement *)buildNode:(id<DOMNode>)_node templateBuilder:(id)_builder {
234 switch ([_node nodeType]) {
235 case DOM_ELEMENT_NODE:
236 return [self buildElement:(id<DOMElement>)_node
237 templateBuilder:_builder];
239 return [self buildText:(id<DOMText>)_node
240 templateBuilder:_builder];
241 case DOM_CDATA_SECTION_NODE:
242 return [self buildCDATASection:(id<DOMCDATASection>)_node
243 templateBuilder:_builder];
244 case DOM_COMMENT_NODE:
245 return [self buildComment:(id<DOMComment>)_node
246 templateBuilder:_builder];
247 case DOM_DOCUMENT_NODE:
248 return [self buildDocument:(id<DOMDocument>)_node
249 templateBuilder:_builder];
252 if (self->nextBuilder)
253 return [self->nextBuilder buildNode:_node templateBuilder:_builder];
255 NSLog(@"unknown node type %i, node %@", [_node nodeType], _node);
261 - (NSArray *)buildNodes:(id<DOMNodeList>)_nodes templateBuilder:(id)_bld {
262 // Note: returns a regular autoreleased array
263 NSMutableArray *children;
266 if ((count = [_nodes length]) == 0)
269 children = [NSMutableArray arrayWithCapacity:(count + 1)];
271 for (i = 0; i < count; i++) {
274 e = [_bld buildNode:[_nodes objectAtIndex:i] templateBuilder:_bld];
276 [children addObject:e];
283 /* building methods specialized on type (return retained objects !!!) */
285 - (WOElement *)buildDocument:(id<DOMDocument>)_node templateBuilder:(id)_bld {
286 return [self buildElement:[_node documentElement] templateBuilder:_bld];
289 - (WOElement *)buildElement:(id<DOMElement>)_node templateBuilder:(id)_bld {
290 if (self->nextBuilder)
291 return [self->nextBuilder buildElement:_node templateBuilder:_bld];
293 [self logWithFormat:@"cannot build node %@ (template builder %@)",
298 - (WOElement *)buildCharacterData:(id<DOMCharacterData>)_text
299 templateBuilder:(id)_builder
301 static Class ValClass = Nil;
302 WOElement *textElement;
308 if ((len = [str length]) == 0) return nil;
311 we use WOValueAssociation directly, because WOAssociation caches all
315 ValClass = NSClassFromString(@"WOValueAssociation");
318 # warning not using ASCII string !
322 // TODO(perf): improve on that
323 /* not very efficient, but only used during template parsing ... */
324 if ([str dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:NO])
330 isASCII = ([str characterAtIndex:0] < 128) ? YES : NO;
334 str = [[ValClass alloc] initWithString:str];
336 [[(isASCII?AStrClass:StrClass) alloc] initWithValue:str escapeHTML:YES];
340 - (WOElement *)buildText:(id<DOMText>)_node
341 templateBuilder:(id)_builder
343 return [self buildCharacterData:_node templateBuilder:_builder];
345 - (WOElement *)buildCDATASection:(id<DOMCDATASection>)_node
346 templateBuilder:(id)_builder
348 return [self buildCharacterData:_node templateBuilder:_builder];
351 - (WOElement *)buildComment:(id<DOMComment>)_node
352 templateBuilder:(id)_builder
354 /* comments aren't delivered ... */
358 /* building the whole template */
360 - (WOElement *)buildTemplateFromDocument:(id<DOMDocument>)_document {
361 NSAutoreleasePool *pool;
364 pool = [[NSAutoreleasePool alloc] init];
365 result = [[self buildNode:_document templateBuilder:self] retain];
367 return [result autorelease];
370 /* association callbacks */
372 - (WOAssociation *)associationForValue:(id)_value {
373 return [WOAssociation associationWithValue:_value];
376 - (WOAssociation *)associationForKeyPath:(NSString *)_path {
377 return [WOAssociation associationWithKeyPath:_path];
380 - (WOAssociation *)associationForJavaScript:(NSString *)_js {
381 WOAssociation *assoc;
383 assoc = [NSClassFromString(@"WOScriptAssociation") alloc];
384 assoc = [(id)assoc initWithScript:_js language:@"javascript"];
385 return [assoc autorelease];
388 - (WOAssociation *)associationForAttribute:(id<DOMAttr>)_attribute {
391 WOAssociation *assoc;
394 nsuri = [_attribute namespaceURI];
395 value = [_attribute nodeValue];
397 c = [self associationClassForNamespaceURI:[_attribute namespaceURI]];
399 [self warnWithFormat:
400 @"found no association class for attribute %@ (namespace=%@)",
401 _attribute, [_attribute namespaceURI]];
405 [self logWithFormat:@"use class %@ for namespaceURI %@ (attribute %@)",
406 c, [_attribute namespaceURI], [_attribute name]];
409 assoc = [[c alloc] initWithString:value];
410 if (logAssocCreation) {
411 [self logWithFormat:@"created assoc %@ for attribute %@",
412 assoc, [_attribute name]];
415 return [assoc autorelease];
418 - (NSMutableDictionary *)associationsForAttributes:(id<DOMNamedNodeMap>)_attrs{
419 NSMutableDictionary *assocs;
422 if ((count = [_attrs length]) == 0)
425 assocs = [NSMutableDictionary dictionaryWithCapacity:(count + 1)];
427 for (i = 0; i < count; i++) {
429 WOAssociation *assoc;
431 attr = [_attrs objectAtIndex:i];
433 if ((assoc = [self associationForAttribute:attr])) {
437 if ([key characterAtIndex:0] == '_')
438 key = [@"?" stringByAppendingString:[key substringFromIndex:1]];
440 [assocs setObject:assoc forKey:key];
446 - (void)_ensureDefaultAssocMappings {
453 self->nsToAssoc = [[NSMutableDictionary alloc] initWithCapacity:8];
454 e = [defaultAssocMap keyEnumerator];
455 while ((ns = [e nextObject])) {
459 className = [defaultAssocMap objectForKey:ns];
460 clazz = NSClassFromString(className);
463 [self warnWithFormat:@"did not find association class: '%@'",
469 [self->nsToAssoc setObject:clazz forKey:ns];
472 - (void)registerAssociationClass:(Class)_class forNamespaceURI:(NSString *)_ns{
473 if (_ns == nil) return;
474 if (_class == Nil) return;
476 [self _ensureDefaultAssocMappings];
477 [self->nsToAssoc setObject:_class forKey:_ns];
479 - (Class)associationClassForNamespaceURI:(NSString *)_ns {
482 [self _ensureDefaultAssocMappings];
484 if ((c = [self->nsToAssoc objectForKey:_ns]) == nil)
485 /* if we have no class mapped for a namespace, we treat it as a value */
489 [self debugWithFormat:@"using class %@ for namespace %@", c, _ns];
493 /* creating unique IDs */
495 - (NSString *)uniqueIDForNode:(id)_node {
496 NSMutableArray *nodePath;
497 NSMutableString *uid;
498 NSEnumerator *topDown;
502 if (_node == nil) return nil;
504 nodePath = [NSMutableArray arrayWithCapacity:16];
506 /* collect all parent nodes in bottom-up form */
508 for (node = _node; node; node = [node parentNode])
509 [nodePath addObject:node];
513 uid = [NSMutableString stringWithCapacity:64];
514 topDown = [nodePath reverseObjectEnumerator];
518 for (isFirst = YES; (node = [topDown nextObject]); parent = node) {
523 [uid appendString:@"."];
525 /* determine index of _node */
527 children = (NSArray *)[parent childNodes];
528 for (i = 0, count = [children count]; i < count; i++) {
529 if ([children objectAtIndex:i] == node)
532 [uid appendFormat:@"%d", i];
535 [uid appendString:@"R"];
540 return [[uid copy] autorelease];
552 - (void)logWithFormat:(NSString *)_format, ... {
553 NSString *value = nil;
556 va_start(ap, _format);
557 value = [[NSString alloc] initWithFormat:_format arguments:ap];
560 NSLog(@"|%@| %@", self, value);
563 - (void)debugWithFormat:(NSString *)_format, ... {
564 static char showDebug = 2;
565 NSString *value = nil;
568 if (showDebug == 2) {
569 showDebug = [WOApplication isDebuggingEnabled] ? 1 : 0;
573 va_start(ap, _format);
574 value = [[NSString alloc] initWithFormat:_format arguments:ap];
577 NSLog(@"|%@|D %@", self, value);
582 /* managing builder queues */
584 - (void)setNextBuilder:(WOxElemBuilder *)_builder {
585 ASSIGN(self->nextBuilder, _builder);
587 - (WOxElemBuilder *)nextBuilder {
588 return self->nextBuilder;
591 /* component script parts */
593 - (void)addComponentScriptPart:(WOComponentScriptPart *)_part {
594 if (self->script == nil)
595 self->script = [[WOComponentScript alloc] init];
597 [self->script addScriptPart:_part];
599 - (void)addComponentScript:(NSString *)_script line:(unsigned)_line {
600 WOComponentScriptPart *part;
602 part = [[WOComponentScriptPart alloc] initWithURL:nil startLine:_line
604 [self addComponentScriptPart:part];
608 - (WOComponentScript *)componentScript {
612 /* subcomponent registry, created during parsing ... */
614 - (void)registerSubComponentWithId:(NSString *)_cid
615 componentName:(NSString *)_name
616 bindings:(NSMutableDictionary *)_bindings
618 WOxElemBuilderComponentInfo *info;
620 info = [[WOxElemBuilderComponentInfo alloc] initWithComponentId:_cid
624 if (self->subcomponentInfos == nil)
625 self->subcomponentInfos = [[NSMutableArray alloc] initWithCapacity:16];
626 [self->subcomponentInfos addObject:info];
630 - (NSArray *)subcomponentInfos {
631 return self->subcomponentInfos;
635 [self->subcomponentInfos removeAllObjects];
636 ASSIGN(self->script, (id)nil);
639 @end /* WOxElemBuilder */