diff --git a/tinyxml2.cpp b/tinyxml2.cpp index 2287341..3a9b2c9 100644 --- a/tinyxml2.cpp +++ b/tinyxml2.cpp @@ -5,6 +5,7 @@ #include #include #include +#include //#pragma warning ( disable : 4291 ) @@ -332,6 +333,13 @@ void XMLNode::Unlink( XMLNode* child ) } +void XMLNode::DeleteChild( XMLNode* node ) +{ + TIXMLASSERT( node->parent == this ); + DELETE_NODE( node ); +} + + XMLNode* XMLNode::InsertEndChild( XMLNode* addThis ) { if ( lastChild ) { @@ -428,13 +436,6 @@ const XMLElement* XMLNode::LastChildElement( const char* value ) const } -void XMLNode::DeleteChild( XMLNode* node ) -{ - TIXMLASSERT( node->parent == this ); - TIXMLASSERT( 0 ); -} - - char* XMLNode::ParseDeep( char* p ) { while( p && *p ) { @@ -733,6 +734,25 @@ void XMLElement::LinkAttribute( XMLAttribute* attrib ) } +void XMLElement::DeleteAttribute( const char* name ) +{ + XMLAttribute* prev = 0; + for( XMLAttribute* a=rootAttribute; a; a=a->next ) { + if ( XMLUtil::StringEqual( name, a->Name() ) ) { + if ( prev ) { + prev->next = a->next; + } + else { + rootAttribute = a->next; + } + DELETE_ATTRIBUTE( a ); + break; + } + prev = a; + } +} + + char* XMLElement::ParseAttributes( char* p, bool* closedElement ) { const char* start = p; @@ -947,13 +967,45 @@ XMLStreamer::XMLStreamer( FILE* file ) : fp( file ), depth( 0 ), elementJustOpen entityFlag[ entities[i].value ] = true; } } + buffer.Push( 0 ); +} + + +void XMLStreamer::Print( const char* format, ... ) +{ + va_list va; + va_start( va, format ); + + if ( fp ) { + vfprintf( fp, format, va ); + } + else { + // This seems brutally complex. Haven't figured out a better + // way on windows. + #ifdef _MSC_VER + int len = -1; + while ( len < 0 ) { + len = vsnprintf_s( accumulator.Mem(), accumulator.Capacity(), accumulator.Capacity()-1, format, va ); + if ( len < 0 ) { + accumulator.PushArr( 1000 ); + } + } + char* p = buffer.PushArr( len ) - 1; + memcpy( p, accumulator.Mem(), len+1 ); + #else + int len = vsnprintf( 0, 0, format, va ); + char* p = buffer.PushArr( len ) - 1; + vsprintf_s( p, len+1, format, va ); + #endif + } + va_end( va ); } void XMLStreamer::PrintSpace( int depth ) { for( int i=0; i 0 ) { - fprintf( fp, "%s", p ); + Print( "%s", p ); } } + void XMLStreamer::OpenElement( const char* name ) { if ( elementJustOpened ) { @@ -999,11 +1052,11 @@ void XMLStreamer::OpenElement( const char* name ) stack.Push( name ); if ( textDepth < 0 && depth > 0) { - fprintf( fp, "\n" ); + Print( "\n" ); PrintSpace( depth ); } - fprintf( fp, "<%s", name ); + Print( "<%s", name ); elementJustOpened = true; ++depth; } @@ -1012,9 +1065,9 @@ void XMLStreamer::OpenElement( const char* name ) void XMLStreamer::PushAttribute( const char* name, const char* value ) { TIXMLASSERT( elementJustOpened ); - fprintf( fp, " %s=\"", name ); + Print( " %s=\"", name ); PrintString( value ); - fprintf( fp, "\"" ); + Print( "\"" ); } @@ -1024,20 +1077,20 @@ void XMLStreamer::CloseElement() const char* name = stack.Pop(); if ( elementJustOpened ) { - fprintf( fp, "/>" ); + Print( "/>" ); } else { if ( textDepth < 0 ) { - fprintf( fp, "\n" ); + Print( "\n" ); PrintSpace( depth ); } - fprintf( fp, "", name ); + Print( "", name ); } if ( textDepth == depth ) textDepth = -1; if ( depth == 0 ) - fprintf( fp, "\n" ); + Print( "\n" ); elementJustOpened = false; } @@ -1045,7 +1098,7 @@ void XMLStreamer::CloseElement() void XMLStreamer::SealElement() { elementJustOpened = false; - fprintf( fp, ">" ); + Print( ">" ); } @@ -1057,10 +1110,10 @@ void XMLStreamer::PushText( const char* text, bool cdata ) SealElement(); } if ( cdata ) - fprintf( fp, "" ); + Print( "]]>" ); } @@ -1070,10 +1123,10 @@ void XMLStreamer::PushComment( const char* comment ) SealElement(); } if ( textDepth < 0 && depth > 0) { - fprintf( fp, "\n" ); + Print( "\n" ); PrintSpace( depth ); } - fprintf( fp, "", comment ); + Print( "", comment ); } diff --git a/tinyxml2.h b/tinyxml2.h index 0d5a591..2e46ba1 100644 --- a/tinyxml2.h +++ b/tinyxml2.h @@ -13,13 +13,16 @@ X hide copy constructor X hide = operator X UTF8 support: isAlpha, etc. - - string buffer for sets. (Grr.) + X string buffer for sets. (Grr.) - MS BOM - print to memory buffer - tests from xml1 - xml1 tests especially UTF-8 - perf test: xml1 - perf test: xenowar + - test: load(char*) + - test: load(FILE*) + */ #include @@ -64,7 +67,7 @@ #define TIXML_SNPRINTF _snprintf #define TIXML_SSCANF sscanf #elif defined(__GNUC__) && (__GNUC__ >= 3 ) - // GCC version 3 and higher.s + // GCC version 3 and higher //#warning( "Using sn* functions." ) #define TIXML_SNPRINTF snprintf #define TIXML_SSCANF sscanf @@ -87,6 +90,12 @@ class XMLUnknown; class XMLStreamer; +/* + A class that wraps strings. Normally stores the start and end + pointers into the XML file itself, and will apply normalization + and entity transalion if actually read. Can also store (and memory + manage) a traditional char[] +*/ class StrPair { public: @@ -132,6 +141,11 @@ private: }; +/* + A dynamic array of Plain Old Data. Doesn't support constructors, etc. + Has a small initial memory pool, so that low or no usage will not + cause a call to new/delete +*/ template class DynArray { @@ -170,12 +184,13 @@ public: size -= count; } - bool Empty() const { return size == 0; } - T& operator[](int i) { TIXMLASSERT( i>= 0 && i < size ); return mem[i]; } - const T& operator[](int i) const { TIXMLASSERT( i>= 0 && i < size ); return mem[i]; } - int Size() const { return size; } - const T* Mem() const { return mem; } - T* Mem() { return mem; } + bool Empty() const { return size == 0; } + T& operator[](int i) { TIXMLASSERT( i>= 0 && i < size ); return mem[i]; } + const T& operator[](int i) const { TIXMLASSERT( i>= 0 && i < size ); return mem[i]; } + int Size() const { return size; } + int Capacity() const { return allocated; } + const T* Mem() const { return mem; } + T* Mem() { return mem; } private: @@ -197,6 +212,10 @@ private: }; +/* + Parent virtual class a a pool for fast allocation + and deallocation of objects. +*/ class MemPool { public: @@ -209,6 +228,9 @@ public: }; +/* + Template child class to create pools of the correct type. +*/ template< int SIZE > class MemPoolT : public MemPool { @@ -321,13 +343,16 @@ public: }; +/* + Utility functionality. +*/ 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; } + 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; @@ -418,11 +443,17 @@ public: */ XMLNode* InsertAfterChild( XMLNode* afterThis, XMLNode* addThis ); + /** + Tests: All (used by destructor) + */ void ClearChildren(); + + /** + Tests: Progammatic DOM + */ void DeleteChild( XMLNode* node ); virtual bool Accept( XMLVisitor* visitor ) const = 0; - //virtual void Print( XMLStreamer* streamer ); virtual char* ParseDeep( char* ); virtual bool IsClosingElement() const { return false; } @@ -549,6 +580,12 @@ public: const char* Value() const { return value.GetStr(); } const XMLAttribute* Next() const { return next; } + int IntAttribute( const char* name ) const { int i=0; QueryIntAttribute( &i ); return i; } + unsigned UnsignedAttribute( const char* name ) const{ unsigned i=0; QueryUnsignedAttribute( &i ); return i; } + bool BoolAttribute( const char* name ) const { bool b=false; QueryBoolAttribute( &b ); return b; } + double DoubleAttribute( const char* name ) const { double d=0; QueryDoubleAttribute( &d ); return d; } + float FloatAttribute( const char* name ) const { float f=0; QueryFloatAttribute( &f ); return f; } + int QueryIntAttribute( int* value ) const; int QueryUnsignedAttribute( unsigned int* value ) const; int QueryBoolAttribute( bool* value ) const; @@ -612,7 +649,10 @@ public: void SetAttribute( const char* name, bool value ) { XMLAttribute* a = FindOrCreateAttribute( name ); a->SetAttribute( value ); } void SetAttribute( const char* name, double value ) { XMLAttribute* a = FindOrCreateAttribute( name ); a->SetAttribute( value ); } - void RemoveAttribute( const char* name ); + /** + Tests: Programmatic DOM + */ + void DeleteAttribute( const char* name ); const XMLAttribute* FirstAttribute() const { return rootAttribute; } const XMLAttribute* FindAttribute( const char* name ) const; @@ -657,18 +697,23 @@ public: virtual bool Accept( XMLVisitor* visitor ) const; /** - Testing: Programmatic DOM + Tests: Programmatic DOM */ XMLElement* NewElement( const char* name ); /** - Testing: Programmatic DOM + Tests: Programmatic DOM */ XMLComment* NewComment( const char* comment ); /** - Testing: Programmatic DOM + Tests: Programmatic DOM */ XMLText* NewText( const char* text ); + /** + Tests: Programmatic DOM + */ + void DeleteNode( XMLNode* node ) { node->parent->DeleteChild( node ); } + enum { NO_ERROR = 0, ERROR_ELEMENT_MISMATCH, @@ -705,7 +750,7 @@ private: class XMLStreamer : public XMLVisitor { public: - XMLStreamer( FILE* file ); + XMLStreamer( FILE* file=0 ); ~XMLStreamer() {} void OpenElement( const char* name ); @@ -724,11 +769,13 @@ public: virtual bool Visit( const XMLText& text ); virtual bool Visit( const XMLComment& comment ); + const char* CStr() const { return buffer.Mem(); } private: void SealElement(); void PrintSpace( int depth ); void PrintString( const char* ); // prints out, after detecting entities. + void Print( const char* format, ... ); FILE* fp; int depth; @@ -741,6 +788,7 @@ private: bool entityFlag[ENTITY_RANGE]; DynArray< const char*, 10 > stack; + DynArray< char, 20 > buffer, accumulator; }; @@ -748,4 +796,4 @@ private: -#endif // TINYXML2_INCLUDED \ No newline at end of file +#endif // TINYXML2_INCLUDED diff --git a/xmltest.cpp b/xmltest.cpp index a7194e0..f5cb241 100644 --- a/xmltest.cpp +++ b/xmltest.cpp @@ -147,6 +147,26 @@ int main( int argc, const char* argv ) XMLTest( "Programmatic DOM", 2, doc->FirstChildElement()->LastChildElement( "sub" )->IntAttribute( "attrib" ) ); XMLTest( "Programmatic DOM", "& Text!", doc->FirstChildElement()->LastChildElement( "sub" )->FirstChild()->ToText()->Value() ); + + // And now deletion: + element->DeleteChild( sub[2] ); + doc->DeleteNode( comment ); + + element->FirstChildElement()->SetAttribute( "attrib", true ); + element->LastChildElement()->DeleteAttribute( "attrib" ); + + XMLTest( "Programmatic DOM", true, doc->FirstChildElement()->FirstChildElement()->BoolAttribute( "attrib" ) ); + int value = 10; + int result = doc->FirstChildElement()->LastChildElement()->QueryIntAttribute( "attrib", &value ); + XMLTest( "Programmatic DOM", result, NO_ATTRIBUTE ); + XMLTest( "Programmatic DOM", value, 10 ); + + doc->Print(); + + XMLStreamer streamer; + doc->Print( &streamer ); + printf( "%s", streamer.CStr() ); + delete doc; }