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

new element parsing. reasonable set of test cases.

Lee Thomason 14 лет назад
Родитель
Сommit
67d6131d74
4 измененных файлов с 98 добавлено и 72 удалено
  1. 82 64
      tinyxml2.cpp
  2. 14 8
      tinyxml2.h
  3. BIN
      tinyxml2.suo
  4. 2 0
      xmltest.cpp

+ 82 - 64
tinyxml2.cpp

@@ -186,6 +186,7 @@ char* XMLBase::Identify( XMLDocument* document, char* p, XMLNode** node )
 XMLNode::XMLNode( XMLDocument* doc ) :
 	document( doc ),
 	parent( 0 ),
+	isTextParent( false ),
 	firstChild( 0 ), lastChild( 0 ),
 	prev( 0 ), next( 0 )
 {
@@ -242,6 +243,9 @@ XMLNode* XMLNode::InsertEndChild( XMLNode* addThis )
 		addThis->prev = 0;
 		addThis->next = 0;
 	}
+	if ( addThis->ToText() ) {
+		SetTextParent();
+	}
 	return addThis;
 }
 
@@ -254,6 +258,25 @@ void XMLNode::Print( FILE* fp, int depth )
 }
 
 
+char* XMLNode::ParseDeep( char* p )
+{
+	while( p && *p ) {
+		XMLNode* node = 0;
+		p = Identify( document, p, &node );
+		if ( p && node ) {
+			p = node->ParseDeep( p );
+			// FIXME: is it the correct closing element?
+			if ( node->IsClosingElement() ) {
+				delete node;
+				return p;
+			}
+			this->InsertEndChild( node );
+		}
+	}
+	return 0;
+}
+
+
 void XMLNode::PrintSpace( FILE* fp, int depth ) 
 {
 	for( int i=0; i<depth; ++i ) {
@@ -276,7 +299,8 @@ char* XMLText::ParseDeep( char* p )
 
 void XMLText::Print( FILE* cfile, int depth )
 {
-	fprintf( cfile, value.GetStr() );
+	const char* v = value.GetStr();
+	fprintf( cfile, v );
 }
 
 
@@ -350,23 +374,10 @@ XMLElement::~XMLElement()
 }
 
 
-char* XMLElement::ParseDeep( char* p )
+char* XMLElement::ParseAttributes( char* p, bool* closedElement )
 {
-	// Read the element name.
-	p = SkipWhiteSpace( p );
-	if ( !p ) return 0;
 	const char* start = p;
-
-	// The closing element is the </element> form. It is
-	// parsed just like a regular element then deleted from
-	// the DOM.
-	if ( *p == '/' ) {
-		closing = true;
-		++p;
-	}
-
-	p = ParseName( p, &name );
-	if ( name.Empty() ) return 0;
+	*closedElement = false;
 
 	// Read the attributes.
 	while( p ) {
@@ -400,6 +411,7 @@ char* XMLElement::ParseDeep( char* p )
 				document->SetError( XMLDocument::ERROR_PARSING_ELEMENT, start, p );
 				return 0;
 			}
+			*closedElement = true;
 			return p+2;	// done; sealed element.
 		}
 		// end of the tag
@@ -412,38 +424,47 @@ char* XMLElement::ParseDeep( char* p )
 			return 0;
 		}
 	}
+	return p;
+}
 
