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