mirror of https://github.com/AxioDL/tinyxml2.git
print to memory support
This commit is contained in:
parent
09a11c5964
commit
ae25a44d94
101
tinyxml2.cpp
101
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 );
|
||||
}
|
||||
|
||||
|
||||
|
|
82
tinyxml2.h
82
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
xmltest.cpp
20
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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue