Index: src/OFExceptions.h ================================================================== --- src/OFExceptions.h +++ src/OFExceptions.h @@ -1150,5 +1150,66 @@ /** * \brief An exception indicating that the hash has already been calculated. */ @interface OFHashAlreadyCalculatedException: OFException {} @end + +/** + * \brief An exception indicating an attempt to use an unbound namespace. + */ +@interface OFUnboundNamespaceException: OFException +{ + OFString *namespace; + OFString *prefix; +} + +#ifdef OF_HAVE_PROPERTIES +@property (readonly, nonatomic) OFString *namespace; +@property (readonly, nonatomic) OFString *prefix; +#endif + +/** + * \param class_ The class of the object which caused the exception + * \param namespace The namespace which is unbound + * \return A new unbound namespace exception + */ ++ newWithClass: (Class)class_ + namespace: (OFString*)namespace; + +/** + * \param class_ The class of the object which caused the exception + * \param prefix The prefix which is unbound + * \return A new unbound namespace exception + */ ++ newWithClass: (Class)class_ + prefix: (OFString*)prefix; + +/** + * Initializes an already allocated unbound namespace failed exception + * + * \param class_ The class of the object which caused the exception + * \param namespace The namespace which is unbound + * \return An initialized unbound namespace exception + */ +- initWithClass: (Class)class_ + namespace: (OFString*)namespace; + +/** + * Initializes an already allocated unbound namespace failed exception + * + * \param class_ The class of the object which caused the exception + * \param prefix The prefix which is unbound + * \return An initialized unbound namespace exception + */ +- initWithClass: (Class)class_ + prefix: (OFString*)prefix; + +/** + * \return The unbound namespace + */ +- (OFString*)namespace; + +/** + * \return The unbound prefix + */ +- (OFString*)prefix; +@end Index: src/OFExceptions.m ================================================================== --- src/OFExceptions.m +++ src/OFExceptions.m @@ -1510,5 +1510,82 @@ @"data can be added", [inClass className]]; return string; } @end + +@implementation OFUnboundNamespaceException ++ newWithClass: (Class)class_ + namespace: (OFString*)namespace +{ + return [[self alloc] initWithClass: class_ + namespace: namespace]; +} + ++ newWithClass: (Class)class_ + prefix: (OFString*)prefix +{ + return [[self alloc] initWithClass: class_ + prefix: prefix]; +} + +- initWithClass: (Class)class_ +{ + @throw [OFNotImplementedException newWithClass: isa + selector: _cmd]; +} + +- initWithClass: (Class)class_ + namespace: (OFString*)namespace_ +{ + self = [super initWithClass: class_]; + + namespace = [namespace_ copy]; + + return self; +} + +- initWithClass: (Class)class_ + prefix: (OFString*)prefix_ +{ + self = [super initWithClass: class_]; + + prefix = [prefix_ copy]; + + return self; +} + +- (void)dealloc +{ + [namespace release]; + [prefix release]; + + [super dealloc]; +} + +- (OFString*)string +{ + if (string != nil) + return string; + + if (namespace != nil) + string = [[OFString alloc] initWithFormat: + @"The namespace %s is not bound in class %s", + [inClass className]]; + else if (prefix != nil) + string = [[OFString alloc] initWithFormat: + @"The prefix %s is not bound to any namespace in %s", + [inClass className]]; + + return string; +} + +- (OFString*)namespace +{ + return namespace; +} + +- (OFString*)prefix +{ + return prefix; +} +@end Index: src/OFXMLElement.h ================================================================== --- src/OFXMLElement.h +++ src/OFXMLElement.h @@ -10,69 +10,58 @@ */ #import "OFObject.h" #import "OFString.h" -@class OFDictionary; @class OFMutableArray; +@class OFMutableDictionary; extern int _OFXMLElement_reference; /** * \brief A representation of an attribute of an XML element as an object. */ @interface OFXMLAttribute: OFObject { - OFString *prefix; OFString *name; OFString *namespace; OFString *stringValue; } #ifdef OF_HAVE_PROPERTIES -@property (readonly, retain) OFString *prefix; @property (readonly, retain) OFString *name; @property (readonly, retain) OFString *namespace; @property (readonly, retain) OFString *stringValue; #endif /** * \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; @@ -89,50 +78,100 @@ * modified and converted back to XML again. */ @interface OFXMLElement: OFObject { OFString *name; + OFString *namespace; + OFString *defaultNamespace; OFMutableArray *attributes; OFString *stringValue; + OFMutableDictionary *namespaces; OFMutableArray *children; } /** * \param name The name for the element - * \return A new autorelease OFXMLElement with the specified element name + * \return A new autoreleased OFXMLElement with the specified element name */ + elementWithName: (OFString*)name; /** * \param name The name for the element * \param stringval The value for the element - * \return A new autorelease OFXMLElement with the specified element name and + * \return A new autoreleased OFXMLElement with the specified element name and * value */ + elementWithName: (OFString*)name stringValue: (OFString*)stringval; /** - * Initializes an already allocated OFXMLElement with the specified name. + * \param name The name for the element + * \param ns The namespace for the element + * \return A new autoreleased OFXMLElement with the specified element name and + * namespace + */ ++ elementWithName: (OFString*)name + namespace: (OFString*)ns; + +/** + * \param name The name for the element + * \param ns The namespace for the element + * \param stringval The value for the element + * \return A new autoreleased OFXMLElement with the specified element name, + * namespace and value + */ ++ elementWithName: (OFString*)name + namespace: (OFString*)ns + stringValue: (OFString*)stringval; + +/** + * Initializes an already allocated OFXMLElement with the specified element + * name. * * \param name The name for the element * \return An initialized OFXMLElement with the specified element name */ - initWithName: (OFString*)name; /** - * Initializes an already allocated OFXMLElement with the specified name and - * value. + * Initializes an already allocated OFXMLElement with the specified element + * name and value. * * \param name The name for the element * \param stringval The value for the element * \return An initialized OFXMLElement with the specified element name and * value */ - initWithName: (OFString*)name stringValue: (OFString*)stringval; +/** + * Initializes an already allocated OFXMLElement with the specified element + * name and namespace. + * + * \param name The name for the element + * \param ns The namespace for the element + * \return An initialized OFXMLElement with the specified element name and + * namespace + */ +- initWithName: (OFString*)name + namespace: (OFString*)ns; + +/** + * Initializes an already allocated OFXMLElement with the specified element + * name, namespace and value. + * + * \param name The name for the element + * \param ns The namespace for the element + * \param stringval The value for the element + * \return An initialized OFXMLElement with the specified element name, + * namespace and value + */ +- initWithName: (OFString*)name + namespace: (OFString*)ns + stringValue: (OFString*)stringval; + /** * \return A new autoreleased OFString representing the OFXMLElement as an * XML string */ - (OFString*)string; @@ -151,10 +190,37 @@ * \param value The value of the attribute */ - (void)addAttributeWithName: (OFString*)name stringValue: (OFString*)value; +/** + * Adds the specified attribute with the specified namespace and value. + * + * \param name The name of the attribute + * \param ns The namespace of the attribute + * \param value The value of the attribute + */ +- (void)addAttributeWithName: (OFString*)name + namespace: (OFString*)ns + stringValue: (OFString*)value; + +/** + * Sets a prefix for a namespace. + * + * \param prefix The prefix for the namespace + * \param ns The namespace for which the prefix is set + */ +- (void)setPrefix: (OFString*)prefix + forNamespace: (OFString*)ns; + +/** + * Sets the default namespace for the element. + * + * \param ns The default namespace for the element + */ +- (void)setDefaultNamespace: (OFString*)ns; + /** * Adds a child to the OFXMLElement. * * \param child Another OFXMLElement which is added as a child */ Index: src/OFXMLElement.m ================================================================== --- src/OFXMLElement.m +++ src/OFXMLElement.m @@ -16,46 +16,42 @@ #include #import "OFXMLElement.h" #import "OFString.h" #import "OFArray.h" +#import "OFDictionary.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]; namespace = [ns copy]; stringValue = [value copy]; return self; } - (void)dealloc { [name release]; - [prefix release]; [namespace release]; [stringValue release]; [super dealloc]; } @@ -63,15 +59,10 @@ - (OFString*)name { return [[name copy] autorelease]; } -- (OFString*)prefix -{ - return [[prefix copy] autorelease]; -} - - (OFString*)namespace { return [[namespace copy] autorelease]; } @@ -91,82 +82,185 @@ stringValue: (OFString*)stringval { return [[[self alloc] initWithName: name stringValue: stringval] autorelease]; } + ++ elementWithName: (OFString*)name + namespace: (OFString*)ns +{ + return [[[self alloc] initWithName: name + namespace: ns] autorelease]; +} + ++ elementWithName: (OFString*)name + namespace: (OFString*)ns + stringValue: (OFString*)stringval +{ + return [[[self alloc] initWithName: name + namespace: ns + stringValue: stringval] autorelease]; +} - init { @throw [OFNotImplementedException newWithClass: isa selector: _cmd]; } - initWithName: (OFString*)name_ { - self = [super init]; + return [self initWithName: name_ + namespace: nil + stringValue: nil]; +} - name = [name_ copy]; +- initWithName: (OFString*)name_ + stringValue: (OFString*)stringval +{ + return [self initWithName: name_ + namespace: nil + stringValue: stringval]; +} - return self; +- initWithName: (OFString*)name_ + namespace: (OFString*)ns +{ + return [self initWithName: name_ + namespace: ns + stringValue: nil]; } - initWithName: (OFString*)name_ + namespace: (OFString*)ns stringValue: (OFString*)stringval { self = [super init]; name = [name_ copy]; + namespace = [ns copy]; stringValue = [stringval copy]; + + namespaces = [[OFMutableDictionary alloc] initWithKeysAndObjects: + @"http://www.w3.org/XML/1998/namespace", @"xml", + @"http://www.w3.org/2000/xmlns/", @"xmlns", nil]; return self; } -- (OFString*)string +- (OFString*)_stringWithParentNamespaces: (OFDictionary*)parent_namespaces + parentDefaultNamespace: (OFString*)parent_default_ns { - OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; + OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init], *pool2; char *str_c; size_t len, i, j, attrs_count; + OFString *prefix = nil; OFXMLAttribute **attrs_carray; OFString *ret, *tmp; + OFMutableDictionary *all_namespaces; + OFString *def_ns; + + def_ns = (defaultNamespace != nil + ? defaultNamespace : parent_default_ns); + + if (parent_namespaces != nil) { + OFEnumerator *key_enum = [namespaces keyEnumerator]; + OFEnumerator *obj_enum = [namespaces objectEnumerator]; + id key, obj; + + all_namespaces = [[parent_namespaces mutableCopy] autorelease]; + + while ((key = [key_enum nextObject]) != nil && + (obj = [obj_enum nextObject]) != nil) + [all_namespaces setObject: obj + forKey: key]; + } else + all_namespaces = namespaces; + i = 0; len = [name cStringLength] + 3; str_c = [self allocMemoryWithSize: len]; /* Start of tag */ - *str_c = '<'; - memcpy(str_c + 1, [name cString], [name cStringLength]); - i = [name cStringLength] + 1; + str_c[i++] = '<'; + + if ((namespace == nil && def_ns != nil) || + (namespace != nil && def_ns == nil) || + (namespace != nil && ![namespace isEqual: def_ns])) { + if ((prefix = [all_namespaces objectForKey: + (namespace != nil ? namespace : @"")]) == nil) + @throw [OFUnboundNamespaceException + newWithClass: isa + namespace: namespace]; + + len += [prefix cStringLength] + 1; + @try { + str_c = [self resizeMemory: str_c + toSize: len]; + } @catch (OFException *e) { + [self freeMemory: str_c]; + @throw e; + } + + memcpy(str_c + i, [prefix cString], + [prefix cStringLength]); + i += [prefix cStringLength]; + str_c[i++] = ':'; + } + + memcpy(str_c + i, [name cString], [name cStringLength]); + i += [name cStringLength]; /* Attributes */ attrs_carray = [attributes cArray]; attrs_count = [attributes count]; + pool2 = [[OFAutoreleasePool alloc] init]; for (j = 0; j < attrs_count; j++) { - /* FIXME: Add namespace support */ OFString *attr_name = [attrs_carray[j] name]; + OFString *attr_prefix = nil; tmp = [[attrs_carray[j] stringValue] stringByXMLEscaping]; - len += [attr_name cStringLength] + [tmp cStringLength] + 4; + if (([attrs_carray[j] namespace] == nil && namespace != nil) || + ([attrs_carray[j] namespace] != nil && namespace == nil) || + ([attrs_carray[j] namespace] != nil && + ![[attrs_carray[j] namespace] isEqual: namespace])) + if ((attr_prefix = [all_namespaces + objectForKey: [attrs_carray[j] namespace]]) == nil) + @throw [OFUnboundNamespaceException + newWithClass: isa + namespace: [attrs_carray[j] namespace]]; + + len += [attr_name cStringLength] + + (attr_prefix != nil ? [attr_prefix cStringLength] + 1 : 0) + + [tmp cStringLength] + 4; + @try { str_c = [self resizeMemory: str_c toSize: len]; } @catch (OFException *e) { [self freeMemory: str_c]; @throw e; } str_c[i++] = ' '; + if (attr_prefix != nil) { + memcpy(str_c + i, [attr_prefix cString], + [attr_prefix cStringLength]); + i += [attr_prefix cStringLength]; + str_c[i++] = ':'; + } memcpy(str_c + i, [attr_name cString], [attr_name cStringLength]); i += [attr_name cStringLength]; str_c[i++] = '='; str_c[i++] = '\''; memcpy(str_c + i, [tmp cString], [tmp cStringLength]); i += [tmp cStringLength]; str_c[i++] = '\''; - [pool releaseObjects]; + [pool2 releaseObjects]; } /* Childen */ if (stringValue != nil || children != nil) { if (stringValue != nil) @@ -181,11 +275,15 @@ @selector(appendCStringWithoutUTF8Checking:)]; for (j = 0; j < children_count; j++) append(tmp, @selector( appendCStringWithoutUTF8Checking:), - [[children_carray[j] string] cString]); + [[children_carray[j] + _stringWithParentNamespaces: + all_namespaces + parentDefaultNamespace: defaultNamespace] + cString]); } len += [tmp cStringLength] + [name cStringLength] + 2; @try { str_c = [self resizeMemory: str_c @@ -198,10 +296,25 @@ str_c[i++] = '>'; memcpy(str_c + i, [tmp cString], [tmp cStringLength]); i += [tmp cStringLength]; str_c[i++] = '<'; str_c[i++] = '/'; + if (prefix != nil) { + len += [prefix cStringLength] + 1; + @try { + str_c = [self resizeMemory: str_c + toSize: len]; + } @catch (OFException *e) { + [self freeMemory: str_c]; + @throw e; + } + + memcpy(str_c + i, [prefix cString], + [prefix cStringLength]); + i += [prefix cStringLength]; + str_c[i++] = ':'; + } memcpy(str_c + i, [name cString], [name cStringLength]); i += [name cStringLength]; } else str_c[i++] = '/'; @@ -216,10 +329,16 @@ } @finally { [self freeMemory: str_c]; } return ret; } + +- (OFString*)string +{ + return [self _stringWithParentNamespaces: nil + parentDefaultNamespace: nil]; +} - (void)addAttribute: (OFXMLAttribute*)attr { if (attributes == nil) attributes = [[OFMutableArray alloc] init]; @@ -232,18 +351,53 @@ - (void)addAttributeWithName: (OFString*)name_ stringValue: (OFString*)value { OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; [self addAttribute: [OFXMLAttribute attributeWithName: name_ - prefix: nil namespace: nil stringValue: value]]; [pool release]; } + +- (void)addAttributeWithName: (OFString*)name_ + namespace: (OFString*)ns + stringValue: (OFString*)value +{ + OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; + [self addAttribute: [OFXMLAttribute attributeWithName: name_ + namespace: ns + stringValue: value]]; + [pool release]; +} /* TODO: Replace attribute */ /* TODO: Remove attribute */ + +- (void)setPrefix: (OFString*)prefix + forNamespace: (OFString*)ns +{ + if (prefix == nil || [prefix isEqual: @""]) + @throw [OFInvalidArgumentException newWithClass: isa + selector: _cmd]; + if (ns == nil) + ns = @""; + + [namespaces setObject: prefix + forKey: ns]; +} + +- (OFString*)defaultNamespace +{ + return [[defaultNamespace retain] autorelease]; +} + +- (void)setDefaultNamespace: (OFString*)ns +{ + OFString *old = defaultNamespace; + defaultNamespace = [ns copy]; + [old release]; +} - (void)addChild: (OFXMLElement*)child { if (stringValue != nil) @throw [OFInvalidArgumentException newWithClass: isa @@ -256,12 +410,14 @@ } - (void)dealloc { [name release]; + [namespace release]; [attributes release]; [stringValue release]; + [namespaces release]; [children release]; [super dealloc]; } @end Index: src/OFXMLParser.h ================================================================== --- src/OFXMLParser.h +++ src/OFXMLParser.h @@ -125,17 +125,21 @@ OF_XMLPARSER_IN_COMMENT_4 } state; OFMutableString *cache; OFString *name; OFString *prefix; - OFString *ns; + OFMutableArray *namespaces; OFMutableArray *attrs; OFString *attrName; OFString *attrPrefix; char delim; OFMutableArray *previous; } + +#ifdef OF_HAVE_PROPERTIES +@property (retain) OFObject *delegate; +#endif /** * \return A new, autoreleased OFXMLParser */ + xmlParser; Index: src/OFXMLParser.m ================================================================== --- src/OFXMLParser.m +++ src/OFXMLParser.m @@ -10,14 +10,16 @@ */ #include "config.h" #include +#include #import "OFXMLParser.h" #import "OFString.h" #import "OFArray.h" +#import "OFDictionary.h" #import "OFAutoreleasePool.h" #import "OFExceptions.h" #import "macros.h" int _OFXMLParser_reference; @@ -29,10 +31,29 @@ /* TODO: Support for xml:space */ [cache removeLeadingAndTrailingWhitespaces]; return [cache stringByXMLUnescapingWithHandler: handler]; } + +static OF_INLINE OFString* +namespace_for_prefix(OFString *prefix, OFArray *namespaces) +{ + OFDictionary **carray = [namespaces cArray]; + ssize_t i; + + if (prefix == nil) + prefix = @""; + + for (i = [namespaces count] - 1; i >= 0; i--) { + OFString *tmp; + + if ((tmp = [carray[i] objectForKey: prefix]) != nil) + return tmp; + } + + return nil; +} static OF_INLINE OFString* parse_numeric_entity(const char *entity, size_t length) { of_unichar_t c; @@ -89,14 +110,24 @@ - init { self = [super init]; @try { + OFAutoreleasePool *pool; + OFMutableDictionary *dict; + cache = [[OFMutableString alloc] init]; previous = [[OFMutableArray alloc] init]; + namespaces = [[OFMutableArray alloc] init]; + + pool = [[OFAutoreleasePool alloc] init]; + dict = [OFMutableDictionary dictionaryWithKeysAndObjects: + @"xml", @"http://www.w3.org/XML/1998/namespace", + @"xmlns", @"http://www.w3.org/2000/xmlns/", nil]; + [namespaces addObject: dict]; + [pool release]; } @catch (OFException *e) { - /* We can't use [super dealloc] on OS X here. Compiler bug? */ [self dealloc]; @throw e; } return self; @@ -107,11 +138,11 @@ [delegate release]; [cache release]; [name release]; [prefix release]; - [ns release]; + [namespaces release]; [attrs release]; [attrName release]; [attrPrefix release]; [previous release]; @@ -207,11 +238,21 @@ name = [cache copy]; prefix = nil; } if (buf[i] == '>' || buf[i] == '/') { + OFString *ns; + pool = [[OFAutoreleasePool alloc] init]; + ns = namespace_for_prefix(prefix, + namespaces); + + if (prefix != nil && ns == nil) + @throw + [OFUnboundNamespaceException + newWithClass: isa + prefix: prefix]; [delegate xmlParser: self didStartTagWithName: name prefix: prefix namespace: ns @@ -228,18 +269,24 @@ [pool release]; [name release]; [prefix release]; - [ns release]; - name = prefix = ns = nil; + name = prefix = nil; state = (buf[i] == '/' ? OF_XMLPARSER_EXPECT_CLOSE : OF_XMLPARSER_OUTSIDE_TAG); } else state = OF_XMLPARSER_IN_TAG; + + if (buf[i] != '/') { + pool = [[OFAutoreleasePool alloc] init]; + [namespaces addObject: + [OFMutableDictionary dictionary]]; + [pool release]; + } [cache setToCString: ""]; last = i + 1; } break; @@ -248,10 +295,11 @@ case OF_XMLPARSER_IN_CLOSE_TAG_NAME: if (buf[i] == ' ' || buf[i] == '\n' || buf[i] == '\r' || buf[i] == '>') { const char *cache_c, *tmp; size_t cache_len; + OFString *ns; len = i - last; if (len > 0) [cache appendCString: buf + last withLength: len]; @@ -279,47 +327,61 @@ [cache setToCString: ""]; pool = [[OFAutoreleasePool alloc] init]; + ns = namespace_for_prefix(prefix, namespaces); + if (prefix != nil && ns == nil) + @throw [OFUnboundNamespaceException + newWithClass: isa + prefix: prefix]; + [namespaces removeNObjects: 1]; + [delegate xmlParser: self didEndTagWithName: name prefix: prefix namespace: ns]; [pool release]; [name release]; [prefix release]; - [ns release]; - name = prefix = ns = nil; + name = prefix = nil; last = i + 1; - state = (buf[i] == ' ' || buf[i] == '\n' || - buf[i] == '\r' - ? OF_XMLPARSER_EXPECT_SPACE_OR_CLOSE - : OF_XMLPARSER_OUTSIDE_TAG); + state = (buf[i] == '>' + ? OF_XMLPARSER_OUTSIDE_TAG + : OF_XMLPARSER_EXPECT_SPACE_OR_CLOSE); } break; /* Inside a tag, name found */ case OF_XMLPARSER_IN_TAG: if (buf[i] == '>' || buf[i] == '/') { + OFString *ns; + pool = [[OFAutoreleasePool alloc] init]; + ns = namespace_for_prefix(prefix, namespaces); + + if (prefix != nil && ns == nil) + @throw [OFUnboundNamespaceException + newWithClass: isa + prefix: prefix]; [delegate xmlParser: self didStartTagWithName: name prefix: prefix namespace: ns attributes: attrs]; - if (buf[i] == '/') + if (buf[i] == '/') { [delegate xmlParser: self didEndTagWithName: name prefix: prefix namespace: ns]; - else if (prefix != nil) { + [namespaces removeNObjects: 1]; + } else if (prefix != nil) { OFString *str = [OFString stringWithFormat: @"%s:%s", [prefix cString], [name cString]]; [previous addObject: str]; @@ -328,13 +390,12 @@ [pool release]; [name release]; [prefix release]; - [ns release]; [attrs release]; - name = prefix = ns = nil; + name = prefix = nil; attrs = nil; last = i + 1; state = (buf[i] == '/' ? OF_XMLPARSER_EXPECT_CLOSE @@ -394,28 +455,45 @@ break; /* Looking for attribute value */ case OF_XMLPARSER_IN_ATTR_VALUE: if (buf[i] == delim) { + OFString *attr_ns; OFString *attr_val; len = i - last; if (len > 0) [cache appendCString: buf + last withLength: len]; + pool = [[OFAutoreleasePool alloc] init]; + attr_ns = namespace_for_prefix( + (attrPrefix != nil ? attrPrefix : prefix), + namespaces); + attr_val = [cache + stringByXMLUnescapingWithHandler: self]; + + if (attrPrefix == nil && + [attrName isEqual: @"xmlns"]) { + [[namespaces lastObject] + setObject: attr_val + forKey: @""]; + attr_ns = nil; + } + if ([attrPrefix isEqual: @"xmlns"]) + [[namespaces lastObject] + setObject: attr_val + forKey: attrName]; + if (attrs == nil) attrs = [[OFMutableArray alloc] init]; - pool = [[OFAutoreleasePool alloc] init]; - attr_val = [cache - stringByXMLUnescapingWithHandler: self]; [attrs addObject: [OFXMLAttribute attributeWithName: attrName - prefix: attrPrefix - namespace: nil + namespace: attr_ns stringValue: attr_val]]; + [pool release]; [cache setToCString: ""]; [attrName release]; [attrPrefix release]; Index: tests/OFXMLElementTests.m ================================================================== --- tests/OFXMLElementTests.m +++ tests/OFXMLElementTests.m @@ -24,11 +24,11 @@ @implementation TestsAppDelegate (OFXMLElementTests) - (void)XMLElementTests { OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; - OFXMLElement *elem[2]; + OFXMLElement *elem[4]; TEST(@"+[elementWithName:]", (elem[0] = [OFXMLElement elementWithName: @"foo"]) && [[elem[0] string] isEqual: @""]) @@ -35,20 +35,54 @@ TEST(@"+[elementWithName:stringValue:]", (elem[1] = [OFXMLElement elementWithName: @"foo" stringValue: @"b&ar"]) && [[elem[1] string] isEqual: @"b&ar"]) + TEST(@"+[elementWithName:namespace:]", + (elem[2] = [OFXMLElement elementWithName: @"foo" + namespace: @"urn:objfw:test"]) && + R([elem[2] addAttributeWithName: @"test" + namespace: @"urn:objfw:test" + stringValue: @"test"]) && + R([elem[2] setPrefix: @"objfw-test" + forNamespace: @"urn:objfw:test"]) && + [[elem[2] string] isEqual: @""]) + + TEST(@"+[elementWithName:namespace:stringValue:]", + (elem[3] = [OFXMLElement elementWithName: @"foo" + namespace: @"urn:objfw:test" + stringValue: @"x"]) && + R([elem[3] setPrefix: @"objfw-test" + forNamespace: @"urn:objfw:test"]) && + [[elem[3] string] isEqual: @"x"]) + TEST(@"-[addAttributeWithName:stringValue:]", R([elem[0] addAttributeWithName: @"foo" stringValue: @"b&ar"]) && [[elem[0] string] isEqual: @""] && R([elem[1] addAttributeWithName: @"foo" stringValue: @"b&ar"]) && [[elem[1] string] isEqual: @"b&ar"]) + TEST(@"-[setPrefix:forNamespace:]", + R([elem[1] setPrefix: @"objfw-test" + forNamespace: @"urn:objfw:test"])) + + TEST(@"-[addAttributeWithName:namespace:stringValue:]", + R([elem[1] addAttributeWithName: @"foo" + namespace: @"urn:objfw:test" + stringValue: @"bar"]) && + [[elem[1] string] isEqual: + @"b&ar"]) + TEST(@"-[addChild:]", R([elem[0] addChild: [OFXMLElement elementWithName: @"bar"]]) && - [[elem[0] string] isEqual: @""]) + [[elem[0] string] isEqual: @""] && + R([elem[2] addChild: + [OFXMLElement elementWithName: @"bar" + namespace: @"urn:objfw:test"]]) && + [[elem[2] string] isEqual: + @""]) [pool drain]; } @end Index: tests/OFXMLParserTests.m ================================================================== --- tests/OFXMLParserTests.m +++ tests/OFXMLParserTests.m @@ -40,85 +40,187 @@ attributes: (OFArray*)attrs string: (OFString*)string comment: (OFString*)comment { OFString *msg; - id *carray; - size_t count; i++; msg = [OFString stringWithFormat: @"Parsing part #%d", i]; switch (i) { case 1: - case 5: - TEST(msg, et == STRING && [string isEqual: @"bar"]) + TEST(msg, et == STRING && [string isEqual: @"foo"]) break; case 2: - /* FIXME: Namespace */ - carray = [attrs cArray]; - count = [attrs count]; - TEST(msg, et == TAG_START && [name isEqual: @"bar"] && - [prefix isEqual: @"foo"] && ns == nil && - attrs != nil && count == 2 && - /* Attribute 1 */ - [[carray[0] name] isEqual: @"bar"] && - [carray[0] prefix] == nil && - [[carray[0] stringValue] isEqual: @"b&az"] && - [carray[0] namespace] == nil && - /* Attribute 2 */ - [[carray[1] name] isEqual: @"qux"] && - [[carray[1] prefix] isEqual: @"qux"] && - [[carray[1] stringValue] isEqual: @" quux "] && - [carray[1] namespace] == nil) + prefix == nil && ns == nil && attrs == nil) break; case 3: - TEST(msg, et == STRING && [string isEqual: @"foo\r\nfoo<barbar quxbar\r\n"; + const char *str = "foo\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + ""; size_t j, len; TEST(@"+[xmlParser]", (parser = [OFXMLParser xmlParser])) TEST(@"-[setDelegate:]", R([parser setDelegate: self])) @@ -206,10 +317,10 @@ else [parser parseBuffer: str + j withSize: 2]; } - TEST(@"Checking if everything was parsed", i == 11) + TEST(@"Checking if everything was parsed", i == 26) [pool drain]; } @end