1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/xml/xml.cpp 
   3 // Purpose:     wxXmlDocument - XML parser & data holder class 
   4 // Author:      Vaclav Slavik 
   7 // Copyright:   (c) 2000 Vaclav Slavik 
   8 // Licence:     wxWindows licence 
   9 ///////////////////////////////////////////////////////////////////////////// 
  11 // For compilers that support precompilation, includes "wx.h". 
  12 #include "wx/wxprec.h" 
  20 #include "wx/xml/xml.h" 
  28 #include "wx/wfstream.h" 
  29 #include "wx/datstrm.h" 
  30 #include "wx/zstream.h" 
  31 #include "wx/strconv.h" 
  32 #include "wx/scopedptr.h" 
  33 #include "wx/versioninfo.h" 
  35 #include "expat.h" // from Expat 
  37 // DLL options compatibility check: 
  38 WX_CHECK_BUILD_OPTIONS("wxXML") 
  41 IMPLEMENT_CLASS(wxXmlDocument
, wxObject
) 
  44 // a private utility used by wxXML 
  45 static bool wxIsWhiteOnly(const wxString
& buf
); 
  48 //----------------------------------------------------------------------------- 
  50 //----------------------------------------------------------------------------- 
  52 wxXmlNode::wxXmlNode(wxXmlNode 
*parent
,wxXmlNodeType type
, 
  53                      const wxString
& name
, const wxString
& content
, 
  54                      wxXmlAttribute 
*attrs
, wxXmlNode 
*next
, int lineNo
) 
  55     : m_type(type
), m_name(name
), m_content(content
), 
  56       m_attrs(attrs
), m_parent(parent
), 
  57       m_children(NULL
), m_next(next
), 
  61     wxASSERT_MSG ( type 
!= wxXML_ELEMENT_NODE 
|| content
.empty(), "element nodes can't have content" ); 
  65         if (m_parent
->m_children
) 
  67             m_next 
= m_parent
->m_children
; 
  68             m_parent
->m_children 
= this; 
  71             m_parent
