Index: src/OFXMLElement.h ================================================================== --- src/OFXMLElement.h +++ src/OFXMLElement.h @@ -14,18 +14,76 @@ #import "OFDictionary.h" #import "OFArray.h" extern int _OFXMLElement_reference; +/** + * The OFXMLAttribute represents an attribute of an XML element as an object. + */ +@interface OFXMLAttribute: OFObject +{ + OFString *prefix; + OFString *name; + OFString *ns; + OFString *value; +} + +/** + * \param name The name of the attribute + * \param prefix The prefix of the attribute + * \param ns The namespace of the attribute + * \param value The string value of the attribute + * \return A new autoreleased OFXMLAttribute with the specified parameters + */ ++ attributeWithName: (OFString*)name + prefix: (OFString*)prefix + namespace: (OFString*)ns + stringValue: (OFString*)value; + +/** + * Initializes an already allocated OFXMLAttribute. + * + * \param name The name of the attribute + * \param prefix The prefix of the attribute + * \param ns The namespace of the attribute + * \param value The string value of the attribute + * \return An initialized OFXMLAttribute with the specified parameters + */ +- initWithName: (OFString*)name + prefix: (OFString*)prefix + namespace: (OFString*)ns + stringValue: (OFString*)value; + +/** + * \return The name of the attribute as an autoreleased OFString + */ +- (OFString*)name; + +/** + * \return The prefix of the attribute as an autoreleased OFString + */ +- (OFString*)prefix; + +/** + * \return The namespace of the attribute as an autoreleased OFString + */ +- (OFString*)namespace; + +/** + * \return The string value of the attribute as an autoreleased OFString + */ +- (OFString*)stringValue; +@end + /** * The OFXMLElement represents an XML element as an object which can be * modified and converted back to XML again. */ @interface OFXMLElement: OFObject { OFString *name; - OFDictionary *attrs; + OFArray *attrs; OFString *stringval; OFArray *children; } /** @@ -67,10 +125,17 @@ * \return A new autoreleased OFString representing the OFXMLElement as an * XML string */ - (OFString*)string; +/** + * Adds the specified attribute. + * + * \param attr The attribute to add + */ +- addAttribute: (OFXMLAttribute*)attr; + /** * Adds the specified attribute with the specified value. * * \param name The name of the attribute * \param value The value of the attribute Index: src/OFXMLElement.m ================================================================== --- src/OFXMLElement.m +++ src/OFXMLElement.m @@ -18,10 +18,68 @@ #import "OFXMLElement.h" #import "OFAutoreleasePool.h" #import "OFExceptions.h" int _OFXMLElement_reference; + +@implementation OFXMLAttribute ++ attributeWithName: (OFString*)name_ + prefix: (OFString*)prefix_ + namespace: (OFString*)ns_ + stringValue: (OFString*)value_ +{ + return [[[self alloc] initWithName: name_ + prefix: prefix_ + namespace: ns_ + stringValue: value_] autorelease]; +} + +- initWithName: (OFString*)name_ + prefix: (OFString*)prefix_ + namespace: (OFString*)ns_ + stringValue: (OFString*)value_ +{ + self = [super init]; + + name = [name_ copy]; + prefix = [prefix_ copy]; + ns = [ns_ copy]; + value = [value_ copy]; + + return self; +} + +- (void)dealloc +{ + [name release]; + [prefix release]; + [ns release]; + [value release]; + + [super dealloc]; +} + +- (OFString*)name +{ + return [[name copy] autorelease]; +} + +- (OFString*)prefix +{ + return [[prefix copy] autorelease]; +} + +- (OFString*)namespace +{ + return [[ns copy] autorelease]; +} + +- (OFString*)stringValue +{ + return [[value copy] autorelease]; +} +@end @implementation OFXMLElement + elementWithName: (OFString*)name_ { return [[[self alloc] initWithName: name_] autorelease]; @@ -62,11 +120,12 @@ - (OFString*)string { OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; char *str_c; - size_t len, i, j; + size_t len, i, j, attrs_count; + OFXMLAttribute **attrs_data; OFString *ret, *tmp; len = [name length] + 4; str_c = [self allocMemoryWithSize: len]; @@ -74,44 +133,38 @@ *str_c = '<'; memcpy(str_c + 1, [name cString], [name length]); i = [name length] + 1; /* Attributes */ - if (attrs != nil) { - OFIterator *iter = [attrs iterator]; - - for (;;) { - of_iterator_pair_t pair; - - pair = [iter nextKeyObjectPair]; - - if (pair.key == nil || pair.object == nil) - break; - - tmp = [pair.object stringByXMLEscaping]; - - len += [pair.key length] + [tmp length] + 4; - @try { - str_c = [self resizeMemory: str_c - toSize: len]; - } @catch (OFException *e) { - [self freeMemory: str_c]; - @throw e; - } - - str_c[i++] = ' '; - memcpy(str_c + i, [pair.key cString], - [pair.key length]); - i += [pair.key length]; - str_c[i++] = '='; - str_c[i++] = '\''; - memcpy(str_c + i, [tmp cString], [tmp length]); - i += [tmp length]; - str_c[i++] = '\''; - - [pool releaseObjects]; - } + attrs_data = [attrs data]; + attrs_count = [attrs count]; + + for (j = 0; j < attrs_count; j++) { + /* FIXME: Add namespace support */ + OFString *attr_name = [attrs_data[j] name]; + tmp = [[attrs_data[j] stringValue] stringByXMLEscaping]; + + len += [attr_name length] + [tmp length] + 4; + @try { + str_c = [self resizeMemory: str_c + toSize: len]; + } @catch (OFException *e) { + [self freeMemory: str_c]; + @throw e; + } + + str_c[i++] = ' '; + memcpy(str_c + i, [attr_name cString], + [attr_name length]); + i += [attr_name length]; + str_c[i++] = '='; + str_c[i++] = '\''; + memcpy(str_c + i, [tmp cString], [tmp length]); + i += [tmp length]; + str_c[i++] = '\''; + + [pool releaseObjects]; } /* Childen */ if (stringval != nil || children != nil) { if (stringval != nil) @@ -163,21 +216,37 @@ } return ret; } -- addAttributeWithName: (OFString*)name_ - stringValue: (OFString*)value_ +- addAttribute: (OFXMLAttribute*)attr { if (attrs == nil) - attrs = [[OFMutableDictionary alloc] init]; + attrs = [[OFMutableArray alloc] init]; + + /* FIXME: Prevent having it twice! */ + + [attrs addObject: attr]; + + return self; +} - [attrs setObject: value_ - forKey: name_]; +- addAttributeWithName: (OFString*)name_ + stringValue: (OFString*)value +{ + OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; + [self addAttribute: [OFXMLAttribute attributeWithName: name_ + prefix: nil + namespace: nil + stringValue: value]]; + [pool release]; return self; } + +/* TODO: Replace attribute */ +/* TODO: Remove attribute */ - addChild: (OFXMLElement*)child { if (stringval != nil) @throw [OFInvalidArgumentException newWithClass: isa Index: src/OFXMLParser.h ================================================================== --- src/OFXMLParser.h +++ src/OFXMLParser.h @@ -9,11 +9,10 @@ * the packaging of this file. */ #import "OFObject.h" #import "OFString.h" -#import "OFDictionary.h" extern int _OFXMLParser_reference; @class OFXMLParser; @@ -20,11 +19,11 @@ @protocol OFXMLParserDelegate - (BOOL)xmlParser: (OFXMLParser*)parser didStartTagWithName: (OFString*)name prefix: (OFString*)prefix namespace: (OFString*)ns - attributes: (OFDictionary*)attrs; + attributes: (OFArray*)attrs; - (BOOL)xmlParser: (OFXMLParser*)parser didEndTagWithName: (OFString*)name prefix: (OFString*)prefix namespace: (OFString*)ns; - (BOOL)xmlParser: (OFXMLParser*)parser @@ -54,12 +53,13 @@ } state; OFString *cache; OFString *name; OFString *prefix; OFString *ns; - OFDictionary *attrs; + OFArray *attrs; OFString *attr_name; + OFString *attr_prefix; char delim; OFArray *previous; } + xmlParser; Index: src/OFXMLParser.m ================================================================== --- src/OFXMLParser.m +++ src/OFXMLParser.m @@ -97,10 +97,11 @@ [name release]; [prefix release]; [ns release]; [attrs release]; [attr_name release]; + [attr_prefix release]; [previous release]; [super dealloc]; } @@ -182,22 +183,20 @@ cache_c = [cache cString]; cache_len = [cache length]; if ((tmp = memchr(cache_c, ':', cache_len)) != NULL) { - prefix = [[OFString alloc] - initWithCString: cache_c - length: tmp - cache_c]; name = [[OFString alloc] initWithCString: tmp + 1 length: cache_len - (tmp - cache_c) - 1]; - } else { - prefix = nil; - name = [[OFString alloc] + prefix = [[OFString alloc] initWithCString: cache_c - length: cache_len]; + length: tmp - cache_c]; + } else { + name = [cache copy]; + prefix = nil; } if (buf[i] == '>' || buf[i] == '/') { pool = [[OFAutoreleasePool alloc] init]; @@ -246,22 +245,20 @@ cache_c = [cache cString]; cache_len = [cache length]; if ((tmp = memchr(cache_c, ':', cache_len)) != NULL) { - prefix = [[OFString alloc] - initWithCString: cache_c - length: tmp - cache_c]; name = [[OFString alloc] initWithCString: tmp + 1 length: cache_len - (tmp - cache_c) - 1]; - } else { - prefix = nil; - name = [[OFString alloc] + prefix = [[OFString alloc] initWithCString: cache_c - length: cache_len]; + length: tmp - cache_c]; + } else { + name = [cache copy]; + prefix = nil; } if (![[previous lastObject] isEqual: cache]) @throw [OFMalformedXMLException newWithClass: isa]; @@ -335,16 +332,35 @@ break; /* Looking for attribute name */ case OF_XMLPARSER_IN_ATTR_NAME: if (buf[i] == '=') { + const char *cache_c, *tmp; + size_t cache_len; + len = i - last; if (len > 0) [cache appendCString: buf + last withLength: len]; - attr_name = [cache copy]; + cache_c = [cache cString]; + cache_len = [cache length]; + + if ((tmp = memchr(cache_c, ':', + cache_len)) != NULL ) { + attr_name = [[OFString alloc] + initWithCString: tmp + 1 + length: cache_len - (tmp - + cache_c) - 1]; + attr_prefix = [[OFString alloc] + initWithCString: cache_c + length: tmp - cache_c]; + } else { + attr_name = [cache copy]; + attr_prefix = nil; + } + [cache setToCString: ""]; last = i + 1; state = OF_XMLPARSER_EXPECT_DELIM; } @@ -370,22 +386,27 @@ if (len > 0) [cache appendCString: buf + last withLength: len]; if (attrs == nil) - attrs = - [[OFMutableDictionary alloc] init]; + attrs = [[OFMutableArray alloc] init]; pool = [[OFAutoreleasePool alloc] init]; attr_val = [cache stringByXMLUnescapingWithHandler: self]; - [attrs setObject: attr_val - forKey: attr_name]; + [attrs addObject: [OFXMLAttribute + attributeWithName: attr_name + prefix: attr_prefix + namespace: nil + stringValue: attr_val]]; [pool release]; [cache setToCString: ""]; [attr_name release]; + [attr_prefix release]; + attr_name = nil; + attr_prefix = nil; last = i + 1; state = OF_XMLPARSER_IN_TAG; } break; Index: tests/OFXMLParser/OFXMLParser.m ================================================================== --- tests/OFXMLParser/OFXMLParser.m +++ tests/OFXMLParser/OFXMLParser.m @@ -22,29 +22,33 @@ @implementation ParserDelegate - (BOOL)xmlParser: (OFXMLParser*)parser didStartTagWithName: (OFString*)name prefix: (OFString*)prefix namespace: (OFString*)ns - attributes: (OFDictionary*)attrs + attributes: (OFArray*)attrs { + OFXMLAttribute **attrs_data; + size_t i, attrs_count; + printf("START\nname=\"%s\"\nprefix=\"%s\"\nns=\"%s\"\n", [name cString], [prefix cString], [ns cString]); - if (attrs) { - OFIterator *iter = [attrs iterator]; - - for (;;) { - of_iterator_pair_t pair; - - pair = [iter nextKeyObjectPair]; - - if (pair.key == nil || pair.object == nil) - break; - - printf("ATTR: \"%s\"=\"%s\"\n", - [pair.key cString], [pair.object cString]); - } + attrs_data = [attrs data]; + attrs_count = [attrs count]; + + for (i = 0; i < attrs_count; i++) { + OFString *attr_name = [attrs_data[i] name]; + OFString *attr_prefix = [attrs_data[i] prefix]; + OFString *attr_ns = [attrs_data[i] namespace]; + OFString *attr_value = [attrs_data[i] stringValue]; + + printf("ATTR:\n name=\"%s\"\n", [attr_name cString]); + if (attr_prefix != nil) + printf(" prefix=\"%s\"\n", [attr_prefix cString]); + if (attr_ns != nil) + printf(" ns=\"%s\"\n", [attr_ns cString]); + printf(" value=\"%s\"\n", [attr_value cString]); } puts(""); return YES; @@ -80,12 +84,13 @@ @end int main() { - const char *foo = "barfoo<bar" - "barquxbar"; + const char *foo = "bar" + "foo<barbarquxbar" + ""; size_t len = strlen(foo); size_t i; OFXMLParser *parser = [OFXMLParser xmlParser]; [parser setDelegate: [[ParserDelegate alloc] init]];