diff --git a/tinyxml2.cpp b/tinyxml2.cpp index 4ce9b32..fbeca4e 100644 --- a/tinyxml2.cpp +++ b/tinyxml2.cpp @@ -1092,34 +1092,33 @@ const char* XMLElement::GetText() const } - XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name ) { - XMLAttribute* attrib = FindAttribute( name ); + XMLAttribute* last = 0; + XMLAttribute* attrib = 0; + for( attrib = rootAttribute; + attrib; + last = attrib, attrib = attrib->next ) + { + if ( XMLUtil::StringEqual( attrib->Name(), name ) ) { + break; + } + } if ( !attrib ) { attrib = new (document->attributePool.Alloc() ) XMLAttribute(); attrib->memPool = &document->attributePool; - LinkAttribute( attrib ); + if ( last ) { + last->next = attrib; + } + else { + rootAttribute = attrib; + } attrib->SetName( name ); } return attrib; } -void XMLElement::LinkAttribute( XMLAttribute* attrib ) -{ - if ( rootAttribute ) { - XMLAttribute* end = rootAttribute; - while ( end->next ) - end = end->next; - end->next = attrib; - } - else { - rootAttribute = attrib; - } -} - - void XMLElement::DeleteAttribute( const char* name ) { XMLAttribute* prev = 0; @@ -1142,6 +1141,7 @@ void XMLElement::DeleteAttribute( const char* name ) char* XMLElement::ParseAttributes( char* p ) { const char* start = p; + XMLAttribute* prevAttribute = 0; // Read the attributes. while( p ) { @@ -1162,7 +1162,19 @@ char* XMLElement::ParseAttributes( char* p ) document->SetError( XML_ERROR_PARSING_ATTRIBUTE, start, p ); return 0; } - LinkAttribute( attrib ); + // There is a minor bug here: if the attribute in the source xml + // document is duplicated, it will not be detected and the + // attribute will be doubly added. However, tracking the 'prevAttribute' + // avoids re-scanning the attribute list. Preferring performance for + // now, may reconsider in the future. + if ( prevAttribute ) { + prevAttribute->next = attrib; + prevAttribute = attrib; + } + else { + rootAttribute = attrib; + prevAttribute = rootAttribute; + } } // end of the tag else if ( *p == '/' && *(p+1) == '>' ) { diff --git a/tinyxml2.h b/tinyxml2.h index 76e47ac..4ebb87f 100644 --- a/tinyxml2.h +++ b/tinyxml2.h @@ -24,25 +24,13 @@ distribution. #ifndef TINYXML2_INCLUDED #define TINYXML2_INCLUDED -#if 1 - #include - #include - #include - #include - #include -#else - // Not completely sure all the interesting systems - // can handle the new headers; can switch this if - // there is an include problem. - #include - #include - #include - #include // Needed by mac. -#endif - +#include +#include +#include +#include +#include /* - TODO: add 'lastAttribute' for faster parsing. TODO: intern strings instead of allocation. */ /* @@ -978,10 +966,13 @@ private: XMLAttribute* FindAttribute( const char* name ); XMLAttribute* FindOrCreateAttribute( const char* name ); - void LinkAttribute( XMLAttribute* attrib ); + //void LinkAttribute( XMLAttribute* attrib ); char* ParseAttributes( char* p ); int closingType; + // The attribute list is ordered; there is no 'lastAttribute' + // because the list needs to be scanned for dupes before adding + // a new attribute. XMLAttribute* rootAttribute; }; diff --git a/xmltest.cpp b/xmltest.cpp index 0991e2d..a88d1bc 100644 --- a/xmltest.cpp +++ b/xmltest.cpp @@ -760,6 +760,32 @@ int main( int /*argc*/, const char ** /*argv*/ ) } XMLTest( "Error in snprinf handling.", true, doc.Error() ); } + + { + // Attribute ordering. + static const char* xml = ""; + XMLDocument doc; + doc.Parse( xml ); + XMLElement* ele = doc.FirstChildElement(); + + const XMLAttribute* a = ele->FirstAttribute(); + XMLTest( "Attribute order", "1", a->Value() ); + a = a->Next(); + XMLTest( "Attribute order", "2", a->Value() ); + a = a->Next(); + XMLTest( "Attribute order", "3", a->Value() ); + XMLTest( "Attribute order", "attrib3", a->Name() ); + + ele->DeleteAttribute( "attrib2" ); + a = ele->FirstAttribute(); + XMLTest( "Attribute order", "1", a->Value() ); + a = a->Next(); + XMLTest( "Attribute order", "3", a->Value() ); + + ele->DeleteAttribute( "attrib1" ); + ele->DeleteAttribute( "attrib3" ); + XMLTest( "Attribute order (empty)", false, ele->FirstAttribute() ? true : false ); + } // -------- Handles ------------ {