->m_children 
= this; 
  75 wxXmlNode::wxXmlNode(wxXmlNodeType type
, const wxString
& name
, 
  76                      const wxString
& content
, 
  78     : m_type(type
), m_name(name
), m_content(content
), 
  79       m_attrs(NULL
), m_parent(NULL
), 
  80       m_children(NULL
), m_next(NULL
), 
  81       m_lineNo(lineNo
), m_noConversion(false) 
  83     wxASSERT_MSG ( type 
!= wxXML_ELEMENT_NODE 
|| content
.empty(), "element nodes can't have content" ); 
  86 wxXmlNode::wxXmlNode(const wxXmlNode
& node
) 
  93 wxXmlNode::~wxXmlNode() 
  98 wxXmlNode
& wxXmlNode::operator=(const wxXmlNode
& node
) 
 105 void wxXmlNode::DoFree() 
 108     for (c 
= m_children
; c
; c 
= c2
) 
 114     wxXmlAttribute 
*p
, *p2
; 
 115     for (p 
= m_attrs
; p
; p 
= p2
) 
 122 void wxXmlNode::DoCopy(const wxXmlNode
& node
) 
 124     m_type 
= node
.m_type
; 
 125     m_name 
= node
.m_name
; 
 126     m_content 
= node
.m_content
; 
 127     m_lineNo 
= node
.m_lineNo
; 
 128     m_noConversion 
= node
.m_noConversion
; 
 131     wxXmlNode 
*n 
= node
.m_children
; 
 134         AddChild(new wxXmlNode(*n
)); 
 139     wxXmlAttribute 
*p 
= node
.m_attrs
; 
 142        AddAttribute(p
->GetName(), p
->GetValue()); 
 147 bool wxXmlNode::HasAttribute(const wxString
& attrName
) const 
 149     wxXmlAttribute 
*attr 
= GetAttributes(); 
 153         if (attr
->GetName() == attrName
) return true; 
 154         attr 
= attr
->GetNext(); 
 160 bool wxXmlNode::GetAttribute(const wxString
& attrName
, wxString 
*value
) const 
 162     wxCHECK_MSG( value
, false, "value argument must not be NULL" ); 
 164     wxXmlAttribute 
*attr 
= GetAttributes(); 
 168         if (attr
->GetName() == attrName
) 
 170             *value 
= attr
->GetValue(); 
 173         attr 
= attr
->GetNext(); 
 179 wxString 
wxXmlNode::GetAttribute(const wxString
& attrName
, const wxString
& defaultVal
) const 
 182     if (GetAttribute(attrName
, &tmp
)) 
 188 void wxXmlNode::AddChild(wxXmlNode 
*child
) 
 190     if (m_children 
== NULL
) 
 194         wxXmlNode 
*ch 
= m_children
; 
 195         while (ch
->m_next
) ch 
= ch
->m_next
; 
 198     child
->m_next 
= NULL
; 
 199     child
->m_parent 
= this; 
 202 // inserts a new node in front of 'followingNode' 
 203 bool wxXmlNode::InsertChild(wxXmlNode 
*child
, wxXmlNode 
*followingNode
) 
 205     wxCHECK_MSG( child
, false, "cannot insert a NULL node!" ); 
 206     wxCHECK_MSG( child
->m_parent 
== NULL
, false, "node already has a parent" ); 
 207     wxCHECK_MSG( child
->m_next 
== NULL
, false, "node already has m_next" ); 
 208     wxCHECK_MSG( followingNode 
== NULL 
|| followingNode
->GetParent() == this, 
 210                  "wxXmlNode::InsertChild - followingNode has incorrect parent" ); 
 212     // this is for backward compatibility, NULL was allowed here thanks to 
 213     // the confusion about followingNode's meaning 
 214     if ( followingNode 
== NULL 
) 
 215         followingNode 
= m_children
; 
 217     if ( m_children 
== followingNode 
) 
 219         child
->m_next 
= m_children
; 
 224         wxXmlNode 
*ch 
= m_children
; 
 225         while ( ch 
&& ch
->m_next 
!= followingNode 
) 
 229             wxFAIL_MSG( "followingNode has this node as parent, but couldn't be found among children" ); 
 233         child
->m_next 
= followingNode
; 
 237     child
->m_parent 
= this; 
 241 // inserts a new node right after 'precedingNode' 
 242 bool wxXmlNode::InsertChildAfter(wxXmlNode 
*child
, wxXmlNode 
*precedingNode
) 
 244     wxCHECK_MSG( child
, false, "cannot insert a NULL node!" ); 
 245     wxCHECK_MSG( child
->m_parent 
== NULL
, false, "node already has a parent" ); 
 246     wxCHECK_MSG( child
->m_next 
== NULL
, false, "node already has m_next" ); 
 247     wxCHECK_MSG( precedingNode 
== NULL 
|| precedingNode
->m_parent 
== this, false, 
 248                  "precedingNode has wrong parent" ); 
 252         child
->m_next 
= precedingNode
->m_next
; 
 253         precedingNode
->m_next 
= child
; 
 255     else // precedingNode == NULL 
 257         wxCHECK_MSG( m_children 
== NULL
, false, 
 258                      "NULL precedingNode only makes sense when there are no children" ); 
 260         child
->m_next 
= m_children
; 
 264     child
->m_parent 
= this; 
 268 bool wxXmlNode::RemoveChild(wxXmlNode 
*child
) 
 270     if (m_children 
== NULL
) 
 272     else if (m_children 
== child
) 
 274         m_children 
= child
->m_next
; 
 275         child
->m_parent 
= NULL
; 
 276         child
->m_next 
= NULL
; 
 281         wxXmlNode 
*ch 
= m_children
; 
 284             if (ch
->m_next 
== child
) 
 286                 ch
->m_next 
= child
->m_next
; 
 287                 child
->m_parent 
= NULL
; 
 288                 child
->m_next 
= NULL
; 
 297 void wxXmlNode::AddAttribute(const wxString
& name
, const wxString
& value
) 
 299     AddProperty(name
, value
); 
 302 void wxXmlNode::AddAttribute(wxXmlAttribute 
*attr
) 
 307 bool wxXmlNode::DeleteAttribute(const wxString
& name
) 
 309     return DeleteProperty(name
); 
 312 void wxXmlNode::AddProperty(const wxString
& name
, const wxString
& value
) 
 314     AddProperty(new wxXmlAttribute(name
, value
, NULL
)); 
 317 void wxXmlNode::AddProperty(wxXmlAttribute 
*attr
) 
 323         wxXmlAttribute 
*p 
= m_attrs
; 
 324         while (p
->GetNext()) p 
= p
->GetNext(); 
 329 bool wxXmlNode::DeleteProperty(const wxString
& name
) 
 331     wxXmlAttribute 
*attr
; 
 336     else if (m_attrs
->GetName() == name
) 
 339         m_attrs 
= attr
->GetNext(); 
 347         wxXmlAttribute 
*p 
= m_attrs
; 
 350             if (p
->GetNext()->GetName() == name
) 
 353                 p
->SetNext(attr
->GetNext()); 
 364 wxString 
wxXmlNode::GetNodeContent() const 
 366     wxXmlNode 
*n 
= GetChildren(); 
 370         if (n
->GetType() == wxXML_TEXT_NODE 
|| 
 371             n
->GetType() == wxXML_CDATA_SECTION_NODE
) 
 372             return n
->GetContent(); 
 375     return wxEmptyString
; 
 378 int wxXmlNode::GetDepth(wxXmlNode 
*grandparent
) const 
 380     const wxXmlNode 
*n 
= this; 
 387         if (n 
== grandparent
) 
 395 bool wxXmlNode::IsWhitespaceOnly() const 
 397     return wxIsWhiteOnly(m_content
); 
 402 //----------------------------------------------------------------------------- 
 404 //----------------------------------------------------------------------------- 
 406 wxXmlDocument::wxXmlDocument() 
 407     : m_version(wxS("1.0")), m_fileEncoding(wxS("UTF-8")), m_docNode(NULL
) 
 410     m_encoding 
= wxS("UTF-8"); 
 414 wxXmlDocument::wxXmlDocument(const wxString
& filename
, const wxString
& encoding
) 
 415               :wxObject(), m_docNode(NULL
) 
 417     if ( !Load(filename
, encoding
) ) 
 423 wxXmlDocument::wxXmlDocument(wxInputStream
& stream
, const wxString
& encoding
) 
 424               :wxObject(), m_docNode(NULL
) 
 426     if ( !Load(stream
, encoding
) ) 
 432 wxXmlDocument::wxXmlDocument(const wxXmlDocument
& doc
) 
 438 wxXmlDocument
& wxXmlDocument::operator=(const wxXmlDocument
& doc
) 
 445 void wxXmlDocument::DoCopy(const wxXmlDocument
& doc
) 
 447     m_version 
= doc
.m_version
; 
 449     m_encoding 
= doc
.m_encoding
; 
 451     m_fileEncoding 
= doc
.m_fileEncoding
; 
 454         m_docNode 
= new wxXmlNode(*doc
.m_docNode
); 
 459 bool wxXmlDocument::Load(const wxString
& filename
, const wxString
& encoding
, int flags
) 
 461     wxFileInputStream 
stream(filename
); 
 464     return Load(stream
, encoding
, flags
); 
 467 bool wxXmlDocument::Save(const wxString
& filename
, int indentstep
) const 
 469     wxFileOutputStream 
stream(filename
); 
 472     return Save(stream
, indentstep
); 
 475 wxXmlNode 
*wxXmlDocument::GetRoot() const 
 477     wxXmlNode 
*node 
= m_docNode
; 
 480         node 
= m_docNode
->GetChildren(); 
 481         while (node 
!= NULL 
&& node
->GetType() != wxXML_ELEMENT_NODE
) 
 482             node 
= node
->GetNext(); 
 487 wxXmlNode 
*wxXmlDocument::DetachRoot() 
 489     wxXmlNode 
*node 
= m_docNode
; 
 492         node 
= m_docNode
->GetChildren(); 
 493         wxXmlNode 
*prev 
= NULL
; 
 494         while (node 
!= NULL 
&& node
->GetType() != wxXML_ELEMENT_NODE
) 
 497             node 
= node
->GetNext(); 
 501             if (node 
== m_docNode
->GetChildren()) 
 502                 m_docNode
->SetChildren(node
->GetNext()); 
 505                 prev
->SetNext(node
->GetNext()); 
 507             node
->SetParent(NULL
); 
 514 void wxXmlDocument::SetRoot(wxXmlNode 
*root
) 
 518         wxASSERT_MSG( root
->GetType() == wxXML_ELEMENT_NODE
, 
 519                       "Can only set an element type node as root" ); 
 522     wxXmlNode 
*node 
= m_docNode
; 
 525         node 
= m_docNode
->GetChildren(); 
 526         wxXmlNode 
*prev 
= NULL
; 
 527         while (node 
!= NULL 
&& node
->GetType() != wxXML_ELEMENT_NODE
) 
 530             node 
= node
->GetNext(); 
 534             root
->SetNext( node
->GetNext() ); 
 540             m_docNode
->SetChildren(root
); 
 544         m_docNode 
= new wxXmlNode(wxXML_DOCUMENT_NODE
, wxEmptyString
); 
 545         m_docNode
->SetChildren(root
); 
 548         root
->SetParent(m_docNode
); 
 551 void wxXmlDocument::AppendToProlog(wxXmlNode 
*node
) 
 554         m_docNode 
= new wxXmlNode(wxXML_DOCUMENT_NODE
, wxEmptyString
); 
 556         m_docNode
->InsertChild( node
, GetRoot() ); 
 558         m_docNode
->AddChild( node 
); 
 561 //----------------------------------------------------------------------------- 
 562 //  wxXmlDocument loading routines 
 563 //----------------------------------------------------------------------------- 
 565 // converts Expat-produced string in UTF-8 into wxString using the specified 
 566 // conv or keep in UTF-8 if conv is NULL 
 567 static wxString 
CharToString(wxMBConv 
*conv
, 
 568                              const char *s
, size_t len 
= wxString::npos
) 
 573         // there can be no embedded NULs in this string so we don't need the 
 574         // output length, it will be NUL-terminated 
 575         const wxWCharBuffer 
wbuf( 
 576             wxConvUTF8
.cMB2WC(s
, len 
== wxString::npos 
? wxNO_LEN 
: len
, NULL
)); 
 578         return wxString(wbuf
, *conv
); 
 580     // else: the string is wanted in UTF-8 
 581 #endif // !wxUSE_UNICODE 
 584     return wxString::FromUTF8Unchecked(s
, len
); 
 587 // returns true if the given string contains only whitespaces 
 588 bool wxIsWhiteOnly(const wxString
& buf
) 
 590     for ( wxString::const_iterator i 
= buf
.begin(); i 
!= buf
.end(); ++i 
) 
 593         if ( c 
!= wxS(' ') && c 
!= wxS('\t') && c 
!= wxS('\n') && c 
!= wxS('\r')) 
 600 struct wxXmlParsingContext
 
 602     wxXmlParsingContext() 
 607           removeWhiteOnlyNodes(false) 
 612     wxXmlNode 
*node
;                    // the node being parsed 
 613     wxXmlNode 
*lastChild
;               // the last child of "node" 
 614     wxXmlNode 
*lastAsText
;              // the last _text_ child of "node" 
 617     bool       removeWhiteOnlyNodes
; 
 620 // checks that ctx->lastChild is in consistent state 
 621 #define ASSERT_LAST_CHILD_OK(ctx)                                   \ 
 622     wxASSERT( ctx->lastChild == NULL ||                             \ 
 623               ctx->lastChild->GetNext() == NULL );                  \ 
 624     wxASSERT( ctx->lastChild == NULL ||                             \ 
 625               ctx->lastChild->GetParent() == ctx->node ) 
 628 static void StartElementHnd(void *userData
, const char *name
, const char **atts
) 
 630     wxXmlParsingContext 
*ctx 
= (wxXmlParsingContext
*)userData
; 
 631     wxXmlNode 
*node 
= new wxXmlNode(wxXML_ELEMENT_NODE
, 
 632                                     CharToString(ctx
->conv
, name
), 
 634                                     XML_GetCurrentLineNumber(ctx
->parser
)); 
 635     const char **a 
= atts
; 
 637     // add node attributes 
 640         node
->AddAttribute(CharToString(ctx
->conv
, a
[0]), CharToString(ctx
->conv
, a
[1])); 
 644     ASSERT_LAST_CHILD_OK(ctx
); 
 645     ctx
->node
->InsertChildAfter(node
, ctx
->lastChild
); 
 646     ctx
->lastAsText 
= NULL
; 
 647     ctx
->lastChild 
= NULL
; // our new node "node" has no children yet 
 652 static void EndElementHnd(void *userData
, const char* WXUNUSED(name
)) 
 654     wxXmlParsingContext 
*ctx 
= (wxXmlParsingContext
*)userData
; 
 656     // we're exiting the last children of ctx->node->GetParent() and going 
 657     // back one level up, so current value of ctx->node points to the last 
 658     // child of ctx->node->GetParent() 
 659     ctx
->lastChild 
= ctx
->node
; 
 661     ctx
->node 
= ctx
->node
->GetParent(); 
 662     ctx
->lastAsText 
= NULL
; 
 665 static void TextHnd(void *userData
, const char *s
, int len
) 
 667     wxXmlParsingContext 
*ctx 
= (wxXmlParsingContext
*)userData
; 
 668     wxString str 
= CharToString(ctx
->conv
, s
, len
); 
 672         ctx
->lastAsText
->SetContent(ctx
->lastAsText
->GetContent() + str
); 
 676         bool whiteOnly 
= false; 
 677         if (ctx
->removeWhiteOnlyNodes
) 
 678             whiteOnly 
= wxIsWhiteOnly(str
); 
 682             wxXmlNode 
*textnode 
= 
 683                 new wxXmlNode(wxXML_TEXT_NODE
, wxS("text"), str
, 
 684                               XML_GetCurrentLineNumber(ctx
->parser
)); 
 686             ASSERT_LAST_CHILD_OK(ctx
); 
 687             ctx
->node
->InsertChildAfter(textnode
, ctx
->lastChild
); 
 688             ctx
->lastChild
= ctx
->lastAsText 
= textnode
; 
 693 static void StartCdataHnd(void *userData
) 
 695     wxXmlParsingContext 
*ctx 
= (wxXmlParsingContext
*)userData
; 
 697     wxXmlNode 
*textnode 
= 
 698         new wxXmlNode(wxXML_CDATA_SECTION_NODE
, wxS("cdata"), wxS(""), 
 699                       XML_GetCurrentLineNumber(ctx
->parser
)); 
 701     ASSERT_LAST_CHILD_OK(ctx
); 
 702     ctx
->node
->InsertChildAfter(textnode
, ctx
->lastChild
); 
 703     ctx
->lastChild
= ctx
->lastAsText 
= textnode
; 
 706 static void EndCdataHnd(void *userData
) 
 708     wxXmlParsingContext 
*ctx 
= (wxXmlParsingContext
*)userData
; 
 710     // we need to reset this pointer so that subsequent text nodes don't append 
 711     // their contents to this one but create new wxXML_TEXT_NODE objects (or 
 712     // not create anything at all if only white space follows the CDATA section 
 713     // and wxXMLDOC_KEEP_WHITESPACE_NODES is not used as is commonly the case) 
 714     ctx
->lastAsText 
= NULL
; 
 717 static void CommentHnd(void *userData
, const char *data
) 
 719     wxXmlParsingContext 
*ctx 
= (wxXmlParsingContext
*)userData
; 
 721     wxXmlNode 
*commentnode 
= 
 722         new wxXmlNode(wxXML_COMMENT_NODE
, 
 723                       wxS("comment"), CharToString(ctx
->conv
, data
), 
 724                       XML_GetCurrentLineNumber(ctx
->parser
)); 
 726     ASSERT_LAST_CHILD_OK(ctx
); 
 727     ctx
->node
->InsertChildAfter(commentnode
, ctx
->lastChild
); 
 728     ctx
->lastChild 
= commentnode
; 
 729     ctx
->lastAsText 
= NULL
; 
 732 static void PIHnd(void *userData
, const char *target
, const char *data
) 
 734     wxXmlParsingContext 
*ctx 
= (wxXmlParsingContext
*)userData
; 
 737         new wxXmlNode(wxXML_PI_NODE
, CharToString(ctx
->conv
, target
), 
 738                       CharToString(ctx
->conv
, data
), 
 739                       XML_GetCurrentLineNumber(ctx
->parser
)); 
 741     ASSERT_LAST_CHILD_OK(ctx
); 
 742     ctx
->node
->InsertChildAfter(pinode
, ctx
->lastChild
); 
 743     ctx
->lastChild 
= pinode
; 
 744     ctx
->lastAsText 
= NULL
; 
 747 static void DefaultHnd(void *userData
, const char *s
, int len
) 
 750     if (len 
> 6 && memcmp(s
, "<?xml ", 6) == 0) 
 752         wxXmlParsingContext 
*ctx 
= (wxXmlParsingContext
*)userData
; 
 754         wxString buf 
= CharToString(ctx
->conv
, s
, (size_t)len
); 
 756         pos 
= buf
.Find(wxS("encoding=")); 
 757         if (pos 
!= wxNOT_FOUND
) 
 758             ctx
->encoding 
= buf
.Mid(pos 
+ 10).BeforeFirst(buf
[(size_t)pos
+9]); 
 759         pos 
= buf
.Find(wxS("version=")); 
 760         if (pos 
!= wxNOT_FOUND
) 
 761             ctx
->version 
= buf
.Mid(pos 
+ 9).BeforeFirst(buf
[(size_t)pos
+8]); 
 765 static int UnknownEncodingHnd(void * WXUNUSED(encodingHandlerData
), 
 766                               const XML_Char 
*name
, XML_Encoding 
*info
) 
 768     // We must build conversion table for expat. The easiest way to do so 
 769     // is to let wxCSConv convert as string containing all characters to 
 770     // wide character representation: 
 778     for (i 
= 0; i 
< 255; i
++) 
 780         mbBuf
[0] = (char)(i
+1); 
 781         if (conv
.MB2WC(wcBuf
, mbBuf
, 2) == (size_t)-1) 
 783             // invalid/undefined byte in the encoding: 
 786         info
->map
[i
+1] = (int)wcBuf
[0]; 
 790     info
->convert 
= NULL
; 
 791     info
->release 
= NULL
; 
 798 bool wxXmlDocument::Load(wxInputStream
& stream
, const wxString
& encoding
, int flags
) 
 803     m_encoding 
= encoding
; 
 806     const size_t BUFSIZE 
= 1024; 
 808     wxXmlParsingContext ctx
; 
 810     XML_Parser parser 
= XML_ParserCreate(NULL
); 
 811     wxXmlNode 
*root 
= new wxXmlNode(wxXML_DOCUMENT_NODE
, wxEmptyString
); 
 813     ctx
.encoding 
= wxS("UTF-8"); // default in absence of encoding="" 
 816     if ( encoding
.CmpNoCase(wxS("UTF-8")) != 0 ) 
 817         ctx
.conv 
= new wxCSConv(encoding
); 
 819     ctx
.removeWhiteOnlyNodes 
= (flags 
& wxXMLDOC_KEEP_WHITESPACE_NODES
) == 0; 
 823     XML_SetUserData(parser
, (void*)&ctx
); 
 824     XML_SetElementHandler(parser
, StartElementHnd
, EndElementHnd
); 
 825     XML_SetCharacterDataHandler(parser
, TextHnd
); 
 826     XML_SetCdataSectionHandler(parser
, StartCdataHnd
, EndCdataHnd
);; 
 827     XML_SetCommentHandler(parser
, CommentHnd
); 
 828     XML_SetProcessingInstructionHandler(parser
, PIHnd
); 
 829     XML_SetDefaultHandler(parser
, DefaultHnd
); 
 830     XML_SetUnknownEncodingHandler(parser
, UnknownEncodingHnd
, NULL
); 
 835         size_t len 
= stream
.Read(buf
, BUFSIZE
).LastRead(); 
 836         done 
= (len 
< BUFSIZE
); 
 837         if (!XML_Parse(parser
, buf
, len
, done
)) 
 839             wxString 
error(XML_ErrorString(XML_GetErrorCode(parser
)), 
 841             wxLogError(_("XML parsing error: '%s' at line %d"), 
 843                        (int)XML_GetCurrentLineNumber(parser
)); 
 851         if (!ctx
.version
.empty()) 
 852             SetVersion(ctx
.version
); 
 853         if (!ctx
.encoding
.empty()) 
 854             SetFileEncoding(ctx
.encoding
); 
 855         SetDocumentNode(root
); 
 862     XML_ParserFree(parser
); 
 874 //----------------------------------------------------------------------------- 
 875 //  wxXmlDocument saving routines 
 876 //----------------------------------------------------------------------------- 
 878 // helpers for XML generation 
 882 // write string to output: 
 883 bool OutputString(wxOutputStream
& stream
, 
 892     wxUnusedVar(convMem
); 
 894         convFile 
= &wxConvUTF8
; 
 896     const wxScopedCharBuffer 
buf(str
.mb_str(*convFile
)); 
 899         // conversion failed, can't write this string in an XML file in this 
 900         // (presumably non-UTF-8) encoding 
 904     stream
.Write(buf
, buf
.length()); 
 905 #else // !wxUSE_UNICODE 
 906     if ( convFile 
&& convMem 
) 
 908         wxString 
str2(str
.wc_str(*convMem
), *convFile
); 
 909         stream
.Write(str2
.mb_str(), str2
.length()); 
 911     else // no conversions to do 
 913         stream
.Write(str
.mb_str(), str
.length()); 
 915 #endif // wxUSE_UNICODE/!wxUSE_UNICODE 
 917     return stream
.IsOk(); 
 926 // Same as above, but create entities first. 
 927 // Translates '<' to "<", '>' to ">" and so on, according to the spec: 
 928 // http://www.w3.org/TR/2000/WD-xml-c14n-20000119.html#charescaping 
 929 bool OutputEscapedString(wxOutputStream
& stream
, 
 936     escaped
.reserve(str
.length()); 
 938     for ( wxString::const_iterator i 
= str
.begin(); i 
!= str
.end(); ++i 
) 
 945                 escaped
.append(wxS("<")); 
 948                 escaped
.append(wxS(">")); 
 951                 escaped
.append(wxS("&")); 
 954                 escaped
.append(wxS("
")); 
 957                 if ( mode 
== Escape_Attribute 
) 
 962                             escaped
.append(wxS(""")); 
 965                             escaped
.append(wxS("	")); 
 968                             escaped
.append(wxS("
")); 
 982     return OutputString(stream
, escaped
, convMem
, convFile
); 
 985 bool OutputIndentation(wxOutputStream
& stream
, 
 990     wxString 
str(wxS("\n")); 
 991     str 
+= wxString(indent
, wxS(' ')); 
 992     return OutputString(stream
, str
, convMem
, convFile
); 
 995 bool OutputNode(wxOutputStream
& stream
, 
1003     switch (node
->GetType()) 
1005         case wxXML_CDATA_SECTION_NODE
: 
1006             rc 
= OutputString(stream
, wxS("<![CDATA["), convMem
, convFile
) && 
1007                  OutputString(stream
, node
->GetContent(), convMem
, convFile
) && 
1008                  OutputString(stream
, wxS("]]>"), convMem
, convFile
); 
1011         case wxXML_TEXT_NODE
: 
1012             if (node
->GetNoConversion()) 
1014                 stream
.Write(node
->GetContent().c_str(), node
->GetContent().Length()); 
1018                 rc 
= OutputEscapedString(stream
, node
->GetContent(), 
1023         case wxXML_ELEMENT_NODE
: 
1024             rc 
= OutputString(stream
, wxS("<"), convMem
, convFile
) && 
1025                  OutputString(stream
, node
->GetName(), convMem
, convFile
); 
1029                 for ( wxXmlAttribute 
*attr 
= node
->GetAttributes(); 
1031                       attr 
= attr
->GetNext() ) 
1033                     rc 
= OutputString(stream
, 
1034                                       wxS(" ") + attr
->GetName() +  wxS("=\""), 
1035                                       convMem
, convFile
) && 
1036                          OutputEscapedString(stream
, attr
->GetValue(), 
1038                                              Escape_Attribute
) && 
1039                          OutputString(stream
, wxS("\""), convMem
, convFile
); 
1043             if ( node
->GetChildren() ) 
1045                 rc 
= OutputString(stream
, wxS(">"), convMem
, convFile
); 
1047                 wxXmlNode 
*prev 
= NULL
; 
1048                 for ( wxXmlNode 
*n 
= node
->GetChildren(); 
1052                     if ( indentstep 
>= 0 && n
->GetType() != wxXML_TEXT_NODE 
) 
1054                         rc 
= OutputIndentation(stream
, indent 
+ indentstep
, 
1059                         rc 
= OutputNode(stream
, n
, indent 
+ indentstep
, 
1060                                         convMem
, convFile
, indentstep
); 
1065                 if ( rc 
&& indentstep 
>= 0 && 
1066                         prev 
&& prev
->GetType() != wxXML_TEXT_NODE 
) 
1068                     rc 
= OutputIndentation(stream
, indent
, convMem
, convFile
); 
1073                     rc 
= OutputString(stream
, wxS("</"), convMem
, convFile
) && 
1074                          OutputString(stream
, node
->GetName(), 
1075                                       convMem
, convFile
) && 
1076                          OutputString(stream
, wxS(">"), convMem
, convFile
); 
1079             else // no children, output "<foo/>" 
1081                 rc 
= OutputString(stream
, wxS("/>"), convMem
, convFile
); 
1085         case wxXML_COMMENT_NODE
: 
1086             rc 
= OutputString(stream
, wxS("<!--"), convMem
, convFile
) && 
1087                  OutputString(stream
, node
->GetContent(), convMem
, convFile
) && 
1088                  OutputString(stream
, wxS("-->"), convMem
, convFile
); 
1092             rc 
= OutputString(stream
, wxT("<?"), convMem
, convFile
) && 
1093                  OutputString(stream
, node
->GetName(), convMem
, convFile
) && 
1094                  OutputString(stream
, wxT(" "), convMem
, convFile
) && 
1095                  OutputString(stream
, node
->GetContent(), convMem
, convFile
) && 
1096                  OutputString(stream
, wxT("?>"), convMem
, convFile
); 
1100             wxFAIL_MSG("unsupported node type"); 
1107 } // anonymous namespace 
1109 bool wxXmlDocument::Save(wxOutputStream
& stream
, int indentstep
) const 
1114     wxScopedPtr
<wxMBConv
> convMem
, convFile
; 
1117     convFile
.reset(new wxCSConv(GetFileEncoding())); 
1119     if ( GetFileEncoding().CmpNoCase(GetEncoding()) != 0 ) 
1121         convFile
.reset(new wxCSConv(GetFileEncoding())); 
1122         convMem
.reset(new wxCSConv(GetEncoding())); 
1124     //else: file and in-memory encodings are the same, no conversion needed 
1127     wxString dec 
= wxString::Format( 
1128                                     wxS("<?xml version=\"%s\" encoding=\"%s\"?>\n"), 
1129                                     GetVersion(), GetFileEncoding() 
1131     bool rc 
= OutputString(stream
, dec
, convMem
.get(), convFile
.get()); 
1133     wxXmlNode 
*node 
= GetDocumentNode(); 
1135         node 
= node
->GetChildren(); 
1139         rc 
= OutputNode(stream
, node
, 0, convMem
.get(), 
1140                         convFile
.get(), indentstep
) && 
1141              OutputString(stream
, wxS("\n"), convMem
.get(), convFile
.get()); 
1142         node 
= node
->GetNext(); 
1147 /*static*/ wxVersionInfo 
wxXmlDocument::GetLibraryVersionInfo() 
1149     return wxVersionInfo("expat",