2005-09-06 Helge Hess <helge.hess@skyrix.com>
+ * v4.5.197
+
+ * Templates/WOWrapperTemplateBuilder.m: attributes of <WEBOBJECT> or
+ <#Element> tags are now added as associations to dynamic elements.
+ The type of the association is determined by the prefix (hardcoded:
+ var, const, so, rsrc). Tag attributes have precedence over wod
+ associations so that you can define defaults in the .wod file and
+ override them in the .html template.
+ If the .wod file does not contain a definition for a given tagname,
+ the parser will now attempt to treat the tagname as a class (eg:
+ <#WOString var:value="name"/> now works w/o any .wod entry).
+
+ * Templates/WOHTMLParser.m (_parseHashElement): parse attributes
+ defined in hash tags (eg <#abc value="abc"/>)
+
* DynamicElements/WOSwitchComponent.m,
DynamicElements/WOComponentReference.m: minor code cleanups
(v4.5.196)
}
}
+static NSMutableDictionary *
+_parseTagAttributes(NSZone *_zone, const unichar *_buffer,
+ unsigned *_idx, unsigned _len,
+ NSException **_exception, WOHTMLParser *self);
+
static WOElement *_parseHashElement(NSZone *_zone, const unichar *_buffer,
unsigned *_idx, unsigned _len,
NSException **_exc,
NSMutableArray *children = nil;
NSString *name;
NSDictionary *nameDict;
+ NSMutableDictionary *attrs;
BOOL hadSlashAfterHash;
if (*_idx >= _len) return nil; // EOF
[self warnWithFormat:@"typo in hash close tag ('<#/' => '</#')."];
}
+ /* parse tag name */
+
if ((name = _parseStringValue(_zone, _buffer, _idx,_len,_exc,self)) == nil) {
#if HEAVY_DEBUG
[self errorWithFormat:@"got no name for hash tag '<#NAME>'"];
if (_exc != NULL && *_exc != nil) // if there was an error ..
return nil;
}
-
_skipSpaces(_buffer, _idx, _len);
+
+ /* parse attributes */
+
+ attrs = _parseTagAttributes(_zone, _buffer, _idx, _len, _exc, self);
+ if (_exc != NULL) {
+ if (*_exc != nil) {
+ [name release]; name = nil;
+ return nil; // invalid tag attrs
+ }
+ }
+
+ /* parse tag end (> or /) */
+
if (*_idx >= _len) {
*_exc =
_makeHtmlException(*_exc, _buffer, *_idx, _len,
NSLog(@" parsed subelement '%@' ..", subElement);
#endif
- if (subElement) {
+ if (subElement != nil) {
if (children == nil)
children = [NSMutableArray arrayWithCapacity:10];
[children addObject:subElement];
nameDict = [[NSDictionary alloc] initWithObjects:&name forKeys:&nameKey
count:1];
+ if (attrs != nil)
+ [attrs addEntriesFromDictionary:nameDict];
+
element = [self dynamicElementWithName:name
- attributes:nameDict
+ attributes:(attrs != nil ? attrs : nameDict)
contentElements:children];
[name release]; name = nil;
[nameDict release]; nameDict = nil;
attrs = _parseTagAttributes(_zone, _buffer, _idx, _len, _exception, self);
if (attrs == nil) {
- //[self errorWithFormat:
- // @"got no attributes for WO tag (need at least 'NAME').."];
-
- if (_exception) // if there was an error ..
+#if 0
+ [self errorWithFormat:
+ @"got no attributes for WO tag (need at least 'NAME').."];
+#endif
+ if (_exception != NULL) // if there was an error .. // TODO: check this!
return nil;
}
+ (BOOL)isDynamicElement;
@end
-static Class AssocClass = Nil;
-static Class StrClass = Nil;
+static Class AssocClass = Nil;
+static Class StrClass = Nil;
+static Class ValAssoc = Nil;
@implementation WOWrapperTemplateBuilder
static BOOL logExtraAssociations = NO;
static BOOL logScriptAdditions = NO;
static NSStringEncoding parserEncoding;
+static NSDictionary *defaultAssocMap = nil;
+ (int)version {
return [super version] + 0 /* v2 */;
AssocClass = [WOAssociation class];
StrClass = [NSString class];
+
+ if (ValAssoc == Nil)
+ ValAssoc = NSClassFromString(@"WOValueAssociation");
+
+ // TODO: improve extensibility of this (remember WOxElemBuilder)
+ defaultAssocMap = [[ud dictionaryForKey:@"WOxAssociationClassMapping"] copy];
if ([ud boolForKey:@"WOParsersUseUTF8"]) {
parserEncoding = NSUTF8StringEncoding;
return [template autorelease];
}
+/* creating associations from WO/hash tag attributes */
+
+- (NSString *)namespaceURIForPrefix:(NSString *)_prefix {
+ unsigned int len;
+
+ if ((len = [_prefix length]) == 0)
+ return nil;
+
+ switch (len) {
+ case 2:
+ if ([_prefix isEqualToString:@"so"])
+ return @"http://www.skyrix.com/od/so-lookup";
+ break;
+ case 3:
+ if ([_prefix isEqualToString:@"var"])
+ return @"http://www.skyrix.com/od/binding";
+ break;
+ case 4:
+ if ([_prefix isEqualToString:@"rsrc"])
+ return @"OGo:url";
+ break;
+ case 5:
+ if ([_prefix isEqualToString:@"const"])
+ return @"http://www.skyrix.com/od/constant";
+ if ([_prefix isEqualToString:@"label"])
+ return @"OGo:label";
+ break;
+ }
+ [self errorWithFormat:@"found no namespace for prefix: '%@'", _prefix];
+ return nil;
+}
+
+- (Class)associationClassForNamespaceURI:(NSString *)_uri {
+ if (_uri == nil)
+ return ValAssoc;
+
+ // TODO: WOxElemBuilder caches the classes
+ return NSClassFromString([defaultAssocMap objectForKey:_uri]);
+}
+
+- (void)addAttributes:(NSDictionary *)_attrs
+ toAssociations:(NSMutableDictionary *)assocs
+{
+ NSEnumerator *e;
+ NSString *key;
+ unsigned count;
+
+ if (_attrs == nil)
+ return;
+ if ((count = [_attrs count]) == 0)
+ return;
+ if (count == 1 && [_attrs objectForKey:@"NAME"] != nil)
+ return;
+
+ e = [_attrs keyEnumerator];
+ while ((key = [e nextObject]) != nil) {
+ BOOL doRelease;
+ id value;
+
+ if ([key isEqualToString:@"NAME"]) /* <WEBOBJECT NAME="">, not in assocs */
+ continue;
+
+ value = [_attrs objectForKey:key];
+ if (![value isKindOfClass:AssocClass]) {
+ NSRange r;
+ NSString *uri, *name;
+ Class clazz;
+
+ r = [key rangeOfString:@":"];
+ uri = [self namespaceURIForPrefix:
+ (r.length > 0) ? [key substringToIndex:r.location] : nil];
+ name = (r.length > 0)
+ ? [key substringFromIndex:(r.location + r.length)]
+ : key;
+
+ if ((clazz = [self associationClassForNamespaceURI:uri]) == nil) {
+ [self logWithFormat:@"ERROR: could not process tag attribute: %@",key];
+ continue;
+ }
+
+ value = [[clazz alloc] initWithString:value];
+ key = name;
+ doRelease = YES;
+ }
+ else
+ doRelease = NO;
+
+ [assocs setObject:value forKey:key];
+ if (doRelease) [value release];
+ }
+}
+
/* HTML parser callbacks */
- (NSString *)_uniqueComponentNameForDefinitionWithName:(NSString *)_element {
Class elementClass;
NSMutableDictionary *assoc = nil;
WODynamicElement *element;
-
+
if ((def = [self->definitions objectForKey:_element]) == nil) {
- [self errorWithFormat:
- @"did not find definition of dynamic element '%@'",
- _element];
- return [[NSClassFromString(@"WONoContentElement") alloc]
- initWithElementName:_element
- attributes:_attributes
- contentElements:_subElements
- componentDefinition:nil];
+ /*
+ If there is no definition with the name, try to treat it as a
+ classname, eg:
+ <#WOString var:value="abc" />
+ <WEBOBJECT NAME="WOString" var:value="abc"></WEBOBJECT>
+ */
+ if ((elementClass = NSClassFromString(_element)) == nil) {
+ /* ok, we also do not have a matching class */
+
+ [self errorWithFormat:
+ @"did not find definition of dynamic element '%@'",
+ _element];
+ return [[NSClassFromString(@"WONoContentElement") alloc]
+ initWithElementName:_element
+ attributes:_attributes
+ contentElements:_subElements
+ componentDefinition:nil];
+ }
+
+ /* setup fake WOD entry */
+ def = [[[_WODFileEntry alloc] init] autorelease];
+ def->componentName = [_element copy];
+ def->associations = [[NSDictionary alloc] init];
+ def->componentClass = elementClass;
}
if (![def isDynamicElement]) {
NSAssert1(elementClass, @"got no class for element %@", def);
assoc = [def->associations mutableCopy];
-
+ [self addAttributes:_attributes toAssociations:assoc];
+
+ /* create element */
+
element = [[elementClass alloc]
initWithName:_element
associations:assoc