@@ -30,10 +30,12 @@ #import "OFInvalidArgumentException.h" #import "OFMalformedXMLException.h" #import "OFNotImplementedException.h" #import "OFUnboundNamespaceException.h" + +#import "macros.h" @interface OFXMLElement_OFXMLElementBuilderDelegate: OFObject { @public OFXMLElement *element; @@ -42,11 +44,18 @@ @implementation OFXMLElement_OFXMLElementBuilderDelegate - (void)elementBuilder: (OFXMLElementBuilder*)builder didBuildElement: (OFXMLElement*)elem { - element = [elem retain]; + /* + * Make sure we don't take whitespaces before or after the root element + * into account. + */ + if ([elem name] != nil) { + assert(element == nil); + element = [elem retain]; + } } - (void)dealloc { [element release]; @@ -212,11 +221,13 @@ { OFAutoreleasePool *pool; OFXMLParser *parser; OFXMLElementBuilder *builder; OFXMLElement_OFXMLElementBuilderDelegate *delegate; + Class c; + c = isa; [self release]; pool = [[OFAutoreleasePool alloc] init]; parser = [OFXMLParser parser]; @@ -228,11 +239,11 @@ [builder setDelegate: delegate]; [parser parseString: str]; if (![parser finishedParsing]) - @throw [OFMalformedXMLException newWithClass: isa + @throw [OFMalformedXMLException newWithClass: c parser: parser]; self = [delegate->element retain]; @try { @@ -324,18 +335,18 @@ ret->isa = [OFString class]; return ret; } - (OFString*)_XMLStringWithParent: (OFXMLElement*)parent + namespaces: (OFDictionary*)all_namespaces { OFAutoreleasePool *pool, *pool2; char *str_c; size_t len, i, j, attrs_count; OFString *prefix, *parent_prefix; OFXMLAttribute **attrs_carray; OFString *ret, *tmp; - OFMutableDictionary *all_namespaces; OFString *def_ns; if (characters != nil) return [characters stringByXMLEscaping]; @@ -358,43 +369,49 @@ str->isa = [OFString class]; return str; } pool = [[OFAutoreleasePool alloc] init]; - def_ns = (defaultNamespace != nil - ? defaultNamespace - : (parent != nil ? parent->defaultNamespace : (OFString*)nil)); - if (parent != nil && parent->namespaces != nil) { + parent_prefix = [all_namespaces objectForKey: + (parent != nil && parent->ns != nil ? parent->ns : (OFString*)@"")]; + + if (all_namespaces != nil) { OFEnumerator *key_enum = [namespaces keyEnumerator]; OFEnumerator *obj_enum = [namespaces objectEnumerator]; + OFMutableDictionary *mutable; id key, obj; - all_namespaces = [[parent->namespaces mutableCopy] autorelease]; + mutable = [[all_namespaces mutableCopy] autorelease]; while ((key = [key_enum nextObject]) != nil && (obj = [obj_enum nextObject]) != nil) - [all_namespaces setObject: obj - forKey: key]; + [mutable setObject: obj + forKey: key]; + + all_namespaces = mutable; } else all_namespaces = namespaces; prefix = [all_namespaces objectForKey: (ns != nil ? ns : (OFString*)@"")]; - parent_prefix = [all_namespaces objectForKey: - (parent != nil && parent->ns != nil ? parent->ns : (OFString*)@"")]; + + if (parent != nil && parent->ns != nil && parent_prefix == nil) + def_ns = parent->ns; + else if (parent != nil && parent->defaultNamespace != nil) + def_ns = parent->defaultNamespace; + else + def_ns = defaultNamespace; i = 0; len = [name cStringLength] + 3; str_c = [self allocMemoryWithSize: len]; /* Start of tag */ str_c[i++] = '<'; - if (prefix != nil && ![ns isEqual: def_ns] && - (![ns isEqual: (parent != nil ? parent->ns : (OFString*)nil)] || - parent_prefix != nil)) { + if (prefix != nil && ![ns isEqual: def_ns]) { len += [prefix cStringLength] + 1; @try { str_c = [self resizeMemory: str_c toSize: len]; } @catch (id e) { @@ -410,13 +427,12 @@ memcpy(str_c + i, [name cString], [name cStringLength]); i += [name cStringLength]; /* xmlns if necessary */ - if (ns != nil && prefix == nil && ![ns isEqual: def_ns] && - (![ns isEqual: (parent != nil ? parent->ns : (OFString*)nil)] || - parent_prefix != nil)) { + if (prefix == nil && ((ns != nil && ![ns isEqual: def_ns]) || + (ns == nil && def_ns != nil))) { len += [ns cStringLength] + 9; @try { str_c = [self resizeMemory: str_c toSize: len]; @@ -428,12 +444,10 @@ memcpy(str_c + i, " xmlns='", 8); i += 8; memcpy(str_c + i, [ns cString], [ns cStringLength]); i += [ns cStringLength]; str_c[i++] = '\''; - - def_ns = ns; } /* Attributes */ attrs_carray = [attributes cArray]; attrs_count = [attributes count]; @@ -494,11 +508,12 @@ for (j = 0; j < children_count; j++) append(tmp, @selector( appendCStringWithoutUTF8Checking:), [[children_carray[j] - _XMLStringWithParent: self] cString]); + _XMLStringWithParent: self + namespaces: all_namespaces] cString]); len += [tmp cStringLength] + [name cStringLength] + 2; @try { str_c = [self resizeMemory: str_c toSize: len]; @@ -546,11 +561,12 @@ return ret; } - (OFString*)XMLString { - return [self _XMLStringWithParent: nil]; + return [self _XMLStringWithParent: nil + namespaces: nil]; } - (OFString*)description { return [self XMLString]; @@ -748,15 +764,116 @@ [ret addObject: children_c[i]]; } return ret; } + +- (BOOL)isEqual: (id)object +{ + OFXMLElement *other; + + if (![object isKindOfClass: [OFXMLElement class]]) + return NO; + + other = object; + + if (other->name != name && ![other->name isEqual: name]) + return NO; + if (other->ns != ns && ![other->ns isEqual: ns]) + return NO; + if (other->defaultNamespace != defaultNamespace && + ![other->defaultNamespace isEqual: defaultNamespace]) + return NO; + if (other->attributes != attributes && + ![other->attributes isEqual: attributes]) + return NO; + if (other->namespaces != namespaces && + ![other->namespaces isEqual: namespaces]) + return NO; + if (other->children != children && ![other->children isEqual: children]) + return NO; + if (other->characters != characters && + ![other->characters isEqual: characters]) + return NO; + if (other->cdata != cdata && ![other->cdata isEqual: cdata]) + return NO; + if (other->comment != comment && ![other->comment isEqual: comment]) + return NO; + + return YES; +} + +- (uint32_t)hash +{ + uint32_t hash, tmp; + + OF_HASH_INIT(hash); + + tmp = [name hash]; + OF_HASH_ADD(hash, (tmp >> 24) & 0xFF); + OF_HASH_ADD(hash, (tmp >> 16) & 0xFF); + OF_HASH_ADD(hash, (tmp >> 8) & 0xFF); + OF_HASH_ADD(hash, tmp & 0xFF); + + tmp = [ns hash]; + OF_HASH_ADD(hash, (tmp >> 24) & 0xFF); + OF_HASH_ADD(hash, (tmp >> 16) & 0xFF); + OF_HASH_ADD(hash, (tmp >> 8) & 0xFF); + OF_HASH_ADD(hash, tmp & 0xFF); + + tmp = [defaultNamespace hash]; + OF_HASH_ADD(hash, (tmp >> 24) & 0xFF); + OF_HASH_ADD(hash, (tmp >> 16) & 0xFF); + OF_HASH_ADD(hash, (tmp >> 8) & 0xFF); + OF_HASH_ADD(hash, tmp & 0xFF); + + tmp = [attributes hash]; + OF_HASH_ADD(hash, (tmp >> 24) & 0xFF); + OF_HASH_ADD(hash, (tmp >> 16) & 0xFF); + OF_HASH_ADD(hash, (tmp >> 8) & 0xFF); + OF_HASH_ADD(hash, tmp & 0xFF); + + tmp = [namespaces hash]; + OF_HASH_ADD(hash, (tmp >> 24) & 0xFF); + OF_HASH_ADD(hash, (tmp >> 16) & 0xFF); + OF_HASH_ADD(hash, (tmp >> 8) & 0xFF); + OF_HASH_ADD(hash, tmp & 0xFF); + + tmp = [children hash]; + OF_HASH_ADD(hash, (tmp >> 24) & 0xFF); + OF_HASH_ADD(hash, (tmp >> 16) & 0xFF); + OF_HASH_ADD(hash, (tmp >> 8) & 0xFF); + OF_HASH_ADD(hash, tmp & 0xFF); + + tmp = [characters hash]; + OF_HASH_ADD(hash, (tmp >> 24) & 0xFF); + OF_HASH_ADD(hash, (tmp >> 16) & 0xFF); + OF_HASH_ADD(hash, (tmp >> 8) & 0xFF); + OF_HASH_ADD(hash, tmp & 0xFF); + + tmp = [cdata hash]; + OF_HASH_ADD(hash, (tmp >> 24) & 0xFF); + OF_HASH_ADD(hash, (tmp >> 16) & 0xFF); + OF_HASH_ADD(hash, (tmp >> 8) & 0xFF); + OF_HASH_ADD(hash, tmp & 0xFF); + + tmp = [comment hash]; + OF_HASH_ADD(hash, (tmp >> 24) & 0xFF); + OF_HASH_ADD(hash, (tmp >> 16) & 0xFF); + OF_HASH_ADD(hash, (tmp >> 8) & 0xFF); + OF_HASH_ADD(hash, tmp & 0xFF); + + OF_HASH_FINALIZE(hash); + + return hash; +} - (void)dealloc { [name release]; [ns release]; + [defaultNamespace release]; [attributes release]; [namespaces release]; [children release]; [characters release]; [cdata release];