home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / wxos2233.zip / wxOS2-2_3_3.zip / wxWindows-2.3.3 / contrib / src / xrc / xml.cpp < prev    next >
C/C++ Source or Header  |  2002-07-07  |  19KB  |  713 lines

  1. /////////////////////////////////////////////////////////////////////////////
  2. // Name:        xml.cpp
  3. // Purpose:     wxXmlDocument - XML parser & data holder class
  4. // Author:      Vaclav Slavik
  5. // Created:     2000/03/05
  6. // RCS-ID:      $Id: xml.cpp,v 1.9 2002/07/03 09:49:03 VS Exp $
  7. // Copyright:   (c) 2000 Vaclav Slavik
  8. // Licence:     wxWindows licence
  9. /////////////////////////////////////////////////////////////////////////////
  10.  
  11. #ifdef __GNUG__
  12. #pragma implementation "xml.h"
  13. #pragma implementation "xmlio.h"
  14. #endif
  15.  
  16. // For compilers that support precompilation, includes "wx.h".
  17. #include "wx/wxprec.h"
  18.  
  19. #ifdef __BORLANDC__
  20.     #pragma hdrstop
  21. #endif
  22.  
  23.  
  24. #include "wx/wfstream.h"
  25. #include "wx/datstrm.h"
  26. #include "wx/zstream.h"
  27. #include "wx/log.h"
  28. #include "wx/intl.h"
  29. #include "wx/strconv.h"
  30.  
  31. #include "wx/xrc/xml.h"
  32.  
  33. #include "xmlparse.h" // from Expat
  34.  
  35. //-----------------------------------------------------------------------------
  36. //  wxXmlNode
  37. //-----------------------------------------------------------------------------
  38.  
  39. wxXmlNode::wxXmlNode(wxXmlNode *parent,wxXmlNodeType type,
  40.                      const wxString& name, const wxString& content,
  41.                      wxXmlProperty *props, wxXmlNode *next)
  42.     : m_type(type), m_name(name), m_content(content),
  43.       m_properties(props), m_parent(parent),
  44.       m_children(NULL), m_next(next)
  45. {
  46.     if (m_parent)
  47.     {
  48.         if (m_parent->m_children)
  49.         {
  50.             m_next = m_parent->m_children;
  51.             m_parent->m_children = this;
  52.         }
  53.         else
  54.             m_parent->m_children = this;
  55.     }
  56. }
  57.  
  58. wxXmlNode::wxXmlNode(wxXmlNodeType type, const wxString& name,
  59.                      const wxString& content)
  60.     : m_type(type), m_name(name), m_content(content),
  61.       m_properties(NULL), m_parent(NULL),
  62.       m_children(NULL), m_next(NULL)
  63. {}
  64.  
  65. wxXmlNode::wxXmlNode(const wxXmlNode& node)
  66. {
  67.     m_next = NULL;
  68.     m_parent = NULL;
  69.     DoCopy(node);
  70. }
  71.  
  72. wxXmlNode::~wxXmlNode()
  73. {
  74.     wxXmlNode *c, *c2;
  75.     for (c = m_children; c; c = c2)
  76.     {
  77.         c2 = c->m_next;
  78.         delete c;
  79.     }
  80.  
  81.     wxXmlProperty *p, *p2;
  82.     for (p = m_properties; p; p = p2)
  83.     {
  84.         p2 = p->GetNext();
  85.         delete p;
  86.     }
  87. }
  88.  
  89. wxXmlNode& wxXmlNode::operator=(const wxXmlNode& node)
  90. {
  91.     wxDELETE(m_properties);
  92.     wxDELETE(m_children);
  93.     DoCopy(node);
  94.     return *this;
  95. }
  96.  
  97. void wxXmlNode::DoCopy(const wxXmlNode& node)
  98. {
  99.     m_type = node.m_type;
  100.     m_name = node.m_name;
  101.     m_content = node.m_content;
  102.     m_children = NULL;
  103.  
  104.     wxXmlNode *n = node.m_children;
  105.     while (n)
  106.     {
  107.         AddChild(new wxXmlNode(*n));
  108.         n = n->GetNext();
  109.     }
  110.  
  111.     m_properties = NULL;
  112.     wxXmlProperty *p = node.m_properties;
  113.     while (p)
  114.     {
  115.        AddProperty(p->GetName(), p->GetValue());
  116.        p = p->GetNext();
  117.     }
  118. }
  119.  
  120. bool wxXmlNode::HasProp(const wxString& propName) const
  121. {
  122.     wxXmlProperty *prop = GetProperties();
  123.  
  124.     while (prop)
  125.     {
  126.         if (prop->GetName() == propName) return TRUE;
  127.         prop = prop->GetNext();
  128.     }
  129.  
  130.     return FALSE;
  131. }
  132.  
  133. bool wxXmlNode::GetPropVal(const wxString& propName, wxString *value) const
  134. {
  135.     wxXmlProperty *prop = GetProperties();
  136.  
  137.     while (prop)
  138.     {
  139.         if (prop->GetName() == propName)
  140.         {
  141.             *value = prop->GetValue();
  142.             return TRUE;
  143.         }
  144.         prop = prop->GetNext();
  145.     }
  146.  
  147.     return FALSE;
  148. }
  149.  
  150. wxString wxXmlNode::GetPropVal(const wxString& propName, const wxString& defaultVal) const
  151. {
  152.     wxString tmp;
  153.     if (GetPropVal(propName, &tmp))
  154.         return tmp;
  155.     else
  156.         return defaultVal;
  157. }
  158.  
  159. void wxXmlNode::AddChild(wxXmlNode *child)
  160. {
  161.     if (m_children == NULL)
  162.         m_children = child;
  163.     else
  164.     {
  165.         wxXmlNode *ch = m_children;
  166.         while (ch->m_next) ch = ch->m_next;
  167.         ch->m_next = child;
  168.     }
  169.     child->m_next = NULL;
  170.     child->m_parent = this;
  171. }
  172.  
  173. void wxXmlNode::InsertChild(wxXmlNode *child, wxXmlNode *before_node)
  174. {
  175.     wxASSERT_MSG(before_node->GetParent() == this, wxT("wxXmlNode::InsertChild - the node has incorrect parent"));
  176.  
  177.     if (m_children == before_node)
  178.        m_children = child;
  179.     else
  180.     {
  181.         wxXmlNode *ch = m_children;
  182.         while (ch->m_next != before_node) ch = ch->m_next;
  183.         ch->m_next = child;
  184.     }
  185.  
  186.     child->m_parent = this;
  187.     child->m_next = before_node;
  188. }
  189.  
  190. bool wxXmlNode::RemoveChild(wxXmlNode *child)
  191. {
  192.     if (m_children == NULL)
  193.         return FALSE;
  194.     else if (m_children == child)
  195.     {
  196.         m_children = child->m_next;
  197.         child->m_parent = NULL;
  198.         child->m_next = NULL;
  199.         return TRUE;
  200.     }
  201.     else
  202.     {
  203.         wxXmlNode *ch = m_children;
  204.         while (ch->m_next)
  205.         {
  206.             if (ch->m_next == child)
  207.             {
  208.                 ch->m_next = child->m_next;
  209.                 child->m_parent = NULL;
  210.                 child->m_next = NULL;
  211.                 return TRUE;
  212.             }
  213.             ch = ch->m_next;
  214.         }
  215.         return FALSE;
  216.     }
  217. }
  218.  
  219. void wxXmlNode::AddProperty(const wxString& name, const wxString& value)
  220. {
  221.     AddProperty(new wxXmlProperty(name, value, NULL));
  222. }
  223.  
  224. void wxXmlNode::AddProperty(wxXmlProperty *prop)
  225. {
  226.     if (m_properties == NULL)
  227.         m_properties = prop;
  228.     else
  229.     {
  230.         wxXmlProperty *p = m_properties;
  231.         while (p->GetNext()) p = p->GetNext();
  232.         p->SetNext(prop);
  233.     }
  234. }
  235.  
  236. bool wxXmlNode::DeleteProperty(const wxString& name)
  237. {
  238.     wxXmlProperty *prop;
  239.  
  240.     if (m_properties == NULL)
  241.         return FALSE;
  242.  
  243.     else if (m_properties->GetName() == name)
  244.     {
  245.         prop = m_properties;
  246.         m_properties = prop->GetNext();
  247.         prop->SetNext(NULL);
  248.         delete prop;
  249.         return TRUE;
  250.     }
  251.  
  252.     else
  253.     {
  254.         wxXmlProperty *p = m_properties;
  255.         while (p->GetNext())
  256.         {
  257.             if (p->GetNext()->GetName() == name)
  258.             {
  259.                 prop = p->GetNext();
  260.                 p->SetNext(prop->GetNext());
  261.                 prop->SetNext(NULL);
  262.                 delete prop;
  263.                 return TRUE;
  264.             }
  265.             p = p->GetNext();
  266.         }
  267.         return FALSE;
  268.     }
  269. }
  270.  
  271.  
  272.  
  273. //-----------------------------------------------------------------------------
  274. //  wxXmlDocument
  275. //-----------------------------------------------------------------------------
  276.  
  277. wxXmlDocument::wxXmlDocument(const wxString& filename, const wxString& encoding)
  278.                           : wxObject(), m_root(NULL)
  279. {
  280.     if ( !Load(filename, encoding) )
  281.     {
  282.         wxDELETE(m_root);
  283.     }
  284. }
  285.  
  286. wxXmlDocument::wxXmlDocument(wxInputStream& stream, const wxString& encoding)
  287.                           : wxObject(), m_root(NULL)
  288. {
  289.     if ( !Load(stream, encoding) )
  290.     {
  291.         wxDELETE(m_root);
  292.     }
  293. }
  294.  
  295. wxXmlDocument::wxXmlDocument(const wxXmlDocument& doc)
  296. {
  297.     DoCopy(doc);
  298. }
  299.  
  300. wxXmlDocument& wxXmlDocument::operator=(const wxXmlDocument& doc)
  301. {
  302.     wxDELETE(m_root);
  303.     DoCopy(doc);
  304.     return *this;
  305. }
  306.  
  307. void wxXmlDocument::DoCopy(const wxXmlDocument& doc)
  308. {
  309.     m_version = doc.m_version;
  310. #if !wxUSE_UNICODE
  311.     m_encoding = doc.m_encoding;
  312. #endif
  313.     m_fileEncoding = doc.m_fileEncoding;
  314.     m_root = new wxXmlNode(*doc.m_root);
  315. }
  316.  
  317. bool wxXmlDocument::Load(const wxString& filename, const wxString& encoding)
  318. {
  319.     wxFileInputStream stream(filename);
  320.     return Load(stream, encoding);
  321. }
  322.  
  323. bool wxXmlDocument::Save(const wxString& filename) const
  324. {
  325.     wxFileOutputStream stream(filename);
  326.     return Save(stream);
  327. }
  328.  
  329.  
  330.  
  331. //-----------------------------------------------------------------------------
  332. //  wxXmlDocument loading routines
  333. //-----------------------------------------------------------------------------
  334.  
  335. /*
  336.     FIXME:
  337.        - process all elements, including CDATA
  338.  */
  339.  
  340. // converts Expat-produced string in UTF-8 into wxString.
  341. inline static wxString CharToString(wxMBConv *conv,
  342.                                     const char *s, size_t len = wxSTRING_MAXLEN)
  343. {
  344. #if wxUSE_UNICODE
  345.     (void)conv;
  346.     return wxString(s, wxConvUTF8, len);
  347. #else
  348.     if ( conv )
  349.     {
  350.         size_t nLen = (len != wxSTRING_MAXLEN) ? len :
  351.                           nLen = wxConvUTF8.MB2WC((wchar_t*) NULL, s, 0);
  352.  
  353.         wchar_t *buf = new wchar_t[nLen+1];
  354.         wxConvUTF8.MB2WC(buf, s, nLen);
  355.         buf[nLen] = 0;
  356.         wxString s(buf, *conv, len);
  357.         delete[] buf;
  358.         return s;
  359.     }
  360.     else
  361.         return wxString(s, len);
  362. #endif
  363. }
  364.  
  365. struct wxXmlParsingContext
  366. {
  367.     wxMBConv  *conv;
  368.     wxXmlNode *root;
  369.     wxXmlNode *node;
  370.     wxXmlNode *lastAsText;
  371.     wxString   encoding;
  372.     wxString   version;
  373. };
  374.  
  375. static void StartElementHnd(void *userData, const char *name, const char **atts)
  376. {
  377.     wxXmlParsingContext *ctx = (wxXmlParsingContext*)userData;
  378.     wxXmlNode *node = new wxXmlNode(wxXML_ELEMENT_NODE, CharToString(ctx->conv, name));
  379.     const char **a = atts;
  380.     while (*a)
  381.     {
  382.         node->AddProperty(CharToString(ctx->conv, a[0]), CharToString(ctx->conv, a[1]));
  383.         a += 2;
  384.     }
  385.     if (ctx->root == NULL)
  386.         ctx->root = node;
  387.     else
  388.         ctx->node->AddChild(node);
  389.     ctx->node = node;
  390.     ctx->lastAsText = NULL;
  391. }
  392.  
  393. static void EndElementHnd(void *userData, const char* WXUNUSED(name))
  394. {
  395.     wxXmlParsingContext *ctx = (wxXmlParsingContext*)userData;
  396.  
  397.     ctx->node = ctx->node->GetParent();
  398.     ctx->lastAsText = NULL;
  399. }
  400.  
  401. static void TextHnd(void *userData, const char *s, int len)
  402. {
  403.     wxXmlParsingContext *ctx = (wxXmlParsingContext*)userData;
  404.     char *buf = new char[len + 1];
  405.  
  406.     buf[len] = '\0';
  407.     memcpy(buf, s, (size_t)len);
  408.  
  409.     if (ctx->lastAsText)
  410.     {
  411.         ctx->lastAsText->SetContent(ctx->lastAsText->GetContent() +
  412.                                     CharToString(ctx->conv, buf));
  413.     }
  414.     else
  415.     {
  416.         bool whiteOnly = TRUE;
  417.         for (char *c = buf; *c != '\0'; c++)
  418.             if (*c != ' ' && *c != '\t' && *c != '\n' && *c != '\r')
  419.             {
  420.                 whiteOnly = FALSE;
  421.                 break;
  422.             }
  423.         if (!whiteOnly)
  424.         {
  425.             ctx->lastAsText = new wxXmlNode(wxXML_TEXT_NODE, wxT("text"),
  426.                                             CharToString(ctx->conv, buf));
  427.             ctx->node->AddChild(ctx->lastAsText);
  428.         }
  429.     }
  430.  
  431.     delete[] buf;
  432. }
  433.  
  434. static void CommentHnd(void *userData, const char *data)
  435. {
  436.     wxXmlParsingContext *ctx = (wxXmlParsingContext*)userData;
  437.  
  438.     if (ctx->node)
  439.     {
  440.         // VS: ctx->node == NULL happens if there is a comment before
  441.         //     the root element (e.g. wxDesigner's output). We ignore such
  442.         //     comments, no big deal...
  443.         ctx->node->AddChild(new wxXmlNode(wxXML_COMMENT_NODE,
  444.                             wxT("comment"), CharToString(ctx->conv, data)));
  445.     }
  446.     ctx->lastAsText = NULL;
  447. }
  448.  
  449. static void DefaultHnd(void *userData, const char *s, int len)
  450. {
  451.     // XML header:
  452.     if (len > 6 && memcmp(s, "<?xml ", 6) == 0)
  453.     {
  454.         wxXmlParsingContext *ctx = (wxXmlParsingContext*)userData;
  455.  
  456.         wxString buf = CharToString(ctx->conv, s, (size_t)len);
  457.         int pos;
  458.         pos = buf.Find(wxT("encoding="));
  459.         if (pos != wxNOT_FOUND)
  460.             ctx->encoding = buf.Mid(pos + 10).BeforeFirst(buf[(size_t)pos+9]);
  461.         pos = buf.Find(wxT("version="));
  462.         if (pos != wxNOT_FOUND)
  463.             ctx->version = buf.Mid(pos + 9).BeforeFirst(buf[(size_t)pos+8]);
  464.     }
  465. }
  466.  
  467. static int UnknownEncodingHnd(void * WXUNUSED(encodingHandlerData),
  468.                                const XML_Char *name, XML_Encoding *info)
  469. {
  470.     // We must build conversion table for expat. The easiest way to do so
  471.     // is to let wxCSConv convert as string containing all characters to
  472.     // wide character representation:
  473.     wxCSConv conv(wxString(name, wxConvLibc));
  474.     char mbBuf[255];
  475.     wchar_t wcBuf[255];
  476.     size_t i;
  477.  
  478.     for (i = 0; i < 255; i++)
  479.         mbBuf[i] = (char) (i+1);
  480.     mbBuf[255] = 0;
  481.     conv.MB2WC(wcBuf, mbBuf, 255);
  482.     wcBuf[255] = 0;
  483.  
  484.     info->map[0] = 0;
  485.     for (i = 0; i < 255; i++)
  486.         info->map[i+1] = (int)wcBuf[i];
  487.  
  488.     info->data = NULL;
  489.     info->convert = NULL;
  490.     info->release = NULL;
  491.  
  492.     return 1;
  493. }
  494.  
  495. bool wxXmlDocument::Load(wxInputStream& stream, const wxString& encoding)
  496. {
  497. #if wxUSE_UNICODE
  498.     (void)encoding;
  499. #else
  500.     m_encoding = encoding;
  501. #endif
  502.  
  503.     const size_t BUFSIZE = 1024;
  504.     char buf[BUFSIZE];
  505.     wxXmlParsingContext ctx;
  506.     bool done;
  507.     XML_Parser parser = XML_ParserCreate(NULL);
  508.  
  509.     ctx.root = ctx.node = NULL;
  510.     ctx.encoding = wxT("UTF-8"); // default in absence of encoding=""
  511.     ctx.conv = NULL;
  512. #if !wxUSE_UNICODE
  513.     if ( encoding != wxT("UTF-8") && encoding != wxT("utf-8") )
  514.         ctx.conv = new wxCSConv(encoding);
  515. #endif
  516.  
  517.     XML_SetUserData(parser, (void*)&ctx);
  518.     XML_SetElementHandler(parser, StartElementHnd, EndElementHnd);
  519.     XML_SetCharacterDataHandler(parser, TextHnd);
  520.     XML_SetCommentHandler(parser, CommentHnd);
  521.     XML_SetDefaultHandler(parser, DefaultHnd);
  522.     XML_SetUnknownEncodingHandler(parser, UnknownEncodingHnd, NULL);
  523.  
  524.     do
  525.     {
  526.         size_t len = stream.Read(buf, BUFSIZE).LastRead();
  527.         done = (len < BUFSIZE);
  528.         if (!XML_Parse(parser, buf, len, done))
  529.         {
  530.             wxLogError(_("XML parsing error: '%s' at line %d"),
  531.                        XML_ErrorString(XML_GetErrorCode(parser)),
  532.                        XML_GetCurrentLineNumber(parser));
  533.           return FALSE;
  534.         }
  535.     } while (!done);
  536.  
  537.     SetVersion(ctx.version);
  538.     SetFileEncoding(ctx.encoding);
  539.     SetRoot(ctx.root);
  540.  
  541.     XML_ParserFree(parser);
  542. #if !wxUSE_UNICODE
  543.     if ( ctx.conv )
  544.         delete ctx.conv;
  545. #endif
  546.  
  547.     return TRUE;
  548.  
  549. }
  550.  
  551.  
  552.  
  553. //-----------------------------------------------------------------------------
  554. //  wxXmlDocument saving routines
  555. //-----------------------------------------------------------------------------
  556.  
  557. // write string to output:
  558. inline static void OutputString(wxOutputStream& stream, const wxString& str,
  559.                                 wxMBConv *convMem, wxMBConv *convFile)
  560. {
  561.     if (str.IsEmpty()) return;
  562. #if wxUSE_UNICODE
  563.     const wxWX2MBbuf buf(str.mb_str(convFile ? *convFile : wxConvUTF8));
  564.     stream.Write((const char*)buf, strlen((const char*)buf));
  565. #else
  566.     if ( convFile == NULL )
  567.         stream.Write(str.mb_str(), str.Len());
  568.     else
  569.     {
  570.         wxString str2(str.wc_str(*convMem), *convFile);
  571.         stream.Write(str2.mb_str(), str2.Len());
  572.     }
  573. #endif
  574. }
  575.  
  576. // Same as above, but create entities first.
  577. // Translates '<' to "<", '>' to ">" and '&' to "&"
  578. static void OutputStringEnt(wxOutputStream& stream, const wxString& str,
  579.                             wxMBConv *convMem, wxMBConv *convFile)
  580. {
  581.     wxString buf;
  582.     size_t i, last, len;
  583.     wxChar c;
  584.  
  585.     len = str.Len();
  586.     last = 0;
  587.     for (i = 0; i < len; i++)
  588.     {
  589.         c = str.GetChar(i);
  590.         if (c == wxT('<') || c == wxT('>') ||
  591.             (c == wxT('&') && str.Mid(i+1, 4) != wxT("amp;")))
  592.         {
  593.             OutputString(stream, str.Mid(last, i - last), convMem, convFile);
  594.             switch (c)
  595.             {
  596.                 case wxT('<'):
  597.                     OutputString(stream, wxT("<"), NULL, NULL);
  598.                     break;
  599.                 case wxT('>'):
  600.                     OutputString(stream, wxT(">"), NULL, NULL);
  601.                     break;
  602.                 case wxT('&'):
  603.                     OutputString(stream, wxT("&"), NULL, NULL);
  604.                     break;
  605.                 default: break;
  606.             }
  607.             last = i + 1;
  608.         }
  609.     }
  610.     OutputString(stream, str.Mid(last, i - last), convMem, convFile);
  611. }
  612.  
  613. inline static void OutputIndentation(wxOutputStream& stream, int indent)
  614. {
  615.     wxString str = wxT("\n");
  616.     for (int i = 0; i < indent; i++)
  617.         str << wxT(' ') << wxT(' ');
  618.     OutputString(stream, str, NULL, NULL);
  619. }
  620.  
  621. static void OutputNode(wxOutputStream& stream, wxXmlNode *node, int indent,
  622.                        wxMBConv *convMem, wxMBConv *convFile)
  623. {
  624.     wxXmlNode *n, *prev;
  625.     wxXmlProperty *prop;
  626.  
  627.     switch (node->GetType())
  628.     {
  629.         case wxXML_TEXT_NODE:
  630.             OutputStringEnt(stream, node->GetContent(), convMem, convFile);
  631.             break;
  632.  
  633.         case wxXML_ELEMENT_NODE:
  634.             OutputString(stream, wxT("<"), NULL, NULL);
  635.             OutputString(stream, node->GetName(), NULL, NULL);
  636.  
  637.             prop = node->GetProperties();
  638.             while (prop)
  639.             {
  640.                 OutputString(stream, wxT(" ") + prop->GetName() +
  641.                              wxT("=\"") + prop->GetValue() + wxT("\""),
  642.                              NULL, NULL);
  643.                 // FIXME - what if prop contains '"'?
  644.                 prop = prop->GetNext();
  645.             }
  646.  
  647.             if (node->GetChildren())
  648.             {
  649.                 OutputString(stream, wxT(">"), NULL, NULL);
  650.                 prev = NULL;
  651.                 n = node->GetChildren();
  652.                 while (n)
  653.                 {
  654.                     if (n && n->GetType() != wxXML_TEXT_NODE)
  655.                         OutputIndentation(stream, indent + 1);
  656.                     OutputNode(stream, n, indent + 1, convMem, convFile);
  657.                     prev = n;
  658.                     n = n->GetNext();
  659.                 }
  660.                 if (prev && prev->GetType() != wxXML_TEXT_NODE)
  661.                     OutputIndentation(stream, indent);
  662.                 OutputString(stream, wxT("</"), NULL, NULL);
  663.                 OutputString(stream, node->GetName(), NULL, NULL);
  664.                 OutputString(stream, wxT(">"), NULL, NULL);
  665.             }
  666.             else
  667.                 OutputString(stream, wxT("/>"), NULL, NULL);
  668.             break;
  669.  
  670.         case wxXML_COMMENT_NODE:
  671.             OutputString(stream, wxT("<!--"), NULL, NULL);
  672.             OutputString(stream, node->GetContent(), convMem, convFile);
  673.             OutputString(stream, wxT("-->"), NULL, NULL);
  674.             break;
  675.  
  676.         default:
  677.             wxFAIL_MSG(wxT("unsupported node type"));
  678.     }
  679. }
  680.  
  681. bool wxXmlDocument::Save(wxOutputStream& stream) const
  682. {
  683.     if ( !IsOk() )
  684.         return FALSE;
  685.  
  686.     wxString s;
  687.  
  688.     wxMBConv *convMem = NULL, *convFile = NULL;
  689. #if wxUSE_UNICODE
  690.     convFile = new wxCSConv(GetFileEncoding());
  691. #else
  692.     if ( GetFileEncoding() != GetEncoding() )
  693.     {
  694.         convFile = new wxCSConv(GetFileEncoding());
  695.         convMem = new wxCSConv(GetEncoding());
  696.     }
  697. #endif
  698.  
  699.     s.Printf(wxT("<?xml version=\"%s\" encoding=\"%s\"?>\n"),
  700.              GetVersion().c_str(), GetFileEncoding().c_str());
  701.     OutputString(stream, s, NULL, NULL);
  702.  
  703.     OutputNode(stream, GetRoot(), 0, convMem, convFile);
  704.     OutputString(stream, wxT("\n"), NULL, NULL);
  705.  
  706.     if ( convFile )
  707.         delete convFile;
  708.     if ( convMem )
  709.         delete convMem;
  710.  
  711.     return TRUE;
  712. }
  713.