Index: src/OFXMLElement.h ================================================================== --- src/OFXMLElement.h +++ src/OFXMLElement.h @@ -207,35 +207,74 @@ */ - (OFString*)stringValue; /** * Adds the specified attribute. + * + * If an attribute with the same name and namespace already exists, it is not + * added. * * \param attr The attribute to add */ - (void)addAttribute: (OFXMLAttribute*)attr; /** * Adds the specified attribute with the specified value. + * + * If an attribute with the same name and namespace already exists, it is not + * added. * * \param name The name of the attribute * \param value The value of the attribute */ - (void)addAttributeWithName: (OFString*)name stringValue: (OFString*)value; /** * Adds the specified attribute with the specified namespace and value. + * + * If an attribute with the same name and namespace already exists, it is not + * added. * * \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; +/** + * \param attrname The name of the attribute + * \return The attribute with the specified name + */ +- (OFXMLAttribute*)attributeForName: (OFString*)attrname; + +/** + * \param attrname The name of the attribute + * \param attrns The namespace of the attribute + * \return The attribute with the specified name and namespace + */ +- (OFXMLAttribute*)attributeForName: (OFString*)attrname + namespace: (OFString*)attrns; + +/** + * Removes the attribute with the specified name. + * + * \param attrname The name of the attribute + */ +- (void)removeAttributeForName: (OFString*)attrname; + +/** + * Removes the attribute with the specified name and namespace. + * + * \param attrname The name of the attribute + * \param attrns The namespace of the attribute + */ +- (void)removeAttributeForName: (OFString*)attrname + namespace: (OFString*)attrns; + /** * Sets a prefix for a namespace. * * \param prefix The prefix for the namespace * \param ns The namespace for which the prefix is set Index: src/OFXMLElement.m ================================================================== --- src/OFXMLElement.m +++ src/OFXMLElement.m @@ -422,29 +422,21 @@ selector: _cmd]; if (attributes == nil) attributes = [[OFMutableArray alloc] init]; - /* FIXME: Prevent having it twice! */ - - [attributes addObject: attr]; + if ([self attributeForName: attr->name + namespace: attr->ns] == nil) + [attributes addObject: attr]; } - (void)addAttributeWithName: (OFString*)name_ stringValue: (OFString*)value { - OFAutoreleasePool *pool; - - if (name == nil) - @throw [OFInvalidArgumentException newWithClass: isa - selector: _cmd]; - - pool = [[OFAutoreleasePool alloc] init]; - [self addAttribute: [OFXMLAttribute attributeWithName: name_ - namespace: nil - stringValue: value]]; - [pool release]; + [self addAttributeWithName: name_ + namespace: nil + stringValue: value]; } - (void)addAttributeWithName: (OFString*)name_ namespace: (OFString*)ns_ stringValue: (OFString*)value @@ -460,12 +452,69 @@ namespace: ns_ stringValue: value]]; [pool release]; } -/* TODO: Replace attribute */ -/* TODO: Remove attribute */ +- (OFXMLAttribute*)attributeForName: (OFString*)attrname +{ + return [self attributeForName: attrname + namespace: nil]; +} + +- (OFXMLAttribute*)attributeForName: (OFString*)attrname + namespace: (OFString*)attrns +{ + OFXMLAttribute **attrs_c = [attributes cArray]; + size_t i, attrs_count = [attributes count]; + + if (attrns != nil) { + for (i = 0; i < attrs_count; i++) + if ([attrs_c[i]->ns isEqual: attrns] && + [attrs_c[i]->name isEqual: attrname]) + return attrs_c[i]; + } else { + for (i = 0; i < attrs_count; i++) + if (attrs_c[i]->ns == nil && + [attrs_c[i]->name isEqual: attrname]) + return attrs_c[i]; + } + + return nil; +} + +- (void)removeAttributeForName: (OFString*)attrname +{ + [self removeAttributeForName: attrname + namespace: nil]; +} + +- (void)removeAttributeForName: (OFString*)attrname + namespace: (OFString*)attrns +{ + OFXMLAttribute **attrs_c = [attributes cArray]; + size_t i, attrs_count = [attributes count]; + + if (attrns != nil) { + for (i = 0; i < attrs_count; i++) { + if ([attrs_c[i]->ns isEqual: attrns] && + [attrs_c[i]->name isEqual: attrname]) { + [attributes removeObjectAtIndex: i]; + + return; + } + } + } else { + for (i = 0; i < attrs_count; i++) { + if (attrs_c[i]->ns == nil && + [attrs_c[i]->name isEqual: attrname]) { + [attributes removeObjectAtIndex: i]; + + return; + } + } + } +} - (void)setPrefix: (OFString*)prefix forNamespace: (OFString*)ns_ { if (name == nil || prefix == nil || [prefix isEqual: @""]) Index: tests/OFXMLElementTests.m ================================================================== --- tests/OFXMLElementTests.m +++ tests/OFXMLElementTests.m @@ -92,13 +92,24 @@ TEST(@"-[addAttributeWithName:namespace:stringValue:]", R([elem[1] addAttributeWithName: @"foo" namespace: @"urn:objfw:test" stringValue: @"bar"]) && + R([elem[1] addAttributeWithName: @"foo" + namespace: @"urn:objfw:test" + stringValue: @"ignored"]) && [[elem[1] stringValue] isEqual: @"b&ar"]) + TEST(@"-[removeAttributeForName:namespace:]", + R([elem[1] removeAttributeForName: @"foo"]) && + [[elem[1] stringValue] isEqual: + @"b&ar"] && + R([elem[1] removeAttributeForName: @"foo" + namespace: @"urn:objfw:test"]) && + [[elem[1] stringValue] isEqual: @"b&ar"]) + TEST(@"-[addChild:]", R([elem[0] addChild: [OFXMLElement elementWithName: @"bar"]]) && [[elem[0] stringValue] isEqual: @""] && R([elem[2] addChild: [OFXMLElement elementWithName: @"bar"