#ifndef TINYXML2_INCLUDED #define TINYXML2_INCLUDED #include <limits.h> #include <ctype.h> #include <stdio.h> #if defined( _DEBUG ) || defined( DEBUG ) || defined (__DEBUG__) #ifndef DEBUG #define DEBUG #endif #endif #if defined(DEBUG) #if defined(_MSC_VER) #define TIXMLASSERT( x ) if ( !(x)) { _asm { int 3 } } //if ( !(x)) WinDebugBreak() #elif defined (ANDROID_NDK) #include <android/log.h> #define TIXMLASSERT( x ) if ( !(x)) { __android_log_assert( "assert", "grinliz", "ASSERT in '%s' at %d.", __FILE__, __LINE__ ); } #else #include <assert.h> #define TIXMLASSERT assert #endif #else #define TIXMLASSERT( x ) {} #endif namespace tinyxml2 { class XMLDocument; class XMLElement; class XMLAttribute; class XMLComment; class XMLNode; class XMLText; class XMLStreamer; // internal - move to separate namespace struct CharBuffer { size_t length; char mem[1]; static CharBuffer* Construct( const char* in ); static void Free( CharBuffer* ); }; // FIXME: refactor to be the basis for all string handling. class StrPair { public: enum { NEEDS_ENTITY_PROCESSING = 0x01, NEEDS_NEWLINE_NORMALIZATION = 0x02 }; StrPair() : flags( 0 ), start( 0 ), end( 0 ) {} void Set( char* start, char* end, int flags ) { this->start = start; this->end = end; this->flags = flags | NEEDS_FLUSH; } const char* GetStr(); bool Empty() const { return start == end; } private: enum { NEEDS_FLUSH = 0x100 }; // After parsing, if *end != 0, it can be set to zero. int flags; char* start; char* end; }; class XMLBase { public: XMLBase() {} virtual ~XMLBase() {} protected: static const char* SkipWhiteSpace( const char* p ) { while( isspace( *p ) ) { ++p; } return p; } static char* SkipWhiteSpace( char* p ) { while( isspace( *p ) ) { ++p; } return p; } inline static bool StringEqual( const char* p, const char* q, int nChar=INT_MAX ) { int n = 0; if ( p == q ) { return true; } while( *p && *q && *p == *q && n<nChar ) { ++p; ++q; ++n; } if ( (n == nChar) || ( *p == 0 && *q == 0 ) ) { return true; } 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; } char* ParseText( char* in, StrPair* pair, const char* endTag ); char* ParseName( char* in, StrPair* pair ); char* Identify( XMLDocument* document, char* p, XMLNode** node ); }; class XMLNode : public XMLBase { friend class XMLDocument; friend class XMLElement; public: virtual ~XMLNode(); XMLNode* InsertEndChild( XMLNode* addThis ); virtual void Print( XMLStreamer* streamer ); virtual XMLElement* ToElement() { return 0; } virtual XMLText* ToText() { return 0; } virtual XMLComment* ToComment() { return 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* ); void Unlink( XMLNode* child ); XMLDocument* document; XMLNode* parent; bool isTextParent; XMLNode* firstChild; XMLNode* lastChild; XMLNode* prev; XMLNode* next; private: }; class XMLText : public XMLNode { public: XMLText( XMLDocument* doc ) : XMLNode( doc ) {} virtual ~XMLText() {} virtual void Print( XMLStreamer* streamer ); const char* Value() { return value.GetStr(); } virtual XMLText* ToText() { return this; } char* ParseDeep( char* ); protected: private: StrPair value; }; class XMLComment : public XMLNode { public: XMLComment( XMLDocument* doc ); virtual ~XMLComment(); virtual void Print( XMLStreamer* ); virtual XMLComment* ToComment() { return this; } const char* Value() { return value.GetStr(); } char* ParseDeep( char* ); protected: private: StrPair value; }; class XMLAttribute : public XMLBase { friend class XMLElement; public: XMLAttribute( XMLElement* element ) : next( 0 ) {} virtual ~XMLAttribute() {} virtual void Print( XMLStreamer* streamer ); private: char* ParseDeep( char* p ); StrPair name; StrPair value; XMLAttribute* next; }; class XMLElement : public XMLNode { public: XMLElement( XMLDocument* doc ); virtual ~XMLElement(); const char* Name() { return name.GetStr(); } virtual void Print( XMLStreamer* ); virtual XMLElement* ToElement() { return this; } virtual bool IsClosingElement() const { return closing; } char* ParseDeep( char* p ); protected: private: char* ParseAttributes( char* p, bool *closedElement ); StrPair name; bool closing; XMLAttribute* rootAttribute; XMLAttribute* lastAttribute; }; class XMLDocument : public XMLNode { public: XMLDocument(); ~XMLDocument(); bool Parse( const char* ); void Print( XMLStreamer* streamer=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 ); private: XMLDocument( const XMLDocument& ); // intentionally not implemented CharBuffer* charBuffer; }; // FIXME: break out into string pointer stack class StringStack { public: StringStack(); ~StringStack(); void Push( const char* str ); const char* Pop(); int NumPositive() const { return nPositive; } private: enum { INIT=10 // fixme, super small for testing }; char* mem; char pool[INIT]; int inUse; // includes null int allocated; // bytes allocated int nPositive; // number of strings with len > 0 }; class XMLStreamer { public: XMLStreamer( FILE* file ); ~XMLStreamer() {} void OpenElement( const char* name, bool textParent ); void PushAttribute( const char* name, const char* value ); void CloseElement(); void PushText( const char* text ); void PushComment( const char* comment ); private: void SealElement(); void PrintSpace( int depth ); void PrintString( const char* ); // prints out, after detecting entities. FILE* fp; int depth; bool elementJustOpened; enum { ENTITY_RANGE = 64 }; bool entityFlag[ENTITY_RANGE]; StringStack stack; StringStack text; }; }; // tinyxml2 #endif // TINYXML2_INCLUDED