/*************************************************************************** * Copyright (C) 2005 by Lee Thomason * * root@sat * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include "Stdafx.h" #include "jxmlparser.h" using namespace std; namespace jcommon { XmlCursor::XmlCursor() { Clear(); } void XmlCursor::Clear() { row = col = -1; } XmlVisitor::~XmlVisitor() { } bool XmlVisitor::VisitEnter(const XmlDocument& doc) { return true; } bool XmlVisitor::VisitExit(const XmlDocument& doc) { return true; } bool XmlVisitor::VisitEnter(const XmlElement& element, const XmlAttribute* firstAttribute) { return true; } bool XmlVisitor::VisitExit(const XmlElement& element) { return true; } bool XmlVisitor::Visit(const XmlDeclaration& declaration) { return true; } bool XmlVisitor::Visit(const XmlText& text) { return true; } bool XmlVisitor::Visit(const XmlComment& comment) { return true; } bool XmlVisitor::Visit(const XmlUnknown& unknown) { return true; } bool XmlBase::IsWhiteSpace( char c ) { return (isspace( (uint8_t) c ) || c == '\n' || c == '\r'); } bool XmlBase::IsWhiteSpace( int c ) { if ( c < 256 ) { return IsWhiteSpace( (char) c ); } return false; // Again, only truly correct for English/Latin...but usually works. } const char * XmlBase::GetChar(const char* p, char* _value, int* length, XmlEncoding encoding) { assert( p ); if ( encoding == TIXML_ENCODING_UTF8 ) { *length = utf8ByteTable[ *((const uint8_t *)p) ]; assert( *length >= 0 && *length < 5 ); } else { *length = 1; } if ( *length == 1 ) { if ( *p == '&' ) return GetEntity( p, _value, length, encoding ); *_value = *p; return p+1; } else if ( *length ) { //strncpy( _value, p, *length ); // lots of compilers don't like this function (unsafe), // and the null terminator isn't needed for( int i=0; p[i] && i<*length; ++i ) { _value[i] = p[i]; } return p + (*length); } else { // Not valid text. return 0; } } int XmlBase::ToLower(int v, XmlEncoding encoding) { if ( encoding == TIXML_ENCODING_UTF8 ) { if ( v < 128 ) return tolower( v ); return v; } else { return tolower( v ); } } XmlBase::XmlBase() : userData(0) { } XmlBase::~XmlBase() { } void XmlBase::SetCondenseWhiteSpace(bool condense) { condenseWhiteSpace = condense; } bool XmlBase::IsWhiteSpaceCondensed() { return condenseWhiteSpace; } int XmlBase::Row() const { return location.row + 1; } int XmlBase::Column() const // SEE Row { return location.col + 1; } void XmlBase::SetUserData(void *user) { userData = user; } ///< Set a pointer to arbitrary user data. void * XmlBase::GetUserData() { return userData; } ///< Get a pointer to arbitrary user data. const void * XmlBase::GetUserData() const { return userData; } ///< Get a pointer to arbitrary user data. const char * XmlNode::Value() const { return value.c_str (); } const std::string& XmlNode::ValueStr() const { return value; } void XmlNode::SetValue(const char * _value) { value = _value; } void XmlNode::SetValue( const std::string& _value ) { value = _value; } XmlNode* XmlNode::Parent() { return parent; } const XmlNode* XmlNode::Parent() const { return parent; } const XmlNode* XmlNode::FirstChild() const { return firstChild; } ///< The first child of this node. Will be null if there are no children. XmlNode* XmlNode::FirstChild() { return firstChild; } XmlNode* XmlNode::FirstChild( const char * _value ) { // Call through to the const version - safe since nothing is changed. // Exiting syntax: cast this to a const (always safe) call the method, // cast the return back to non-const. return const_cast< XmlNode* > ((const_cast< const XmlNode* >(this))->FirstChild( _value )); } const XmlNode* XmlNode::LastChild() const { return lastChild; } /// The last child of this node. Will be null if there are no children. XmlNode* XmlNode::LastChild() { return lastChild; } XmlNode* XmlNode::LastChild( const char * _value ) { return const_cast< XmlNode* > ((const_cast< const XmlNode* >(this))->LastChild( _value )); } const XmlNode* XmlNode::FirstChild( const std::string& _value ) const { return FirstChild (_value.c_str ()); } ///< STL std::string form. XmlNode* XmlNode::FirstChild( const std::string& _value ) { return FirstChild (_value.c_str ()); } ///< STL std::string form. const XmlNode* XmlNode::LastChild( const std::string& _value ) const { return LastChild (_value.c_str ()); } ///< STL std::string form. XmlNode* XmlNode::LastChild( const std::string& _value ) { return LastChild (_value.c_str ()); } ///< STL std::string form. XmlNode* XmlNode::IterateChildren( XmlNode* previous ) { return const_cast< XmlNode* >( (const_cast< const XmlNode* >(this))->IterateChildren( previous ) ); } XmlNode* XmlNode::IterateChildren( const char * _value, XmlNode* previous ) { return const_cast< XmlNode* >( const_cast< const XmlNode* >(this)->IterateChildren( _value, previous ) ); } const XmlNode* XmlNode::IterateChildren( const std::string& _value, const XmlNode* previous ) const { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form. XmlNode* XmlNode::IterateChildren( const std::string& _value, XmlNode* previous ) { return IterateChildren (_value.c_str (), previous); } // Navigate to a sibling node. const XmlNode* XmlNode::PreviousSibling() const { return prev; } XmlNode* XmlNode::PreviousSibling() { return prev; } XmlNode* XmlNode::PreviousSibling( const char *_prev ) { return const_cast< XmlNode* >( const_cast< const XmlNode* >(this)->PreviousSibling( _prev ) ); } const XmlNode* XmlNode::PreviousSibling( const std::string& _value ) const { return PreviousSibling (_value.c_str ()); } XmlNode* XmlNode::PreviousSibling( const std::string& _value ) { return PreviousSibling (_value.c_str ()); } const XmlNode* XmlNode::NextSibling( const std::string& _value) const { return NextSibling (_value.c_str ()); } XmlNode* XmlNode::NextSibling( const std::string& _value) { return NextSibling (_value.c_str ()); } const XmlNode* XmlNode::NextSibling() const { return next; } XmlNode* XmlNode::NextSibling() { return next; } XmlNode* XmlNode::NextSibling( const char* _next ) { return const_cast< XmlNode* >( const_cast< const XmlNode* >(this)->NextSibling( _next ) ); } XmlElement* XmlNode::NextSiblingElement() { return const_cast< XmlElement* >( const_cast< const XmlNode* >(this)->NextSiblingElement() ); } XmlElement* XmlNode::NextSiblingElement( const char *_next ) { return const_cast< XmlElement* >( const_cast< const XmlNode* >(this)->NextSiblingElement( _next ) ); } const XmlElement* XmlNode::NextSiblingElement( const std::string& _value) const { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form. XmlElement* XmlNode::NextSiblingElement( const std::string& _value) { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form. XmlElement* XmlNode::FirstChildElement() { return const_cast< XmlElement* >( const_cast< const XmlNode* >(this)->FirstChildElement() ); } XmlElement* XmlNode::FirstChildElement( const char * _value ) { return const_cast< XmlElement* >( const_cast< const XmlNode* >(this)->FirstChildElement( _value ) ); } const XmlElement* XmlNode::FirstChildElement( const std::string& _value ) const { return FirstChildElement (_value.c_str ()); } ///< STL std::string form. XmlElement* XmlNode::FirstChildElement( const std::string& _value ) { return FirstChildElement (_value.c_str ()); } ///< STL std::string form. int XmlNode::Type() const { return type; } XmlDocument* XmlNode::GetDocument() { return const_cast< XmlDocument* >( const_cast< const XmlNode* >(this)->GetDocument() ); } bool XmlNode::HasChildren() const { return firstChild != NULL; } const XmlDocument * XmlNode::ToDocument() const { return NULL; } const XmlElement * XmlNode::ToElement() const { return NULL; } const XmlComment * XmlNode::ToComment() const { return NULL; } const XmlUnknown * XmlNode::ToUnknown() const { return NULL; } const XmlText * XmlNode::ToText() const { return NULL; } const XmlDeclaration * XmlNode::ToDeclaration() const { return NULL; } XmlDocument * XmlNode::ToDocument() { return NULL; } XmlElement * XmlNode::ToElement() { return NULL; } XmlComment * XmlNode::ToComment() { return NULL; } XmlUnknown * XmlNode::ToUnknown() { return NULL; } XmlText * XmlNode::ToText() { return NULL; } XmlDeclaration * XmlNode::ToDeclaration() { return NULL; } XmlAttribute::XmlAttribute() : XmlBase() { document = 0; prev = next = 0; } XmlAttribute::XmlAttribute( const std::string& _name, const std::string& _value ) { name = _name; value = _value; document = 0; prev = next = 0; } XmlAttribute::XmlAttribute( const char * _name, const char * _value ) { name = _name; value = _value; document = 0; prev = next = 0; } const char * XmlAttribute::Name() const { return name.c_str(); } ///< Return the name of this attribute. const char * XmlAttribute::Value() const { return value.c_str(); } ///< Return the value of this attribute. const std::string& XmlAttribute::ValueStr() const { return value; } ///< Return the value of this attribute. const std::string& XmlAttribute::NameTStr() const { return name; } void XmlAttribute::SetName( const char* _name ) { name = _name; } void XmlAttribute::SetValue( const char* _value ) { value = _value; } void XmlAttribute::SetName( const std::string& _name ) { name = _name; } void XmlAttribute::SetValue( const std::string& _value ) { value = _value; } XmlAttribute* XmlAttribute::Next() { return const_cast< XmlAttribute* >( const_cast< const XmlAttribute* >(this)->Next() ); } XmlAttribute* XmlAttribute::Previous() { return const_cast< XmlAttribute* >( const_cast< const XmlAttribute* >(this)->Previous() ); } bool XmlAttribute::operator==( const XmlAttribute& rhs ) const { return rhs.name == name; } bool XmlAttribute::operator<( const XmlAttribute& rhs ) const { return name < rhs.name; } bool XmlAttribute::operator>( const XmlAttribute& rhs ) const { return name > rhs.name; } void XmlAttribute::Print( FILE* cfile, int depth ) const { Print( cfile, depth, 0 ); } void XmlAttribute::SetDocument( XmlDocument* doc ) { document = doc; } const XmlAttribute* XmlAttributeSet::First() const { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; } XmlAttribute* XmlAttributeSet::First() { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; } const XmlAttribute* XmlAttributeSet::Last() const { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; } XmlAttribute* XmlAttributeSet::Last() { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; } XmlAttribute* XmlAttributeSet::Find( const char* _name ) { return const_cast< XmlAttribute* >( const_cast< const XmlAttributeSet* >(this)->Find( _name ) ); } XmlAttribute* XmlAttributeSet::Find( const std::string& _name ) { return const_cast< XmlAttribute* >( const_cast< const XmlAttributeSet* >(this)->Find( _name ) ); } int XmlElement::QueryFloatAttribute( const char* name, float* _value ) const { double d; int result = QueryDoubleAttribute( name, &d ); if ( result == TIXML_SUCCESS ) { *_value = (float)d; } return result; } void XmlElement::RemoveAttribute( const std::string& name ) { RemoveAttribute (name.c_str ()); } const XmlAttribute* XmlElement::FirstAttribute() const { return attributeSet.First(); } XmlAttribute* XmlElement::FirstAttribute() { return attributeSet.First(); } const XmlAttribute* XmlElement::LastAttribute() const { return attributeSet.Last(); } XmlAttribute* XmlElement::LastAttribute() { return attributeSet.Last(); } const XmlElement * XmlElement::ToElement() const { return this; } XmlElement * XmlElement::ToElement() { return this; } XmlComment::XmlComment() : XmlNode( XmlNode::COMMENT ) { } XmlComment::XmlComment( const char* _value ) : XmlNode( XmlNode::COMMENT ) { SetValue( _value ); } XmlComment::~XmlComment() { } const XmlComment* XmlComment::ToComment() const { return this; } XmlComment* XmlComment::ToComment() { return this; } XmlText::XmlText (const char * initValue ) : XmlNode (XmlNode::TEXT) { SetValue( initValue ); cdata = false; } XmlText::~XmlText() { } XmlText::XmlText( const std::string& initValue ) : XmlNode (XmlNode::TEXT) { SetValue( initValue ); cdata = false; } XmlText::XmlText( const XmlText& copy ) : XmlNode( XmlNode::TEXT ) { copy.CopyTo( this ); } void XmlText::operator=( const XmlText& base ) { base.CopyTo( this ); } const XmlText* XmlText::ToText() const { return this; } XmlText * XmlText::ToText() { return this; } XmlDeclaration::XmlDeclaration() : XmlNode( XmlNode::DECLARATION ) { } XmlDeclaration::~XmlDeclaration() { } const char *XmlDeclaration::Version() const { return version.c_str (); } /// Encoding. Will return an empty string if none was found. const char *XmlDeclaration::Encoding() const { return encoding.c_str (); } /// Is this a standalone document? const char *XmlDeclaration::Standalone() const { return standalone.c_str (); } void XmlDeclaration::Print( FILE* cfile, int depth ) const { Print( cfile, depth, 0 ); } const XmlDeclaration* XmlDeclaration::ToDeclaration() const { return this; } XmlDeclaration* XmlDeclaration::ToDeclaration() { return this; } XmlUnknown::XmlUnknown() : XmlNode( XmlNode::UNKNOWN ) { } XmlUnknown::~XmlUnknown() { } XmlUnknown::XmlUnknown( const XmlUnknown& copy ) : XmlNode( XmlNode::UNKNOWN ) { copy.CopyTo( this ); } void XmlUnknown::operator=( const XmlUnknown& copy ) { copy.CopyTo( this ); } const XmlUnknown* XmlUnknown::ToUnknown() const { return this; } XmlUnknown * XmlUnknown::ToUnknown() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. XmlDocument::~XmlDocument() { Clear(); location.Clear(); } bool XmlDocument::LoadFile( const std::string& filename, XmlEncoding encoding )///< STL std::string version. { // StringToBuffer f( filename ); // return ( f.buffer && LoadFile( f.buffer, encoding )); return LoadFile( filename.c_str(), encoding ); } bool XmlDocument::SaveFile( const std::string& filename ) const ///< STL std::string version. { // StringToBuffer f( filename ); // return ( f.buffer && SaveFile( f.buffer )); return SaveFile( filename.c_str() ); } const XmlElement* XmlDocument::RootElement() const { return FirstChildElement(); } XmlElement* XmlDocument::RootElement() { return FirstChildElement(); } bool XmlDocument::Error() const { return error; } const char * XmlDocument::ErrorDesc() const { return errorDesc.c_str (); } int XmlDocument::ErrorId() const { return errorId; } int XmlDocument::ErrorRow() { return errorLocation.row+1; } int XmlDocument::ErrorCol() { return errorLocation.col+1; } ///< The column where the error occured. See ErrorRow() void XmlDocument::SetTabSize( int _tabsize ) { tabsize = _tabsize; } int XmlDocument::TabSize() const { return tabsize; } void XmlDocument::ClearError() { error = false; errorId = 0; errorDesc = ""; errorLocation.row = errorLocation.col = 0; //errorLocation.last = 0; } void XmlDocument::Print() const { Print( stdout, 0 ); } const XmlDocument* XmlDocument::ToDocument() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. XmlDocument* XmlDocument::ToDocument() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. XmlHandle::XmlHandle( XmlNode* _node ) { this->node = _node; } /// Copy constructor XmlHandle::XmlHandle( const XmlHandle& ref ) { this->node = ref.node; } XmlHandle XmlHandle::operator=( const XmlHandle& ref ) { this->node = ref.node; return *this; } XmlHandle XmlHandle::FirstChild( const std::string& _value ) const { return FirstChild( _value.c_str() ); } XmlHandle XmlHandle::FirstChildElement( const std::string& _value ) const { return FirstChildElement( _value.c_str() ); } XmlHandle XmlHandle::Child( const std::string& _value, int index ) const { return Child( _value.c_str(), index ); } XmlHandle XmlHandle::ChildElement( const std::string& _value, int index ) const { return ChildElement( _value.c_str(), index ); } XmlNode* XmlHandle::ToNode() const { return node; } XmlElement* XmlHandle::ToElement() const { return ( ( node && node->ToElement() ) ? node->ToElement() : 0 ); } XmlText* XmlHandle::ToText() const { return ( ( node && node->ToText() ) ? node->ToText() : 0 ); } XmlUnknown* XmlHandle::ToUnknown() const { return ( ( node && node->ToUnknown() ) ? node->ToUnknown() : 0 ); } XmlNode* XmlHandle::Node() const { return ToNode(); } XmlElement* XmlHandle::Element() const { return ToElement(); } XmlText* XmlHandle::Text() const { return ToText(); } XmlUnknown* XmlHandle::Unknown() const { return ToUnknown(); } XmlPrinter::XmlPrinter(): depth( 0 ), simpleTextPrint( false ), buffer(), indent( " " ), lineBreak( "\n" ) { } void XmlPrinter::SetIndent( const char* _indent ) { indent = _indent ? _indent : "" ; } const char* XmlPrinter::Indent() { return indent.c_str(); } void XmlPrinter::SetLineBreak( const char* _lineBreak ) { lineBreak = _lineBreak ? _lineBreak : ""; } const char* XmlPrinter::LineBreak() { return lineBreak.c_str(); } void XmlPrinter::SetStreamPrinting() { indent = ""; lineBreak = ""; } const char* XmlPrinter::CStr() { return buffer.c_str(); } size_t XmlPrinter::Size() { return buffer.size(); } const std::string& XmlPrinter::Str() { return buffer; } void XmlPrinter::DoIndent() { for( int i=0; i' }, { """, 6, '\"' }, { "'", 6, '\'' } }; // Bunch of unicode info at: // http://www.unicode.org/faq/utf_bom.html // Including the basic of this table, which determines the #bytes in the // sequence from the lead byte. 1 placed for invalid sequences -- // although the result will be junk, pass it through as much as possible. // Beware of the non-characters in UTF-8: // ef bb bf (Microsoft "lead bytes") // ef bf be // ef bf bf const uint8_t TIXML_UTF_LEAD_0 = 0xefU; const uint8_t TIXML_UTF_LEAD_1 = 0xbbU; const uint8_t TIXML_UTF_LEAD_2 = 0xbfU; const int XmlBase::utf8ByteTable[256] = { // 0 1 2 3 4 5 6 7 8 9 a b c d e f 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x00 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x10 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x20 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x30 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x40 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x50 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x70 End of ASCII range 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x80 0x80 to 0xc1 invalid 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x90 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xa0 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xb0 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xc0 0xc2 to 0xdf 2 byte 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xd0 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xe0 0xe0 to 0xef 3 byte 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 0xf0 0xf0 to 0xf4 4 byte, 0xf5 and higher invalid }; void XmlBase::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ) { const unsigned long BYTE_MASK = 0xBF; const unsigned long BYTE_MARK = 0x80; const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; if (input < 0x80) *length = 1; else if ( input < 0x800 ) *length = 2; else if ( input < 0x10000 ) *length = 3; else if ( input < 0x200000 ) *length = 4; else { *length = 0; return; } // This code won't covert this correctly anyway. output += *length; // Scary scary fall throughs. switch (*length) { case 4: --output; *output = (char)((input | BYTE_MARK) & BYTE_MASK); input >>= 6; case 3: --output; *output = (char)((input | BYTE_MARK) & BYTE_MASK); input >>= 6; case 2: --output; *output = (char)((input | BYTE_MARK) & BYTE_MASK); input >>= 6; case 1: --output; *output = (char)(input | FIRST_BYTE_MARK[*length]); } } /*static*/ int XmlBase::IsAlpha( uint8_t anyByte, XmlEncoding /*encoding*/ ) { // This will only work for low-ascii, everything else is assumed to be a valid // letter. I'm not sure this is the best approach, but it is quite tricky trying // to figure out alhabetical vs. not across encoding. So take a very // conservative approach. // if ( encoding == TIXML_ENCODING_UTF8 ) // { if ( anyByte < 127 ) return isalpha( anyByte ); else return 1; // What else to do? The unicode set is huge...get the english ones right. // } // else // { // return isalpha( anyByte ); // } } /*static*/ int XmlBase::IsAlphaNum( uint8_t anyByte, XmlEncoding /*encoding*/ ) { // This will only work for low-ascii, everything else is assumed to be a valid // letter. I'm not sure this is the best approach, but it is quite tricky trying // to figure out alhabetical vs. not across encoding. So take a very // conservative approach. // if ( encoding == TIXML_ENCODING_UTF8 ) // { if ( anyByte < 127 ) return isalnum( anyByte ); else return 1; // What else to do? The unicode set is huge...get the english ones right. // } // else // { // return isalnum( anyByte ); // } } class XmlParsingData { friend class XmlDocument; public: void Stamp( const char* now, XmlEncoding encoding ); const XmlCursor& Cursor() { return cursor; } private: // Only used by the document! XmlParsingData( const char* start, int _tabsize, int row, int col ) { assert( start ); stamp = start; tabsize = _tabsize; cursor.row = row; cursor.col = col; } XmlCursor cursor; const char* stamp; int tabsize; }; void XmlParsingData::Stamp( const char* now, XmlEncoding encoding ) { assert( now ); // Do nothing if the tabsize is 0. if ( tabsize < 1 ) { return; } // Get the current row, column. int row = cursor.row; int col = cursor.col; const char* p = stamp; assert( p ); while ( p < now ) { // Treat p as unsigned, so we have a happy compiler. const uint8_t* pU = (const uint8_t *)p; // Code contributed by Fletcher Dunn: (modified by lee) switch (*pU) { case 0: // We *should* never get here, but in case we do, don't // advance past the terminating null character, ever return; case '\r': // bump down to the next line ++row; col = 0; // Eat the character ++p; // Check for \r\n sequence, and treat this as a single character if (*p == '\n') { ++p; } break; case '\n': // bump down to the next line ++row; col = 0; // Eat the character ++p; // Check for \n\r sequence, and treat this as a single // character. (Yes, this bizarre thing does occur still // on some arcane platforms...) if (*p == '\r') { ++p; } break; case '\t': // Eat the character ++p; // Skip to next tab stop col = (col / tabsize + 1) * tabsize; break; case TIXML_UTF_LEAD_0: if ( encoding == TIXML_ENCODING_UTF8 ) { if ( *(p+1) && *(p+2) ) { // In these cases, don't advance the column. These are // 0-width spaces. if ( *(pU+1)==TIXML_UTF_LEAD_1 && *(pU+2)==TIXML_UTF_LEAD_2 ) p += 3; else if ( *(pU+1)==0xbfU && *(pU+2)==0xbeU ) p += 3; else if ( *(pU+1)==0xbfU && *(pU+2)==0xbfU ) p += 3; else { p +=3; ++col; } // A normal character. } } else { ++p; ++col; } break; default: if ( encoding == TIXML_ENCODING_UTF8 ) { // Eat the 1 to 4 byte utf8 character. int step = XmlBase::utf8ByteTable[*((const uint8_t *)p)]; if ( step == 0 ) step = 1; // Error case from bad encoding, but handle gracefully. p += step; // Just advance one column, of course. ++col; } else { ++p; ++col; } break; } } cursor.row = row; cursor.col = col; assert( cursor.row >= -1 ); assert( cursor.col >= -1 ); stamp = p; assert( stamp ); } const char* XmlBase::SkipWhiteSpace( const char* p, XmlEncoding encoding ) { if ( !p || !*p ) { return 0; } if (encoding == TIXML_ENCODING_UTF8) { while (*p) { const uint8_t *pU = (const uint8_t *)p; // Skip the stupid Microsoft UTF-8 Byte order marks if (*(pU+0)==TIXML_UTF_LEAD_0 && *(pU+1)==TIXML_UTF_LEAD_1 && *(pU+2)==TIXML_UTF_LEAD_2 ) { p += 3; continue; } else if(*(pU+0)==TIXML_UTF_LEAD_0 && *(pU+1)==0xbfU && *(pU+2)==0xbeU ) { p += 3; continue; } else if(*(pU+0)==TIXML_UTF_LEAD_0 && *(pU+1)==0xbfU && *(pU+2)==0xbfU ) { p += 3; continue; } // Still using old rules for white space. if ( IsWhiteSpace( *p ) || *p == '\n' || *p =='\r' ) { ++p; } else { break; } } } else { while (*p && (IsWhiteSpace( *p ) || *p == '\n' || *p =='\r')) { ++p; } } return p; } /*static*/ bool XmlBase::StreamWhiteSpace( std::istream * in, std::string * tag ) { for( ;; ) { if ( !in->good() ) return false; int c = in->peek(); // At this scope, we can't get to a document. So fail silently. if ( !IsWhiteSpace( c ) || c <= 0 ) return true; *tag += (char) in->get(); } } /*static*/ bool XmlBase::StreamTo( std::istream * in, int character, std::string * tag ) { //assert( character > 0 && character < 128 ); // else it won't work in utf-8 while ( in->good() ) { int c = in->peek(); if ( c == character ) return true; if ( c <= 0 ) // Silent failure: can't get document at this scope return false; in->get(); *tag += (char) c; } return false; } // One of nyXML's more performance demanding functions. Try to keep the memory overhead down. The // "assign" optimization removes over 10% of the execution time. // const char* XmlBase::ReadName( const char* p, std::string * name, XmlEncoding encoding ) { // Oddly, not supported on some comilers, //name->clear(); // So use this: *name = ""; assert( p ); // Names start with letters or underscores. // Of course, in unicode, tinyxml has no idea what a letter *is*. The // algorithm is generous. // // After that, they can be letters, underscores, numbers, // hyphens, or colons. (Colons are valid ony for namespaces, // but tinyxml can't tell namespaces from names.) if ( p && *p && ( IsAlpha( (uint8_t) *p, encoding ) || *p == '_' ) ) { const char* start = p; while( p && *p && ( IsAlphaNum( (uint8_t) *p, encoding ) || *p == '_' || *p == '-' || *p == '.' || *p == ':' ) ) { //(*name) += *p; // expensive ++p; } if ( p-start > 0 ) { name->assign( start, p-start ); } return p; } return 0; } const char* XmlBase::GetEntity( const char* p, char* value, int* length, XmlEncoding encoding ) { // Presume an entity, and pull it out. std::string ent; int i; *length = 0; if ( *(p+1) && *(p+1) == '#' && *(p+2) ) { unsigned long ucs = 0; ptrdiff_t delta = 0; unsigned mult = 1; if ( *(p+2) == 'x' ) { // Hexadecimal. if ( !*(p+3) ) return 0; const char* q = p+3; q = strchr( q, ';' ); if ( !q || !*q ) return 0; delta = q-p; --q; while ( *q != 'x' ) { if ( *q >= '0' && *q <= '9' ) ucs += mult * (*q - '0'); else if ( *q >= 'a' && *q <= 'f' ) ucs += mult * (*q - 'a' + 10); else if ( *q >= 'A' && *q <= 'F' ) ucs += mult * (*q - 'A' + 10 ); else return 0; mult *= 16; --q; } } else { // Decimal. if ( !*(p+2) ) return 0; const char* q = p+2; q = strchr( q, ';' ); if ( !q || !*q ) return 0; delta = q-p; --q; while ( *q != '#' ) { if ( *q >= '0' && *q <= '9' ) ucs += mult * (*q - '0'); else return 0; mult *= 10; --q; } } if ( encoding == TIXML_ENCODING_UTF8 ) { // convert the UCS to UTF-8 ConvertUTF32ToUTF8( ucs, value, length ); } else { *value = (char)ucs; *length = 1; } return p + delta + 1; } // Now try to match it. for( i=0; iappend( cArr, len ); } } else { bool whitespace = false; // Remove leading white space: p = SkipWhiteSpace( p, encoding ); while ( p && *p && !StringEqual( p, endTag, caseInsensitive, encoding ) ) { if ( *p == '\r' || *p == '\n' ) { whitespace = true; ++p; } else if ( IsWhiteSpace( *p ) ) { whitespace = true; ++p; } else { // If we've found whitespace, add it before the // new character. Any whitespace just becomes a space. if ( whitespace ) { (*text) += ' '; whitespace = false; } int len; char cArr[4] = { 0, 0, 0, 0 }; p = GetChar( p, cArr, &len, encoding ); if ( len == 1 ) (*text) += cArr[0]; // more efficient else text->append( cArr, len ); } } } if ( p ) p += strlen( endTag ); return p; } void XmlDocument::StreamIn( std::istream * in, std::string * tag ) { // The basic issue with a document is that we don't know what we're // streaming. Read something presumed to be a tag (and hope), then // identify it, and call the appropriate stream method on the tag. // // This "pre-streaming" will never read the closing ">" so the // sub-tag can orient itself. if ( !StreamTo( in, '<', tag ) ) { SetError( TIXML_ERROR_PARSING_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); return; } while ( in->good() ) { int tagIndex = (int) tag->length(); while ( in->good() && in->peek() != '>' ) { int c = in->get(); if ( c <= 0 ) { SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); break; } (*tag) += (char) c; } if ( in->good() ) { // We now have something we presume to be a node of // some sort. Identify it, and call the node to // continue streaming. XmlNode* node = Identify( tag->c_str() + tagIndex, TIXML_DEFAULT_ENCODING ); if ( node ) { node->StreamIn( in, tag ); bool isElement = node->ToElement() != 0; delete node; node = 0; // If this is the root element, we're done. Parsing will be // done by the >> operator. if ( isElement ) { return; } } else { SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN ); return; } } } // We should have returned sooner. SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN ); } const char* XmlDocument::Parse( const char* p, XmlParsingData* prevData, XmlEncoding encoding ) { ClearError(); // Parse away, at the document level. Since a document // contains nothing but other tags, most of what happens // here is skipping white space. if ( !p || !*p ) { SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); return 0; } // Note that, for a document, this needs to come // before the while space skip, so that parsing // starts from the pointer we are given. location.Clear(); if ( prevData ) { location.row = prevData->cursor.row; location.col = prevData->cursor.col; } else { location.row = 0; location.col = 0; } XmlParsingData data( p, TabSize(), location.row, location.col ); location = data.Cursor(); if ( encoding == TIXML_ENCODING_UNKNOWN ) { // Check for the Microsoft UTF-8 lead bytes. const uint8_t *pU = (const uint8_t *)p; if ( *(pU+0) && *(pU+0) == TIXML_UTF_LEAD_0 && *(pU+1) && *(pU+1) == TIXML_UTF_LEAD_1 && *(pU+2) && *(pU+2) == TIXML_UTF_LEAD_2 ) { encoding = TIXML_ENCODING_UTF8; useMicrosoftBOM = true; } } p = SkipWhiteSpace( p, encoding ); if ( !p ) { SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); return 0; } while ( p && *p ) { XmlNode* node = Identify( p, encoding ); if ( node ) { p = node->Parse( p, &data, encoding ); LinkEndChild( node ); } else { break; } // Did we get encoding info? if ( encoding == TIXML_ENCODING_UNKNOWN && node->ToDeclaration() ) { XmlDeclaration* dec = node->ToDeclaration(); const char* enc = dec->Encoding(); assert( enc ); if ( *enc == 0 ) encoding = TIXML_ENCODING_UTF8; else if ( StringEqual( enc, "UTF-8", true, TIXML_ENCODING_UNKNOWN ) ) encoding = TIXML_ENCODING_UTF8; else if ( StringEqual( enc, "UTF8", true, TIXML_ENCODING_UNKNOWN ) ) encoding = TIXML_ENCODING_UTF8; // incorrect, but be nice else encoding = TIXML_ENCODING_LEGACY; } p = SkipWhiteSpace( p, encoding ); } // Was this empty? if ( !firstChild ) { SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, encoding ); return 0; } // All is well. return p; } void XmlDocument::SetError( int err, const char* pError, XmlParsingData* data, XmlEncoding encoding ) { // The first error in a chain is more accurate - don't set again! if ( error ) return; assert( err > 0 && err < TIXML_ERROR_STRING_COUNT ); error = true; errorId = err; errorDesc = errorString[ errorId ]; errorLocation.Clear(); if ( pError && data ) { data->Stamp( pError, encoding ); errorLocation = data->Cursor(); } } XmlNode * XmlNode::Identify( const char* p, XmlEncoding encoding ) { XmlNode* returnNode = 0; p = SkipWhiteSpace( p, encoding ); if( !p || !*p || *p != '<' ) { return 0; } XmlDocument* doc = GetDocument(); p = SkipWhiteSpace( p, encoding ); if ( !p || !*p ) { return 0; } // What is this thing? // - Elements start with a letter or underscore, but xml is reserved. // - Comments: "; if ( !StringEqual( p, startTag, false, encoding ) ) { document->SetError( TIXML_ERROR_PARSING_COMMENT, p, data, encoding ); return 0; } p += strlen( startTag ); p = ReadText( p, &value, false, endTag, false, encoding ); return p; } const char* XmlAttribute::Parse( const char* p, XmlParsingData* data, XmlEncoding encoding ) { p = SkipWhiteSpace( p, encoding ); if ( !p || !*p ) return 0; // int tabsize = 4; // if ( document ) // tabsize = document->TabSize(); if ( data ) { data->Stamp( p, encoding ); location = data->Cursor(); } // Read the name, the '=' and the value. const char* pErr = p; p = ReadName( p, &name, encoding ); if ( !p || !*p ) { if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding ); return 0; } p = SkipWhiteSpace( p, encoding ); if ( !p || !*p || *p != '=' ) { if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); return 0; } ++p; // skip '=' p = SkipWhiteSpace( p, encoding ); if ( !p || !*p ) { if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); return 0; } const char* end; const char SINGLE_QUOTE = '\''; const char DOUBLE_QUOTE = '\"'; if ( *p == SINGLE_QUOTE ) { ++p; end = "\'"; // single quote in string p = ReadText( p, &value, false, end, false, encoding ); } else if ( *p == DOUBLE_QUOTE ) { ++p; end = "\""; // double quote in string p = ReadText( p, &value, false, end, false, encoding ); } else { // All attribute values should be in single or double quotes. // But this is such a common error that the parser will try // its best, even without them. value = ""; while ( p && *p // existence && !IsWhiteSpace( *p ) && *p != '\n' && *p != '\r' // whitespace && *p != '/' && *p != '>' ) // tag end { if ( *p == SINGLE_QUOTE || *p == DOUBLE_QUOTE ) { // [ 1451649 ] Attribute values with trailing quotes not handled correctly // We did not have an opening quote but seem to have a // closing one. Give up and throw an error. if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); return 0; } value += *p; ++p; } } return p; } void XmlText::StreamIn( std::istream * in, std::string * tag ) { while ( in->good() ) { int c = in->peek(); if ( !cdata && (c == '<' ) ) { return; } if ( c <= 0 ) { XmlDocument* document = GetDocument(); if ( document ) document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); return; } (*tag) += (char) c; in->get(); // "commits" the peek made above if ( cdata && c == '>' && tag->size() >= 3 ) { size_t len = tag->size(); if ( (*tag)[len-2] == ']' && (*tag)[len-3] == ']' ) { // terminator of cdata. return; } } } } const char* XmlText::Parse( const char* p, XmlParsingData* data, XmlEncoding encoding ) { value = ""; XmlDocument* document = GetDocument(); if ( data ) { data->Stamp( p, encoding ); location = data->Cursor(); } const char* const startTag = ""; if ( cdata || StringEqual( p, startTag, false, encoding ) ) { cdata = true; if ( !StringEqual( p, startTag, false, encoding ) ) { document->SetError( TIXML_ERROR_PARSING_CDATA, p, data, encoding ); return 0; } p += strlen( startTag ); // Keep all the white space, ignore the encoding, etc. while ( p && *p && !StringEqual( p, endTag, false, encoding ) ) { value += *p; ++p; } std::string dummy; p = ReadText( p, &dummy, false, endTag, false, encoding ); return p; } else { bool ignoreWhite = true; const char* end = "<"; p = ReadText( p, &value, ignoreWhite, end, false, encoding ); if ( p ) return p-1; // don't truncate the '<' return 0; } } void XmlDeclaration::StreamIn( std::istream * in, std::string * tag ) { while ( in->good() ) { int c = in->get(); if ( c <= 0 ) { XmlDocument* document = GetDocument(); if ( document ) document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); return; } (*tag) += (char) c; if ( c == '>' ) { // All is well. return; } } } const char* XmlDeclaration::Parse( const char* p, XmlParsingData* data, XmlEncoding _encoding ) { p = SkipWhiteSpace( p, _encoding ); // Find the beginning, find the end, and look for // the stuff in-between. XmlDocument* document = GetDocument(); if ( !p || !*p || !StringEqual( p, "SetError( TIXML_ERROR_PARSING_DECLARATION, 0, 0, _encoding ); return 0; } if ( data ) { data->Stamp( p, _encoding ); location = data->Cursor(); } p += 5; version = ""; encoding = ""; standalone = ""; while ( p && *p ) { if ( *p == '>' ) { ++p; return p; } p = SkipWhiteSpace( p, _encoding ); if ( StringEqual( p, "version", true, _encoding ) ) { XmlAttribute attrib; p = attrib.Parse( p, data, _encoding ); version = attrib.Value(); } else if ( StringEqual( p, "encoding", true, _encoding ) ) { XmlAttribute attrib; p = attrib.Parse( p, data, _encoding ); encoding = attrib.Value(); } else if ( StringEqual( p, "standalone", true, _encoding ) ) { XmlAttribute attrib; p = attrib.Parse( p, data, _encoding ); standalone = attrib.Value(); } else { // Read over whatever it is. while( p && *p && *p != '>' && !IsWhiteSpace( *p ) ) ++p; } } return 0; } bool XmlText::Blank() const { for ( unsigned i=0; iappend( str.c_str() + i, 1 ); ++i; if ( str[i] == ';' ) break; } } else if ( c == '&' ) { outString->append( entity[0].str, entity[0].strLength ); ++i; } else if ( c == '<' ) { outString->append( entity[1].str, entity[1].strLength ); ++i; } else if ( c == '>' ) { outString->append( entity[2].str, entity[2].strLength ); ++i; } else if ( c == '\"' ) { outString->append( entity[3].str, entity[3].strLength ); ++i; } else if ( c == '\'' ) { outString->append( entity[4].str, entity[4].strLength ); ++i; } else if ( c < 32 ) { // Easy pass at non-alpha/numeric/symbol // Below 32 is symbolic. char buf[ 32 ]; #if defined(TIXML_SNPRINTF) TIXML_SNPRINTF( buf, sizeof(buf), "&#x%02X;", (unsigned) ( c & 0xff ) ); #else sprintf( buf, "&#x%02X;", (unsigned) ( c & 0xff ) ); #endif //*ME: warning C4267: convert 'size_t' to 'int' //*ME: Int-Cast to make compiler happy ... outString->append( buf, (int)strlen( buf ) ); ++i; } else { //char realc = (char) c; //outString->append( &realc, 1 ); *outString += (char) c; // somewhat more efficient function call. ++i; } } } XmlNode::XmlNode( NodeType _type ) : XmlBase() { parent = 0; type = _type; firstChild = 0; lastChild = 0; prev = 0; next = 0; } XmlNode::~XmlNode() { XmlNode* node = firstChild; XmlNode* temp = 0; while ( node ) { temp = node; node = node->next; delete temp; } } void XmlNode::CopyTo( XmlNode* target ) const { target->SetValue (value.c_str() ); target->userData = userData; } void XmlNode::Clear() { XmlNode* node = firstChild; XmlNode* temp = 0; while ( node ) { temp = node; node = node->next; delete temp; } firstChild = 0; lastChild = 0; } XmlNode* XmlNode::LinkEndChild( XmlNode* node ) { assert( node->parent == 0 || node->parent == this ); assert( node->GetDocument() == 0 || node->GetDocument() == this->GetDocument() ); if ( node->Type() == XmlNode::DOCUMENT ) { delete node; if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); return 0; } node->parent = this; node->prev = lastChild; node->next = 0; if ( lastChild ) lastChild->next = node; else firstChild = node; // it was an empty list. lastChild = node; return node; } XmlNode* XmlNode::InsertEndChild( const XmlNode& addThis ) { if ( addThis.Type() == XmlNode::DOCUMENT ) { if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); return 0; } XmlNode* node = addThis.Clone(); if ( !node ) return 0; return LinkEndChild( node ); } XmlNode* XmlNode::InsertBeforeChild( XmlNode* beforeThis, const XmlNode& addThis ) { if ( !beforeThis || beforeThis->parent != this ) { return 0; } if ( addThis.Type() == XmlNode::DOCUMENT ) { if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); return 0; } XmlNode* node = addThis.Clone(); if ( !node ) return 0; node->parent = this; node->next = beforeThis; node->prev = beforeThis->prev; if ( beforeThis->prev ) { beforeThis->prev->next = node; } else { assert( firstChild == beforeThis ); firstChild = node; } beforeThis->prev = node; return node; } XmlNode* XmlNode::InsertAfterChild( XmlNode* afterThis, const XmlNode& addThis ) { if ( !afterThis || afterThis->parent != this ) { return 0; } if ( addThis.Type() == XmlNode::DOCUMENT ) { if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); return 0; } XmlNode* node = addThis.Clone(); if ( !node ) return 0; node->parent = this; node->prev = afterThis; node->next = afterThis->next; if ( afterThis->next ) { afterThis->next->prev = node; } else { assert( lastChild == afterThis ); lastChild = node; } afterThis->next = node; return node; } XmlNode* XmlNode::ReplaceChild( XmlNode* replaceThis, const XmlNode& withThis ) { if ( replaceThis->parent != this ) return 0; XmlNode* node = withThis.Clone(); if ( !node ) return 0; node->next = replaceThis->next; node->prev = replaceThis->prev; if ( replaceThis->next ) replaceThis->next->prev = node; else lastChild = node; if ( replaceThis->prev ) replaceThis->prev->next = node; else firstChild = node; delete replaceThis; node->parent = this; return node; } bool XmlNode::RemoveChild( XmlNode* removeThis ) { if ( removeThis->parent != this ) { assert( 0 ); return false; } if ( removeThis->next ) removeThis->next->prev = removeThis->prev; else lastChild = removeThis->prev; if ( removeThis->prev ) removeThis->prev->next = removeThis->next; else firstChild = removeThis->next; delete removeThis; return true; } const XmlNode* XmlNode::FirstChild( const char * _value ) const { const XmlNode* node; for ( node = firstChild; node; node = node->next ) { if ( strcmp( node->Value(), _value ) == 0 ) return node; } return 0; } const XmlNode* XmlNode::LastChild( const char * _value ) const { const XmlNode* node; for ( node = lastChild; node; node = node->prev ) { if ( strcmp( node->Value(), _value ) == 0 ) return node; } return 0; } const XmlNode* XmlNode::IterateChildren( const XmlNode* previous ) const { if ( !previous ) { return FirstChild(); } else { assert( previous->parent == this ); return previous->NextSibling(); } } const XmlNode* XmlNode::IterateChildren( const char * val, const XmlNode* previous ) const { if ( !previous ) { return FirstChild( val ); } else { assert( previous->parent == this ); return previous->NextSibling( val ); } } const XmlNode* XmlNode::NextSibling( const char * _value ) const { const XmlNode* node; for ( node = next; node; node = node->next ) { if ( strcmp( node->Value(), _value ) == 0 ) return node; } return 0; } const XmlNode* XmlNode::PreviousSibling( const char * _value ) const { const XmlNode* node; for ( node = prev; node; node = node->prev ) { if ( strcmp( node->Value(), _value ) == 0 ) return node; } return 0; } void XmlElement::RemoveAttribute( const char * name ) { std::string str( name ); XmlAttribute* node = attributeSet.Find( str ); if ( node ) { attributeSet.Remove( node ); delete node; } } const XmlElement* XmlNode::FirstChildElement() const { const XmlNode* node; for ( node = FirstChild(); node; node = node->NextSibling() ) { if ( node->ToElement() ) return node->ToElement(); } return 0; } const XmlElement* XmlNode::FirstChildElement( const char * _value ) const { const XmlNode* node; for ( node = FirstChild( _value ); node; node = node->NextSibling( _value ) ) { if ( node->ToElement() ) return node->ToElement(); } return 0; } const XmlElement* XmlNode::NextSiblingElement() const { const XmlNode* node; for ( node = NextSibling(); node; node = node->NextSibling() ) { if ( node->ToElement() ) return node->ToElement(); } return 0; } const XmlElement* XmlNode::NextSiblingElement( const char * _value ) const { const XmlNode* node; for ( node = NextSibling( _value ); node; node = node->NextSibling( _value ) ) { if ( node->ToElement() ) return node->ToElement(); } return 0; } const XmlDocument* XmlNode::GetDocument() const { const XmlNode* node; for( node = this; node; node = node->parent ) { if ( node->ToDocument() ) return node->ToDocument(); } return 0; } XmlElement::XmlElement (const char * _value) : XmlNode( XmlNode::ELEMENT ) { firstChild = lastChild = 0; value = _value; } XmlElement::XmlElement( const std::string& _value ) : XmlNode( XmlNode::ELEMENT ) { firstChild = lastChild = 0; value = _value; } XmlElement::XmlElement( const XmlElement& copy) : XmlNode( XmlNode::ELEMENT ) { firstChild = lastChild = 0; copy.CopyTo( this ); } void XmlElement::operator=( const XmlElement& base ) { ClearThis(); base.CopyTo( this ); } XmlElement::~XmlElement() { ClearThis(); } void XmlElement::ClearThis() { Clear(); while( attributeSet.First() ) { XmlAttribute* node = attributeSet.First(); attributeSet.Remove( node ); delete node; } } const char* XmlElement::Attribute( std::string name ) const { return Attribute((const char *)name.c_str()); } const char* XmlElement::Attribute( std::string name, int* i ) const { return Attribute((const char *)name.c_str(), i); } const char* XmlElement::Attribute( std::string name, double* d ) const { return Attribute((const char *)name.c_str(), d); } int XmlElement::QueryIntAttribute( const char* name, int* ival ) const { const XmlAttribute* node = attributeSet.Find( name ); if ( !node ) return TIXML_NO_ATTRIBUTE; return node->QueryIntValue( ival ); } const char* XmlElement::Attribute( const char* name ) const { const XmlAttribute* node = attributeSet.Find( name ); if ( node ) return node->Value(); return 0; } const char* XmlElement::Attribute( const char* name, int* i ) const { const char* s = Attribute( name ); if ( i ) { if ( s ) { *i = atoi( s ); } else { *i = 0; } } return s; } const char* XmlElement::Attribute( const char* name, double* d ) const { const char* s = Attribute( name ); if ( d ) { if ( s ) { *d = atof( s ); } else { *d = 0; } } return s; } int XmlElement::QueryIntAttribute( const std::string& name, int* ival ) const { const XmlAttribute* node = attributeSet.Find( name ); if ( !node ) return TIXML_NO_ATTRIBUTE; return node->QueryIntValue( ival ); } int XmlElement::QueryDoubleAttribute( const char* name, double* dval ) const { const XmlAttribute* node = attributeSet.Find( name ); if ( !node ) return TIXML_NO_ATTRIBUTE; return node->QueryDoubleValue( dval ); } int XmlElement::QueryDoubleAttribute( const std::string& name, double* dval ) const { const XmlAttribute* node = attributeSet.Find( name ); if ( !node ) return TIXML_NO_ATTRIBUTE; return node->QueryDoubleValue( dval ); } void XmlElement::SetAttribute( const char * name, int val ) { char buf[64]; #if defined(TIXML_SNPRINTF) TIXML_SNPRINTF( buf, sizeof(buf), "%d", val ); #else sprintf( buf, "%d", val ); #endif SetAttribute( name, buf ); } void XmlElement::SetAttribute( const std::string& name, int val ) { std::ostringstream oss; oss << val; SetAttribute( name, oss.str() ); } void XmlElement::SetDoubleAttribute( const char * name, double val ) { char buf[256]; #if defined(TIXML_SNPRINTF) TIXML_SNPRINTF( buf, sizeof(buf), "%f", val ); #else sprintf( buf, "%f", val ); #endif SetAttribute( name, buf ); } void XmlElement::SetAttribute( const char * cname, const char * cvalue ) { std::string _name( cname ); std::string _value( cvalue ); XmlAttribute* node = attributeSet.Find( _name ); if ( node ) { node->SetValue( _value ); return; } try { XmlAttribute* attrib = new XmlAttribute( cname, cvalue ); if ( attrib ) { attributeSet.Add( attrib ); } } catch (std::bad_alloc &) { XmlDocument* document = GetDocument(); if ( document ) { document->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN ); } } } void XmlElement::SetAttribute( const std::string& name, const std::string& _value ) { XmlAttribute* node = attributeSet.Find( name ); if ( node ) { node->SetValue( _value ); return; } try { XmlAttribute* attrib = new XmlAttribute( name, _value ); if ( attrib ) { attributeSet.Add( attrib ); } } catch (std::bad_alloc &) { XmlDocument* document = GetDocument(); if ( document ) { document->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN ); } } } void XmlElement::Print( FILE* cfile, int depth ) const { int i; assert( cfile ); for ( i=0; iNext() ) { fprintf( cfile, " " ); attrib->Print( cfile, depth ); } // There are 3 different formatting approaches: // 1) An element without children is printed as a node // 2) An element with only a text child is printed as text // 3) An element with children is printed on multiple lines. XmlNode* node; if ( !firstChild ) { fprintf( cfile, " />" ); } else if ( firstChild == lastChild && firstChild->ToText() ) { fprintf( cfile, ">" ); firstChild->Print( cfile, depth + 1 ); fprintf( cfile, "", value.c_str() ); } else { fprintf( cfile, ">" ); for ( node = firstChild; node; node=node->NextSibling() ) { if ( !node->ToText() ) { fprintf( cfile, "\n" ); } node->Print( cfile, depth+1 ); } fprintf( cfile, "\n" ); for( i=0; i", value.c_str() ); } } void XmlElement::CopyTo( XmlElement* target ) const { // superclass: XmlNode::CopyTo( target ); // Element class: // Clone the attributes, then clone the children. const XmlAttribute* attribute = 0; for( attribute = attributeSet.First(); attribute; attribute = attribute->Next() ) { target->SetAttribute( attribute->Name(), attribute->Value() ); } XmlNode* node = 0; for ( node = firstChild; node; node = node->NextSibling() ) { target->LinkEndChild( node->Clone() ); } } bool XmlElement::Accept( XmlVisitor* visitor ) const { if ( visitor->VisitEnter( *this, attributeSet.First() ) ) { for ( const XmlNode* node=FirstChild(); node; node=node->NextSibling() ) { if ( !node->Accept( visitor ) ) break; } } return visitor->VisitExit( *this ); } XmlNode* XmlElement::Clone() const { try { XmlElement* clone = new XmlElement( Value() ); CopyTo( clone ); return clone; } catch (std::bad_alloc &) { } return NULL; } std::string XmlElement::GetText() const { const XmlNode* child = this->FirstChild(); if ( child ) { const XmlText* childText = child->ToText(); if ( childText ) { return childText->Value(); } } return ""; } XmlDocument::XmlDocument() : XmlNode( XmlNode::DOCUMENT ) { tabsize = 4; useMicrosoftBOM = false; ClearError(); } XmlDocument::XmlDocument( const char * documentName ) : XmlNode( XmlNode::DOCUMENT ) { tabsize = 4; useMicrosoftBOM = false; value = documentName; ClearError(); } XmlDocument::XmlDocument( const std::string& documentName ) : XmlNode( XmlNode::DOCUMENT ) { tabsize = 4; useMicrosoftBOM = false; value = documentName; ClearError(); } XmlDocument::XmlDocument( const XmlDocument& copy ) : XmlNode( XmlNode::DOCUMENT ) { copy.CopyTo( this ); } void XmlDocument::operator=( const XmlDocument& copy ) { Clear(); copy.CopyTo( this ); } bool XmlDocument::LoadFile( XmlEncoding encoding ) { return LoadFile( Value(), encoding ); } bool XmlDocument::SaveFile() const { return SaveFile( Value() ); } bool XmlDocument::LoadFile( const char* _filename, XmlEncoding encoding ) { // There was a really terrifying little bug here. The code: // value = filename // in the STL case, cause the assignment method of the std::string to // be called. What is strange, is that the std::string had the same // address as it's c_str() method, and so bad things happen. Looks // like a bug in the Microsoft STL implementation. // Add an extra string to avoid the crash. std::string filename( _filename ); value = filename; // reading in binary mode so that tinyxml can normalize the EOL FILE* file = fopen( value.c_str (), "rb" ); if ( file ) { bool result = LoadFile( file, encoding ); fclose( file ); return result; } else { SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); return false; } } bool XmlDocument::LoadFile( FILE* file, XmlEncoding encoding ) { if ( !file ) { SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); return false; } // Delete the existing data: Clear(); location.Clear(); // Get the file size, so we can pre-allocate the string. HUGE speed impact. long length = 0; fseek( file, 0, SEEK_END ); length = ftell( file ); fseek( file, 0, SEEK_SET ); // Strange case, but good to handle up front. if ( length == 0 ) { SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); return false; } // If we have a file, assume it is all one big XML file, and read it in. // The document parser may decide the document ends sooner than the entire file, however. std::string data; data.reserve( length ); // Subtle bug here. nyXml did use fgets. But from the XML spec: // 2.11 End-of-Line Handling // // // ...the XML processor MUST behave as if it normalized all line breaks in external // parsed entities (including the document entity) on input, before parsing, by translating // both the two-character sequence #xD #xA and any #xD that is not followed by #xA to // a single #xA character. // // // It is not clear fgets does that, and certainly isn't clear it works cross platform. // Generally, you expect fgets to translate from the convention of the OS to the c/unix // convention, and not work generally. /* while( fgets( buf, sizeof(buf), file ) ) { data += buf; } */ char *buf = NULL; try { buf = new char[ length+1 ]; } catch (std::bad_alloc &) { return false; } buf[0] = 0; if ( fread( buf, length, 1, file ) != 1 ) { delete [] buf; SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); return false; } const char* lastPos = buf; const char* p = buf; buf[length] = 0; while( *p ) { assert( p < (buf+length) ); if ( *p == 0xa ) { // Newline character. No special rules for this. Append all the characters // since the last string, and include the newline. data.append( lastPos, (p-lastPos+1) ); // append, include the newline ++p; // move past the newline lastPos = p; // and point to the new buffer (may be 0) assert( p <= (buf+length) ); } else if ( *p == 0xd ) { // Carriage return. Append what we have so far, then // handle moving forward in the buffer. if ( (p-lastPos) > 0 ) { data.append( lastPos, p-lastPos ); // do not add the CR } data += (char)0xa; // a proper newline if ( *(p+1) == 0xa ) { // Carriage return - new line sequence p += 2; lastPos = p; assert( p <= (buf+length) ); } else { // it was followed by something else...that is presumably characters again. ++p; lastPos = p; assert( p <= (buf+length) ); } } else { ++p; } } // Handle any left over characters. if ( p-lastPos ) { data.append( lastPos, p-lastPos ); } delete [] buf; buf = 0; Parse( data.c_str(), 0, encoding ); if ( Error() ) return false; else return true; } bool XmlDocument::SaveFile( const char * filename ) const { // The old c stuff lives on... FILE* fp = fopen( filename, "w" ); if ( fp ) { bool result = SaveFile( fp ); fclose( fp ); return result; } return false; } bool XmlDocument::SaveFile( FILE* fp ) const { if ( useMicrosoftBOM ) { const uint8_t TIXML_UTF_LEAD_0 = 0xefU; const uint8_t TIXML_UTF_LEAD_1 = 0xbbU; const uint8_t TIXML_UTF_LEAD_2 = 0xbfU; fputc( TIXML_UTF_LEAD_0, fp ); fputc( TIXML_UTF_LEAD_1, fp ); fputc( TIXML_UTF_LEAD_2, fp ); } Print( fp, 0 ); return (ferror(fp) == 0); } void XmlDocument::CopyTo( XmlDocument* target ) const { XmlNode::CopyTo( target ); target->error = error; target->errorDesc = errorDesc.c_str (); XmlNode* node = 0; for ( node = firstChild; node; node = node->NextSibling() ) { target->LinkEndChild( node->Clone() ); } } XmlNode * XmlDocument::Clone() const { try { XmlDocument* clone = new XmlDocument(); CopyTo( clone ); return clone; } catch (std::bad_alloc &) { } return NULL; } void XmlDocument::Print( FILE* cfile, int depth ) const { assert( cfile ); for ( const XmlNode* node=FirstChild(); node; node=node->NextSibling() ) { node->Print( cfile, depth ); fprintf( cfile, "\n" ); } } 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 ); } const XmlAttribute* XmlAttribute::Next() const { // We are using knowledge of the sentinel. The sentinel // have a value or name. if ( next->value.empty() && next->name.empty() ) return 0; return next; } /* XmlAttribute* XmlAttribute::Next() { // We are using knowledge of the sentinel. The sentinel // have a value or name. if ( next->value.empty() && next->name.empty() ) return 0; return next; } */ const XmlAttribute* XmlAttribute::Previous() const { // We are using knowledge of the sentinel. The sentinel // have a value or name. if ( prev->value.empty() && prev->name.empty() ) return 0; return prev; } /* XmlAttribute* XmlAttribute::Previous() { // We are using knowledge of the sentinel. The sentinel // have a value or name. if ( prev->value.empty() && prev->name.empty() ) return 0; return prev; } */ void XmlAttribute::Print( FILE* cfile, int /*depth*/, std::string* str ) const { std::string n, v; PutString( name, &n ); PutString( value, &v ); if (value.find ('\"') == std::string::npos) { if ( cfile ) { fprintf (cfile, "%s=\"%s\"", n.c_str(), v.c_str() ); } if ( str ) { (*str) += n; (*str) += "=\""; (*str) += v; (*str) += "\""; } } else { if ( cfile ) { fprintf (cfile, "%s='%s'", n.c_str(), v.c_str() ); } if ( str ) { (*str) += n; (*str) += "='"; (*str) += v; (*str) += "'"; } } } int XmlAttribute::QueryIntValue( int* ival ) const { if ( sscanf( value.c_str(), "%d", ival ) == 1 ) return TIXML_SUCCESS; return TIXML_WRONG_TYPE; } int XmlAttribute::QueryDoubleValue( double* dval ) const { if ( sscanf( value.c_str(), "%lf", dval ) == 1 ) return TIXML_SUCCESS; return TIXML_WRONG_TYPE; } void XmlAttribute::SetIntValue( int _value ) { char buf [64]; #if defined(TIXML_SNPRINTF) TIXML_SNPRINTF(buf, sizeof(buf), "%d", _value); #else sprintf (buf, "%d", _value); #endif SetValue (buf); } void XmlAttribute::SetDoubleValue( double _value ) { char buf [256]; #if defined(TIXML_SNPRINTF) TIXML_SNPRINTF( buf, sizeof(buf), "%lf", _value); #else sprintf (buf, "%lf", _value); #endif SetValue (buf); } int XmlAttribute::IntValue() const { return atoi (value.c_str ()); } double XmlAttribute::DoubleValue() const { return atof (value.c_str ()); } XmlComment::XmlComment( const XmlComment& copy ) : XmlNode( XmlNode::COMMENT ) { copy.CopyTo( this ); } void XmlComment::operator=( const XmlComment& base ) { Clear(); base.CopyTo( this ); } void XmlComment::Print( FILE* cfile, int depth ) const { assert( cfile ); for ( int i=0; i", value.c_str() ); } void XmlComment::CopyTo( XmlComment* target ) const { XmlNode::CopyTo( target ); } bool XmlComment::Accept( XmlVisitor* visitor ) const { return visitor->Visit( *this ); } XmlNode* XmlComment::Clone() const { try { XmlComment* clone = new XmlComment(); CopyTo( clone ); return clone; } catch (std::bad_alloc &) { } return NULL; } void XmlText::Print( FILE* cfile, int depth ) const { assert( cfile ); if ( cdata ) { int i; fprintf( cfile, "\n" ); for ( i=0; i\n", value.c_str() ); // unformatted output } else { std::string buffer; PutString( value, &buffer ); fprintf( cfile, "%s", buffer.c_str() ); } } bool XmlText::CDATA() const { return cdata; } void XmlText::SetCDATA( bool _cdata ) { cdata = _cdata; } void XmlText::CopyTo( XmlText* target ) const { XmlNode::CopyTo( target ); target->cdata = cdata; } bool XmlText::Accept( XmlVisitor* visitor ) const { return visitor->Visit( *this ); } XmlNode* XmlText::Clone() const { try { XmlText *clone = new XmlText( "" ); CopyTo( clone ); return clone; } catch (std::bad_alloc &) { } return NULL; } XmlDeclaration::XmlDeclaration( const char * _version, const char * _encoding, const char * _standalone ) : XmlNode( XmlNode::DECLARATION ) { version = _version; encoding = _encoding; standalone = _standalone; } XmlDeclaration::XmlDeclaration( const std::string& _version, const std::string& _encoding, const std::string& _standalone ) : XmlNode( XmlNode::DECLARATION ) { version = _version; encoding = _encoding; standalone = _standalone; } XmlDeclaration::XmlDeclaration( const XmlDeclaration& copy ) : XmlNode( XmlNode::DECLARATION ) { copy.CopyTo( this ); } void XmlDeclaration::operator=( const XmlDeclaration& copy ) { Clear(); copy.CopyTo( this ); } void XmlDeclaration::Print( FILE* cfile, int /*depth*/, std::string* str ) const { if ( cfile ) fprintf( cfile, "" ); if ( str ) (*str) += "?>"; } void XmlDeclaration::CopyTo( XmlDeclaration* target ) const { XmlNode::CopyTo( target ); target->version = version; target->encoding = encoding; target->standalone = standalone; } bool XmlDeclaration::Accept( XmlVisitor* visitor ) const { return visitor->Visit( *this ); } XmlNode* XmlDeclaration::Clone() const { try { XmlDeclaration* clone = new XmlDeclaration(); CopyTo( clone ); return clone; } catch (std::bad_alloc &) { } return NULL; } void XmlUnknown::Print( FILE* cfile, int depth ) const { for ( int i=0; i", value.c_str() ); } void XmlUnknown::CopyTo( XmlUnknown* target ) const { XmlNode::CopyTo( target ); } bool XmlUnknown::Accept( XmlVisitor* visitor ) const { return visitor->Visit( *this ); } XmlNode* XmlUnknown::Clone() const { try { XmlUnknown* clone = new XmlUnknown(); CopyTo( clone ); return clone; } catch (std::bad_alloc &) { } return NULL; } XmlAttributeSet::XmlAttributeSet() { sentinel.next = &sentinel; sentinel.prev = &sentinel; } XmlAttributeSet::~XmlAttributeSet() { assert( sentinel.next == &sentinel ); assert( sentinel.prev == &sentinel ); } void XmlAttributeSet::Add( XmlAttribute* addMe ) { assert( !Find( std::string( addMe->Name() ) ) ); // Shouldn't be multiply adding to the set. addMe->next = &sentinel; addMe->prev = sentinel.prev; sentinel.prev->next = addMe; sentinel.prev = addMe; } void XmlAttributeSet::Remove( XmlAttribute* removeMe ) { XmlAttribute* node; for( node = sentinel.next; node != &sentinel; node = node->next ) { if ( node == removeMe ) { node->prev->next = node->next; node->next->prev = node->prev; node->next = 0; node->prev = 0; return; } } assert( 0 ); // we tried to remove a non-linked attribute. } const XmlAttribute* XmlAttributeSet::Find( const std::string& name ) const { for( const XmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) { if ( node->name == name ) return node; } return 0; } /* XmlAttribute* XmlAttributeSet::Find( const std::string& name ) { for( XmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) { if ( node->name == name ) return node; } return 0; } */ const XmlAttribute* XmlAttributeSet::Find( const char* name ) const { for( const XmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) { if ( strcmp( node->name.c_str(), name ) == 0 ) return node; } return 0; } /* XmlAttribute* XmlAttributeSet::Find( const char* name ) { for( XmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) { if ( strcmp( node->name.c_str(), name ) == 0 ) return node; } return 0; } */ std::istream& operator>> (std::istream & in, XmlNode & base) { std::string tag; tag.reserve( 8 * 1000 ); base.StreamIn( &in, &tag ); base.Parse( tag.c_str(), 0, TIXML_DEFAULT_ENCODING ); return in; } std::ostream& operator<< (std::ostream & out, const XmlNode & base) { XmlPrinter printer; printer.SetStreamPrinting(); base.Accept( &printer ); out << printer.Str(); return out; } std::string& operator<< (std::string& out, const XmlNode& base ) { XmlPrinter printer; printer.SetStreamPrinting(); base.Accept( &printer ); out.append( printer.Str() ); return out; } XmlHandle XmlHandle::FirstChild() const { if ( node ) { XmlNode* child = node->FirstChild(); if ( child ) return XmlHandle( child ); } return XmlHandle( 0 ); } XmlHandle XmlHandle::FirstChild( const char * value ) const { if ( node ) { XmlNode* child = node->FirstChild( value ); if ( child ) return XmlHandle( child ); } return XmlHandle( 0 ); } XmlHandle XmlHandle::FirstChildElement() const { if ( node ) { XmlElement* child = node->FirstChildElement(); if ( child ) return XmlHandle( child ); } return XmlHandle( 0 ); } XmlHandle XmlHandle::FirstChildElement( const char * value ) const { if ( node ) { XmlElement* child = node->FirstChildElement( value ); if ( child ) return XmlHandle( child ); } return XmlHandle( 0 ); } XmlHandle XmlHandle::Child( int count ) const { if ( node ) { int i; XmlNode* child = node->FirstChild(); for ( i=0; child && iNextSibling(), ++i ) { // nothing } if ( child ) return XmlHandle( child ); } return XmlHandle( 0 ); } XmlHandle XmlHandle::Child( const char* value, int count ) const { if ( node ) { int i; XmlNode* child = node->FirstChild( value ); for ( i=0; child && iNextSibling( value ), ++i ) { // nothing } if ( child ) return XmlHandle( child ); } return XmlHandle( 0 ); } XmlHandle XmlHandle::ChildElement( int count ) const { if ( node ) { int i; XmlElement* child = node->FirstChildElement(); for ( i=0; child && iNextSiblingElement(), ++i ) { // nothing } if ( child ) return XmlHandle( child ); } return XmlHandle( 0 ); } XmlHandle XmlHandle::ChildElement( const char* value, int count ) const { if ( node ) { int i; XmlElement* child = node->FirstChildElement( value ); for ( i=0; child && iNextSiblingElement( value ), ++i ) { // nothing } if ( child ) return XmlHandle( child ); } return XmlHandle( 0 ); } bool XmlPrinter::VisitEnter( const XmlDocument& ) { return true; } bool XmlPrinter::VisitExit( const XmlDocument& ) { return true; } bool XmlPrinter::VisitEnter( const XmlElement& element, const XmlAttribute* firstAttribute ) { DoIndent(); buffer += "<"; buffer += element.Value(); for( const XmlAttribute* attrib = firstAttribute; attrib; attrib = attrib->Next() ) { buffer += " "; attrib->Print( 0, 0, &buffer ); } if ( !element.FirstChild() ) { buffer += " />"; DoLineBreak(); } else { buffer += ">"; if ( element.FirstChild()->ToText() && element.LastChild() == element.FirstChild() && element.FirstChild()->ToText()->CDATA() == false ) { simpleTextPrint = true; // no DoLineBreak()! } else { DoLineBreak(); } } ++depth; return true; } bool XmlPrinter::VisitExit( const XmlElement& element ) { --depth; if ( !element.FirstChild() ) { // nothing. } else { if ( simpleTextPrint ) { simpleTextPrint = false; } else { DoIndent(); } buffer += ""; DoLineBreak(); } return true; } bool XmlPrinter::Visit( const XmlText& text ) { if ( text.CDATA() ) { DoIndent(); buffer += ""; DoLineBreak(); } else if ( simpleTextPrint ) { buffer += text.Value(); } else { DoIndent(); buffer += text.Value(); DoLineBreak(); } return true; } bool XmlPrinter::Visit( const XmlDeclaration& declaration ) { DoIndent(); declaration.Print( 0, 0, &buffer ); DoLineBreak(); return true; } bool XmlPrinter::Visit( const XmlComment& comment ) { DoIndent(); buffer += ""; DoLineBreak(); return true; } bool XmlPrinter::Visit( const XmlUnknown& unknown ) { DoIndent(); buffer += "<"; buffer += unknown.Value(); buffer += ">"; DoLineBreak(); return true; } }