-	while( p && *p ) {
-		XMLNode* node = 0;
-		p = Identify( document, p, &node );
-		if ( p && node ) {
-			p = node->ParseDeep( p );
 
-			XMLElement* element = node->ToElement();
-			if ( element && element->Closing() ) {
-				if ( StringEqual( element->Name(), this->Name() ) ) {
-					// All good, this is closing tag.
-					delete node;
-				}
-				else {
-					document->SetError( XMLDocument::ERROR_PARSING_ELEMENT, start, p );
-					delete node;
-					p = 0;
-				}
-				return p;
-			}
-			else {
-				this->InsertEndChild( node );
-			}
-		}
+//
+//	<ele></ele>
+//	<ele>foo<b>bar</b></ele>
+//
+char* XMLElement::ParseDeep( char* p )
+{
+	// Read the element name.
+	p = SkipWhiteSpace( p );
+	if ( !p ) return 0;
+	const char* start = p;
+
+	// The closing element is the </element> form. It is
+	// parsed just like a regular element then deleted from
+	// the DOM.
+	if ( *p == '/' ) {
+		closing = true;
+		++p;
 	}
-	return 0;
+
+	p = ParseName( p, &name );
+	if ( name.Empty() ) return 0;
+
+	bool elementClosed=false;
+	p = ParseAttributes( p, &elementClosed );
+	if ( !p || !*p || elementClosed || closing ) 
+		return p;
+
+	p = XMLNode::ParseDeep( p );
+	return p;
 }
 
 
 void XMLElement::Print( FILE* cfile, int depth )
 {
-	PrintSpace( cfile, depth );
+	if ( !parent || !parent->IsTextParent() ) {
+		PrintSpace( cfile, depth );
+	}
 	fprintf( cfile, "<%s", Name() );
 
 	for( XMLAttribute* attrib=rootAttribute; attrib; attrib=attrib->next ) {
@@ -452,38 +473,39 @@ void XMLElement::Print( FILE* cfile, int depth )
 	}
 
 	if ( firstChild ) {
-		// fixme: once text is on, it should stay on, and not use newlines.
-		bool useNewline = firstChild->ToText() == 0;
-
 		fprintf( cfile, ">", Name() );
-		if ( useNewline ) fprintf( cfile, "\n" );
+		if ( !IsTextParent() ) {
+			fprintf( cfile, "\n" );
+		}
 
 		for( XMLNode* node=firstChild; node; node=node->next ) {
 			node->Print( cfile, depth+1 );
 		}
 
-		fprintf( cfile, "</%s>\n", Name() );
-		// fixme: see note above
-		//if ( useNewline ) fprintf( cfile, "\n" );
+		fprintf( cfile, "</%s>", Name() );
+		if ( !IsTextParent() ) {
+			fprintf( cfile, "\n" );
+		}
 	}
 	else {
-		fprintf( cfile, "/>\n" );
+		fprintf( cfile, "/>" );
+		if ( !IsTextParent() ) {
+			fprintf( cfile, "\n" );
+		}
 	}
 }
 
 
 // --------- XMLDocument ----------- //
-XMLDocument::XMLDocument() : 
+XMLDocument::XMLDocument() :
+	XMLNode( this ),
 	charBuffer( 0 )
 {
-	root = new XMLNode( this );
 }
 
 
 XMLDocument::~XMLDocument()
 {
-	delete root;
-	delete charBuffer;
 }
 
 
@@ -493,25 +515,21 @@ bool XMLDocument::Parse( const char* p )
 	charBuffer = CharBuffer::Construct( p );
 	XMLNode* node = 0;
 	
-	// fixme: clean up
-	char* q = Identify( this, charBuffer->mem, &node );
-	while ( node ) {
-		root->InsertEndChild( node );
-		q = node->ParseDeep( q );
-		node = 0;
-		if ( q && *q ) {
-			q = Identify( this, q, &node );
-		}
-	}
-	return false;
+	char* q = ParseDeep( charBuffer->mem );
+	return true;
 }
 
 
 void XMLDocument::Print( FILE* fp, int depth ) 
 {
-	for( XMLNode* node = root->firstChild; node; node=node->next ) {
+	for( XMLNode* node = firstChild; node; node=node->next ) {
 		node->Print( fp, depth );
 	}
 }
 
 
+void XMLDocument::SetError( int error, const char* str1, const char* str2 )
+{
+	printf( "ERROR: id=%d '%s' '%s'\n", error, str1, str2 );
+}
+

+ 14 - 8
tinyxml2.h

@@ -106,7 +106,6 @@ protected:
 	char* Identify( XMLDocument* document, char* p, XMLNode** node );
 };
 
-
 class XMLNode : public XMLBase
 {
 	friend class XMLDocument;
@@ -121,7 +120,12 @@ public:
 	virtual XMLText*	ToText()		{ return 0; }
 	virtual XMLComment* ToComment()		{ return 0; }
 
-	virtual char* ParseDeep( char* )	{ TIXMLASSERT( 0 ); }
+	// fixme: guarentee null terminator to avoid internal checks
+	virtual char* ParseDeep( char* );
+
+	void SetTextParent()		{ isTextParent = true; } 
+	bool IsTextParent() const	{ return isTextParent; }
+	virtual bool IsClosingElement() const { return false; }
 
 protected:
 	XMLNode( XMLDocument* );
@@ -129,6 +133,7 @@ protected:
 
 	XMLDocument*	document;
 	XMLNode*		parent;
+	bool			isTextParent;
 
 	XMLNode*		firstChild;
 	XMLNode*		lastChild;
@@ -208,13 +213,15 @@ public:
 	virtual void Print( FILE* cfile, int depth );
 
 	virtual XMLElement* ToElement() { return this; }
-	bool Closing() const			{ return closing; }
+	virtual bool IsClosingElement() const { return closing; }
 
 	char* ParseDeep( char* p );
 
 protected:
 
 private:
+	char* ParseAttributes( char* p, bool *closedElement );
+
 	StrPair name;
 	bool closing;
 	XMLAttribute* rootAttribute;
@@ -222,7 +229,7 @@ private:
 };
 
 
-class XMLDocument : public XMLBase
+class XMLDocument : public XMLNode
 {
 public:
 	XMLDocument();
@@ -231,20 +238,19 @@ public:
 	bool Parse( const char* );
 	void Print( FILE* cfile=stdout, int depth=0 );
 
+	/*
 	XMLNode* Root()				{ return root; }
 	XMLNode* RootElement();
-
+	*/
 	enum {
 		ERROR_ELEMENT_MISMATCH,
 		ERROR_PARSING_ELEMENT,
 		ERROR_PARSING_ATTRIBUTE
 	};
-	void SetError( int error, const char* str1, const char* str2 )	{}
+	void SetError( int error, const char* str1, const char* str2 );
 
 private:
 	XMLDocument( const XMLDocument& );	// intentionally not implemented
-
-	XMLNode*	root;
 	CharBuffer* charBuffer;
 };
 


+ 2 - 0
xmltest.cpp

@@ -39,6 +39,8 @@ int main( int argc, const char* argv )
 									    "<element attrib1='foo' attrib2=\"bar\" ></element>",
 									    "<element attrib1='foo' attrib2=\"bar\" ><subelement attrib3='yeehaa' /></element>",
 										"<element>Text inside element.</element>",
+										"<element><b></b></element>",
+										"<element>Text inside and <b>bolded</b> in the element.</element>",
 										0
 		};
 		for( int i=0; test[i]; ++i ) {