Răsfoiți Sursa

print to memory support

U-Stream\Lee 14 ani în urmă
părinte
comite
ae25a44d94
3 a modificat fișierele cu 162 adăugiri și 41 ștergeri
  1. 77 24
      tinyxml2.cpp
  2. 65 17
      tinyxml2.h
  3. 20 0
      xmltest.cpp

+ 77 - 24
tinyxml2.cpp

@@ -5,6 +5,7 @@
 #include <stdio.h>
 #include <ctype.h>
 #include <new.h>
+#include <stdarg.h>
 
 //#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<depth; ++i ) {
-		fprintf( fp, "    " );
+		Print( "    " );
 	}
 }
 
@@ -970,12 +1022,12 @@ void XMLStreamer::PrintString( const char* p )
 			// entity, and keep looking.
 			if ( entityFlag[*q] ) {
 				while ( p < q ) {
-					fputc( *p, fp );
+					Print( "%c", *p );
 					++p;
 				}
 				for( int i=0; i<NUM_ENTITIES; ++i ) {
 					if ( entities[i].value == *q ) {
-						fprintf( fp, "&%s;", entities[i].pattern );
+						Print( "&%s;", entities[i].pattern );
 						break;
 					}
 				}
@@ -987,10 +1039,11 @@ void XMLStreamer::PrintString( const char* p )
 	// Flush the remaining string. This will be the entire
 	// string if an entity wasn't found.
 	if ( q-p > 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, "</%s>", name );
+		Print( "</%s>", 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, "<![CDATA[" );
+		Print( "<![CDATA[" );
 	PrintString( text );
 	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, "<!--%s-->", comment );
+	Print( "<!--%s-->", comment );
 }
 
 

+ 65 - 17
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 <limits.h>
@@ -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 T, int INIT>
 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
+#endif // TINYXML2_INCLUDED

+ 20 - 0
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;
 	}