improved the streamer interface so it doesn't require text parent. now possible to connect visitor and streamer.
diff --git a/tinyxml2.cpp b/tinyxml2.cpp
index fd11c27..72f9db8 100644
--- a/tinyxml2.cpp
+++ b/tinyxml2.cpp
@@ -128,9 +128,9 @@
*/
-// --------- 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 @@
// 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::ParseName( char* p, StrPair* pair )
+char* StrPair::ParseName( char* p )
{
char* start = p;
@@ -159,12 +159,12 @@
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 @@
}
if ( p > start ) {
- pair->Set( start, p, 0 );
+ Set( start, p, 0 );
return p;
}
return 0;
@@ -185,7 +185,7 @@
{
XMLNode* returnNode = 0;
char* start = p;
- p = XMLBase::SkipWhiteSpace( p );
+ p = XMLUtil::SkipWhiteSpace( p );
if( !p || !*p )
{
return 0;
@@ -210,18 +210,18 @@
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 @@
}
+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,12 @@
}
-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;
}
}
@@ -328,6 +342,27 @@
}
+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;
+ }
+ }
+ }
+ return 0;
+}
+
+
+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 @@
// --------- 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 @@
}
+bool XMLText::Accept( XMLVisitor* visitor ) const
+{
+ return visitor->Visit( *this );
+}
+
+
// --------- XMLComment ---------- //
XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc )
@@ -397,19 +438,25 @@
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 @@
// 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::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 @@
++p;
}
- p = XMLBase::ParseName( p, &value );
+ p = value.ParseName( p );
if ( value.Empty() ) return 0;
bool elementClosed=false;
@@ -539,7 +586,7 @@
// 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 @@
}
+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 @@
*/
-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::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 @@
{
--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 @@
{
elementJustOpened = false;
fprintf( fp, ">" );
- if ( !TextOnStack() ) {
- fprintf( fp, "\n" );
- }
}
void XMLStreamer::PushText( const char* text )
{
+ textDepth = depth-1;
+
if ( elementJustOpened ) {
SealElement();
}
diff --git a/tinyxml2.h b/tinyxml2.h
index 2a6e344..cfe297f 100644
--- a/tinyxml2.h
+++ b/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 @@
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 @@
};
*/
-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; }
+};
+
+
+class XMLUtil
+{
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; }
+ // 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 @@
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 @@
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
+ 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 @@
XMLNode( XMLDocument* );
virtual ~XMLNode();
- void ClearChildren();
-
XMLDocument* document;
XMLNode* parent;
bool isTextParent;
@@ -373,6 +437,7 @@
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 @@
virtual XMLComment* ToComment() { return this; }
const char* Value() { return value.GetStr(); }
+ virtual bool Accept( XMLVisitor* visitor ) const;
char* ParseDeep( char* );
@@ -405,7 +471,7 @@
};
-class XMLAttribute : public XMLBase
+class XMLAttribute
{
friend class XMLElement;
public:
@@ -434,6 +500,7 @@
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 @@
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 @@
};
-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 @@
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;
};
diff --git a/xmltest.cpp b/xmltest.cpp
index 243f0fa..7be7c2e 100644
--- a/xmltest.cpp
+++ b/xmltest.cpp
@@ -52,6 +52,7 @@
printf( "----------------------------------------------\n" );
}
}
+#if 0
{
static const char* test = "<element>Text before.</element>";
XMLDocument doc;
@@ -67,5 +68,6 @@
doc->Parse( test );
delete doc;
}
+#endif
return 0;
}
\ No newline at end of file