Просмотр исходного кода

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

Lee Thomason 14 лет назад
Родитель
Сommit
5e3803cac3
3 измененных файлов с 65 добавлено и 36 удалено
  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 ------------
 	{