Răsfoiți Sursa

improved the streamer interface so it doesn't require text parent. now possible to connect visitor and streamer.

Lee Thomason 14 ani în urmă
părinte
comite
56bdd0259e
3 a modificat fișierele cu 206 adăugiri și 74 ștergeri
  1. 99 41
      tinyxml2.cpp
  2. 105 33
      tinyxml2.h
  3. 2 0
      xmltest.cpp

+ 99 - 41
tinyxml2.cpp

@@ -128,9 +128,9 @@ const char* StringPool::Intern( const char* str )
 */
 
 
-// --------- XMLBase ----------- //
+// --------- XMLUtil ----------- //
 
-char* XMLBase::ParseText( char* p, StrPair* pair, const char* endTag, int strFlags )
+char* StrPair::ParseText( char* p, const char* endTag, int strFlags )
 {
 	TIXMLASSERT( endTag && *endTag );
 
@@ -141,7 +141,7 @@ char* XMLBase::ParseText( char* p, StrPair* pair, const char* endTag, int strFla
 	// Inner loop of text parsing.
 	while ( *p ) {
 		if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) {
-			pair->Set( start, p, strFlags );
+			Set( start, p, strFlags );
 			return p + length;
 		}
 		++p;
@@ -150,7 +150,7 @@ char* XMLBase::ParseText( char* p, StrPair* pair, const char* endTag, int strFla
 }
 
 
-char* XMLBase::ParseName( char* p, StrPair* pair )
+char* StrPair::ParseName( char* p )
 {
 	char* start = p;
 
@@ -159,12 +159,12 @@ char* XMLBase::ParseName( char* p, StrPair* pair )
 		return 0;
 	}
 
-	if ( !IsAlpha( *p ) ) {
+	if ( !XMLUtil::IsAlpha( *p ) ) {
 		return 0;
 	}
 
 	while( *p && (
-			   IsAlphaNum( (unsigned char) *p ) 
+			   XMLUtil::IsAlphaNum( (unsigned char) *p ) 
 			|| *p == '_'
 			|| *p == '-'
 			|| *p == '.'
@@ -174,7 +174,7 @@ char* XMLBase::ParseName( char* p, StrPair* pair )
 	}
 
 	if ( p > start ) {
-		pair->Set( start, p, 0 );
+		Set( start, p, 0 );
 		return p;
 	}
 	return 0;
@@ -185,7 +185,7 @@ char* XMLDocument::Identify( char* p, XMLNode** node )
 {
 	XMLNode* returnNode = 0;
 	char* start = p;
-	p = XMLBase::SkipWhiteSpace( p );
+	p = XMLUtil::SkipWhiteSpace( p );
 	if( !p || !*p )
 	{
 		return 0;
@@ -210,18 +210,18 @@ char* XMLDocument::Identify( char* p, XMLNode** node )
 	static const int cdataHeaderLen		= 9;
 	static const int elementHeaderLen	= 1;
 
-	if ( XMLBase::StringEqual( p, commentHeader, commentHeaderLen ) ) {
+	if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) {
 		returnNode = new (commentPool.Alloc()) XMLComment( this );
 		returnNode->memPool = &commentPool;
 		p += commentHeaderLen;
 	}
-	else if ( XMLBase::StringEqual( p, elementHeader, elementHeaderLen ) ) {
+	else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) {
 		returnNode = new (elementPool.Alloc()) XMLElement( this );
 		returnNode->memPool = &elementPool;
 		p += elementHeaderLen;
 	}
 	// fixme: better text detection
-	else if ( (*p != '<') && XMLBase::IsAlphaNum( *p ) ) {
+	else if ( (*p != '<') && XMLUtil::IsAlphaNum( *p ) ) {
 		returnNode = new (textPool.Alloc()) XMLText( this );
 		returnNode->memPool = &textPool;
 		p = start;	// Back it up, all the text counts.
@@ -235,6 +235,20 @@ char* XMLDocument::Identify( char* p, XMLNode** node )
 }
 
 
+bool XMLDocument::Accept( XMLVisitor* visitor ) const
+{
+	if ( visitor->VisitEnter( *this ) )
+	{
+		for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() )
+		{
+			if ( !node->Accept( visitor ) )
+				break;
+		}
+	}
+	return visitor->VisitExit( *this );
+}
+
+
 // --------- XMLNode ----------- //
 
 XMLNode::XMLNode( XMLDocument* doc ) :
@@ -314,12 +328,26 @@ XMLNode* XMLNode::InsertEndChild( XMLNode* addThis )
 }
 
 
-XMLElement* XMLNode::FirstChildElement( const char* value )
+const XMLElement* XMLNode::FirstChildElement( const char* value ) const
 {
 	for( XMLNode* node=firstChild; node; node=node->next ) {
 		XMLElement* element = node->ToElement();
 		if ( element ) {
-			if ( !value || XMLBase::StringEqual( element->Name(), value ) ) {
+			if ( !value || XMLUtil::StringEqual( element->Name(), value ) ) {
+				return element;
+			}
+		}
+	}
+	return 0;
+}
+
+
+const XMLElement* XMLNode::LastChildElement( const char* value ) const
+{
+	for( XMLNode* node=lastChild; node; node=node->prev ) {
+		XMLElement* element = node->ToElement();
+		if ( element ) {
+			if ( !value || XMLUtil::StringEqual( element->Name(), value ) ) {
 				return element;
 			}
 		}
@@ -328,6 +356,13 @@ XMLElement* XMLNode::FirstChildElement( const char* value )
 }
 
 
+void XMLNode::DeleteChild( XMLNode* node )
+{
+	TIXMLASSERT( node->parent == this );
+	TIXMLASSERT( 0 );
+}
+
+
 void XMLNode::Print( XMLStreamer* streamer )
 {
 	for( XMLNode* node = firstChild; node; node=node->next ) {
@@ -357,7 +392,7 @@ char* XMLNode::ParseDeep( char* p )
 // --------- XMLText ---------- //
 char* XMLText::ParseDeep( char* p )
 {
-	p = XMLBase::ParseText( p, &value, "<", StrPair::TEXT_ELEMENT );
+	p = value.ParseText( p, "<", StrPair::TEXT_ELEMENT );
 	// consumes the end tag.
 	if ( p && *p ) {
 		return p-1;
@@ -373,6 +408,12 @@ void XMLText::Print( XMLStreamer* streamer )
 }
 
 
+bool XMLText::Accept( XMLVisitor* visitor ) const
+{
+	return visitor->Visit( *this );
+}
+
+
 // --------- XMLComment ---------- //
 
 XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc )
@@ -397,19 +438,25 @@ void XMLComment::Print( XMLStreamer* streamer )
 char* XMLComment::ParseDeep( char* p )
 {
 	// Comment parses as text.
-	return XMLBase::ParseText( p, &value, "-->", StrPair::COMMENT );
+	return value.ParseText( p, "-->", StrPair::COMMENT );
 }
 
 
+bool XMLComment::Accept( XMLVisitor* visitor ) const
+{
+	return visitor->Visit( *this );
+}
+
+
 // --------- XMLAttribute ---------- //
 char* XMLAttribute::ParseDeep( char* p )
 {
-	p = XMLBase::ParseText( p, &name, "=", StrPair::ATTRIBUTE_NAME );
+	p = name.ParseText( p, "=", StrPair::ATTRIBUTE_NAME );
 	if ( !p || !*p ) return 0;
 
 	char endTag[2] = { *p, 0 };
 	++p;
-	p = XMLBase::ParseText( p, &value, endTag, StrPair::ATTRIBUTE_VALUE );
+	p = value.ParseText( p, endTag, StrPair::ATTRIBUTE_VALUE );
 	if ( value.Empty() ) return 0;
 	return p;
 }
@@ -452,14 +499,14 @@ char* XMLElement::ParseAttributes( char* p, bool* closedElement )
 
 	// Read the attributes.
 	while( p ) {
-		p = XMLBase::SkipWhiteSpace( p );
+		p = XMLUtil::SkipWhiteSpace( p );
 		if ( !p || !(*p) ) {
 			document->SetError( XMLDocument::ERROR_PARSING_ELEMENT, start, Name() );
 			return 0;
 		}
 
 		// attribute.
-		if ( XMLBase::IsAlpha( *p ) ) {
+		if ( XMLUtil::IsAlpha( *p ) ) {
 			XMLAttribute* attrib = new (document->attributePool.Alloc() ) XMLAttribute( this );
 			attrib->memPool = &document->attributePool;
 
@@ -508,7 +555,7 @@ char* XMLElement::ParseAttributes( char* p, bool* closedElement )
 char* XMLElement::ParseDeep( char* p )
 {
 	// Read the element name.
-	p = XMLBase::SkipWhiteSpace( p );
+	p = XMLUtil::SkipWhiteSpace( p );
 	if ( !p ) return 0;
 	const char* start = p;
 
@@ -520,7 +567,7 @@ char* XMLElement::ParseDeep( char* p )
 		++p;
 	}
 
-	p = XMLBase::ParseName( p, &value );
+	p = value.ParseName( p );
 	if ( value.Empty() ) return 0;
 
 	bool elementClosed=false;
@@ -539,7 +586,7 @@ void XMLElement::Print( XMLStreamer* streamer )
 	//	PrintSpace( cfile, depth );
 	//}
 	//fprintf( cfile, "<%s", Name() );
-	streamer->OpenElement( Name(), IsTextParent() );
+	streamer->OpenElement( Name() );
 
 	for( XMLAttribute* attrib=rootAttribute; attrib; attrib=attrib->next ) {
 		//fprintf( cfile, " " );
@@ -554,6 +601,21 @@ void XMLElement::Print( XMLStreamer* streamer )
 }
 
 
+bool XMLElement::Accept( XMLVisitor* visitor ) const
+{
+	if ( visitor->VisitEnter( *this, rootAttribute ) ) 
+	{
+		for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() )
+		{
+			if ( !node->Accept( visitor ) )
+				break;
+		}
+	}
+	return visitor->VisitExit( *this );
+
+}
+
+
 // --------- XMLDocument ----------- //
 XMLDocument::XMLDocument() :
 	XMLNode( 0 ),
@@ -678,7 +740,7 @@ const char* StringStack::Pop() {
 */
 
 
-XMLStreamer::XMLStreamer( FILE* file ) : fp( file ), depth( 0 ), elementJustOpened( false )
+XMLStreamer::XMLStreamer( FILE* file ) : fp( file ), depth( 0 ), elementJustOpened( false ), textDepth( -1 )
 {
 	for( int i=0; i<ENTITY_RANGE; ++i ) {
 		entityFlag[i] = false;
@@ -733,18 +795,18 @@ void XMLStreamer::PrintString( const char* p )
 	}
 }
 
-void XMLStreamer::OpenElement( const char* name, bool textParent )
+void XMLStreamer::OpenElement( const char* name )
 {
 	if ( elementJustOpened ) {
 		SealElement();
 	}
-	if ( !TextOnStack() ) {
+	stack.Push( name );
+
+	if ( textDepth < 0 && depth > 0) {
+		fprintf( fp, "\n" );
 		PrintSpace( depth );
 	}
-	stack.Push( name );
-	text.Push( textParent ? 'T' : 'e' );
 
-	// fixme: can names have entities?
 	fprintf( fp, "<%s", name );
 	elementJustOpened = true;
 	++depth;
@@ -764,25 +826,22 @@ void XMLStreamer::CloseElement()
 {
 	--depth;
 	const char* name = stack.Pop();
-	bool wasText = TextOnStack();
-	text.Pop();
 
 	if ( elementJustOpened ) {
 		fprintf( fp, "/>" );
-		if ( !wasText ) {
-			fprintf( fp, "\n" );
-		}
 	}
 	else {
-		if ( !wasText ) {
+		if ( textDepth < 0 ) {
+			fprintf( fp, "\n" );
 			PrintSpace( depth );
 		}
-		// fixme can names have entities?
 		fprintf( fp, "</%s>", name );
-		if ( !TextOnStack() ) {
-			fprintf( fp, "\n" );
-		}
 	}
+
+	if ( textDepth == depth )
+		textDepth = -1;
+	if ( depth == 0 )
+		fprintf( fp, "\n" );
 	elementJustOpened = false;
 }
 
@@ -791,14 +850,13 @@ void XMLStreamer::SealElement()
 {
 	elementJustOpened = false;
 	fprintf( fp, ">" );
-	if ( !TextOnStack() ) {
-		fprintf( fp, "\n" );
-	}
 }
 
 
 void XMLStreamer::PushText( const char* text )
 {
+	textDepth = depth-1;
+
 	if ( elementJustOpened ) {
 		SealElement();
 	}

+ 105 - 33
tinyxml2.h

@@ -12,7 +12,7 @@
 	- make constructors protected
 	- hide copy constructor
 	- hide = operator
-	- UTF8 support: isAlpha, etc.
+	X UTF8 support: isAlpha, etc.
 */
 
 #include <limits.h>
@@ -74,6 +74,9 @@ public:
 	bool Empty() const { return start == end; }
 
 	void SetInternedStr( const char* str ) { this->start = (char*) str; this->end = 0; this->flags = 0; }
+	char* ParseText( char* in, const char* endTag, int strFlags );
+	char* ParseName( char* in );
+
 
 private:
 	enum {
@@ -278,16 +281,59 @@ private:
 };
 */
 
-class XMLBase
+
+/**
+	Implements the interface to the "Visitor pattern" (see the Accept() method.)
+	If you call the Accept() method, it requires being passed a XMLVisitor
+	class to handle callbacks. For nodes that contain other nodes (Document, Element)
+	you will get called with a VisitEnter/VisitExit pair. Nodes that are always leaves
+	are simply called with Visit().
+
+	If you return 'true' from a Visit method, recursive parsing will continue. If you return
+	false, <b>no children of this node or its sibilings</b> will be Visited.
+
+	All flavors of Visit methods have a default implementation that returns 'true' (continue 
+	visiting). You need to only override methods that are interesting to you.
+
+	Generally Accept() is called on the TiXmlDocument, although all nodes suppert Visiting.
+
+	You should never change the document from a callback.
+
+	@sa XMLNode::Accept()
+*/
+class XMLVisitor
 {
 public:
+	virtual ~XMLVisitor() {}
+
+	/// Visit a document.
+	virtual bool VisitEnter( const XMLDocument& /*doc*/ )			{ return true; }
+	/// Visit a document.
+	virtual bool VisitExit( const XMLDocument& /*doc*/ )			{ return true; }
+
+	/// Visit an element.
+	virtual bool VisitEnter( const XMLElement& /*element*/, const XMLAttribute* /*firstAttribute*/ )	{ return true; }
+	/// Visit an element.
+	virtual bool VisitExit( const XMLElement& /*element*/ )			{ return true; }
+
+	/// Visit a declaration
+	//virtual bool Visit( const TiXmlDeclaration& /*declaration*/ )	{ return true; }
+	/// Visit a text node
+	virtual bool Visit( const XMLText& /*text*/ )					{ return true; }
+	/// Visit a comment node
+	virtual bool Visit( const XMLComment& /*comment*/ )				{ return true; }
+	/// Visit an unknown node
+	//virtual bool Visit( const TiXmlUnknown& /*unknown*/ )			{ return true; }
+};
 
-public:
-	XMLBase() {}
-	virtual ~XMLBase() {}
 
-	static const char* SkipWhiteSpace( const char* p )	{ while( isspace( *p ) ) { ++p; } return p; }
-	static char* SkipWhiteSpace( char* p )				{ while( isspace( *p ) ) { ++p; } return p; }
+class XMLUtil
+{
+public:
+	// Anything in the high order range of UTF-8 is assumed to not be whitespace. This isn't 
+	// correct, but simple, and usually works.
+	static const char* SkipWhiteSpace( const char* p )	{ while( IsUTF8Continuation(*p) || isspace( *p ) ) { ++p; } return p; }
+	static char* SkipWhiteSpace( char* p )				{ while( IsUTF8Continuation(*p) || isspace( *p ) ) { ++p; } return p; }
 
 	inline static bool StringEqual( const char* p, const char* q, int nChar=INT_MAX )  {
 		int n = 0;
@@ -303,13 +349,8 @@ public:
 		return false;
 	}
 	inline static int IsUTF8Continuation( unsigned char p ) { return p & 0x80; }
-	inline static int IsAlphaNum( unsigned char anyByte )	{ return ( anyByte <= 127 ) ? isalnum( anyByte ) : 1; }
-	inline static int IsAlpha( unsigned char anyByte )		{ return ( anyByte <= 127 ) ? isalpha( anyByte ) : 1; }
-
-	static char* ParseText( char* in, StrPair* pair, const char* endTag, int strFlags );
-	static char* ParseName( char* in, StrPair* pair );
-
-private:
+	inline static int IsAlphaNum( unsigned char anyByte )	{ return ( anyByte < 128 ) ? isalnum( anyByte ) : 1; }
+	inline static int IsAlpha( unsigned char anyByte )		{ return ( anyByte < 128 ) ? isalpha( anyByte ) : 1; }
 };
 
 
@@ -318,25 +359,50 @@ class XMLNode
 	friend class XMLDocument;
 	friend class XMLElement;
 public:
-	//void* operator new( size_t size, MemPool* pool );
-	//void operator delete( void* mem, MemPool* pool );
+	XMLDocument* GetDocument()				{ return document; }
 
-	XMLNode* InsertEndChild( XMLNode* addThis );
-	virtual void Print( XMLStreamer* streamer );
+	virtual XMLElement*		ToElement()		{ return 0; }
+	virtual XMLText*		ToText()		{ return 0; }
+	virtual XMLComment*		ToComment()		{ return 0; }
+	virtual XMLDocument*	ToDocument()	{ return 0; }
 
 	const char* Value() const			{ return value.GetStr(); }
 	void SetValue( const char* val )	{ value.SetInternedStr( val ); }
 
-	virtual XMLElement* ToElement()		{ return 0; }
-	virtual XMLText*	ToText()		{ return 0; }
-	virtual XMLComment* ToComment()		{ return 0; }
+	const XMLNode*  FirstChild() const		{ return firstChild; }
+	XMLNode*		FirstChild()			{ return firstChild; }
+	const XMLElement* FirstChildElement( const char* value=0 ) const;
+	XMLElement* FirstChildElement( const char* value=0 )	{ return const_cast<XMLElement*>(const_cast<const XMLNode*>(this)->FirstChildElement( value )); }
 
-	XMLNode* FirstChild()	{ return firstChild; }
-	XMLElement* FirstChildElement( const char* value=0 );
+	const XMLNode*	LastChild() const						{ return lastChild; }
+	XMLNode*		LastChild()								{ return const_cast<XMLNode*>(const_cast<const XMLNode*>(this)->LastChild() ); }
 
-	// fixme: guarentee null terminator to avoid internal checks
-	virtual char* ParseDeep( char* );
+	const XMLElement* LastChildElement( const char* value=0 ) const;
+	XMLElement* LastChildElement( const char* value=0 )	{ return const_cast<XMLElement*>(const_cast<const XMLNode*>(this)->LastChildElement(value) ); }
+	
+	const XMLNode*	PreviousSibling() const					{ return prev; }
+	XMLNode*	PreviousSibling()							{ return prev; }
+
+	const XMLNode*	PreviousSiblingElement( const char* value=0 ) const ;
+	XMLNode*	PreviousSiblingElement( const char* value=0 ) { return const_cast<XMLNode*>(const_cast<const XMLNode*>(this)->PreviousSiblingElement( value ) ); }
+	
+	const XMLNode*	NextSibling() const						{ return next; }
+	XMLNode*	NextSibling()								{ return next; }
+		
+	const XMLNode*	NextSiblingElement( const char* value=0 ) const;
+ 	XMLNode*	NextSiblingElement( const char* value=0 )	{ return const_cast<XMLNode*>(const_cast<const XMLNode*>(this)->NextSiblingElement( value ) ); }
+
+	XMLNode* InsertEndChild( XMLNode* addThis );
+	XMLNode* InsertFirstChild( XMLNode* addThis );
+	XMLNode* InsertAfterChild( XMLNode* afterThis, XMLNode* addThis );
+	
+	void ClearChildren();
+	void DeleteChild( XMLNode* node );
 
+	virtual bool Accept( XMLVisitor* visitor ) const = 0;
+	virtual void Print( XMLStreamer* streamer );
+
+	virtual char* ParseDeep( char* );
 	void SetTextParent()		{ isTextParent = true; } 
 	bool IsTextParent() const	{ return isTextParent; }
 	virtual bool IsClosingElement() const { return false; }
@@ -345,8 +411,6 @@ protected:
 	XMLNode( XMLDocument* );
 	virtual ~XMLNode();
 	
-	void ClearChildren();
-
 	XMLDocument*	document;
 	XMLNode*		parent;
 	bool			isTextParent;
@@ -373,6 +437,7 @@ public:
 	const char* Value() { return value.GetStr(); }
 	void SetValue( const char* );
 
+	virtual bool Accept( XMLVisitor* visitor ) const;
 	virtual XMLText*	ToText()		{ return this; }
 
 	char* ParseDeep( char* );
@@ -394,6 +459,7 @@ public:
 	virtual XMLComment*	ToComment()		{ return this; }
 
 	const char* Value() { return value.GetStr(); }
+	virtual bool Accept( XMLVisitor* visitor ) const;
 
 	char* ParseDeep( char* );
 
@@ -405,7 +471,7 @@ private:
 };
 
 
-class XMLAttribute : public XMLBase
+class XMLAttribute
 {
 	friend class XMLElement;
 public:
@@ -434,6 +500,7 @@ public:
 	virtual void Print( XMLStreamer* );
 
 	virtual XMLElement* ToElement() { return this; }
+	virtual bool Accept( XMLVisitor* visitor ) const;
 
 	// internal:
 	virtual bool IsClosingElement() const { return closing; }
@@ -459,11 +526,14 @@ public:
 	XMLDocument(); 
 	~XMLDocument();
 
+	virtual XMLDocument*	ToDocument()	{ return this; }
+
 	int Parse( const char* );
 	int Load( const char* );
 	int Load( FILE* );
 
 	void Print( XMLStreamer* streamer=0 );
+	virtual bool Accept( XMLVisitor* visitor ) const;
 
 	XMLElement* NewElement( const char* name );
 
@@ -500,13 +570,13 @@ private:
 };
 
 
-class XMLStreamer
+class XMLStreamer 
 {
 public:
 	XMLStreamer( FILE* file );
 	~XMLStreamer()	{}
 
-	void OpenElement( const char* name, bool textParent );
+	void OpenElement( const char* name );
 	void PushAttribute( const char* name, const char* value );
 	void CloseElement();
 
@@ -517,24 +587,26 @@ private:
 	void SealElement();
 	void PrintSpace( int depth );
 	void PrintString( const char* );	// prints out, after detecting entities.
-	bool TextOnStack() const { 
+/*	bool TextOnStack() const { 
 		for( int i=0; i<text.Size(); ++i ) { 
 			if ( text[i] == 'T' ) 
 				return true; 
 		} 
 		return false; 
-	}
+	}*/
 
 	FILE* fp;
 	int depth;
 	bool elementJustOpened;
+	int textDepth;
+
 	enum {
 		ENTITY_RANGE = 64
 	};
 	bool entityFlag[ENTITY_RANGE];
 
 	DynArray< const char*, 10 > stack;
-	DynArray< char, 10 > text;
+	//DynArray< char, 10 > text;
 };
 
 

+ 2 - 0
xmltest.cpp

@@ -52,6 +52,7 @@ int main( int argc, const char* argv )
 			printf( "----------------------------------------------\n" );
 		}
 	}
+#if 0
 	{
 		static const char* test = "<element>Text before.</element>";
 		XMLDocument doc;
@@ -67,5 +68,6 @@ int main( int argc, const char* argv )
 		doc->Parse( test );
 		delete doc;
 	}
+#endif
 	return 0;
 }