Explorar o código

Better attribute system. Removes redundant list searching of LinkAttribute. Faster parsing on initial read. Added badly needed missing test cases.

Lee Thomason %!s(int64=14) %!d(string=hai) anos
pai
achega
5e3803cac3
Modificáronse 3 ficheiros con 65 adicións e 36 borrados
  1. 30 18
      tinyxml2.cpp
  2. 9 18
      tinyxml2.h
  3. 26 0
      xmltest.cpp

+ 30 - 18
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) == '>' ) {

+ 9 - 18
tinyxml2.h

@@ -24,25 +24,13 @@ distribution.
 #ifndef TINYXML2_INCLUDED
 #define TINYXML2_INCLUDED
 
-#if 1
-	#include <cctype>
-	#include <climits>
-	#include <cstdio>
-	#include <cstring>
-	#include <cstdarg>
-#else
-	// Not completely sure all the interesting systems
-	// can handle the new headers; can switch this if
-	// there is an include problem.
-	#include <limits.h>
-	#include <ctype.h>
-	#include <stdio.h>
-	#include <memory.h>		// Needed by mac.
-#endif
-
+#include <cctype>
+#include <climits>
+#include <cstdio>
+#include <cstring>
+#include <cstdarg>
 
 /* 
-   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;
 };
 

+ 26 - 0
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 = "<element attrib1=\"1\" attrib2=\"2\" attrib3=\"3\" />";
+		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 ------------
 	{