]> git.saurik.com Git - wxWidgets.git/commitdiff
moved XML classes to the core
authorVáclav Slavík <vslavik@fastmail.fm>
Sun, 20 Jul 2003 21:37:03 +0000 (21:37 +0000)
committerVáclav Slavík <vslavik@fastmail.fm>
Sun, 20 Jul 2003 21:37:03 +0000 (21:37 +0000)
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@22172 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

include/wx/chkconf.h
include/wx/defs.h
include/wx/msw/setup0.h
include/wx/xml/xml.h [new file with mode: 0644]
src/xml/xml.cpp [new file with mode: 0644]

index 0b48bd75596abe82ec17297c75bf54720f6c2e84..bd489fd7d61ba2753ab6d6be606a5f3ccfe24259 100644 (file)
 #   endif
 #endif /* !defined(wxUSE_REGEX) */
 
 #   endif
 #endif /* !defined(wxUSE_REGEX) */
 
+#ifndef wxUSE_XML
+#   ifdef wxABORT_ON_CONFIG_ERROR
+#       error "wxUSE_XML must be defined."
+#   else
+#       define wxUSE_XML 0
+#   endif
+#endif /* !defined(wxUSE_XML) */
+
 #ifndef wxUSE_SOCKETS
 #   ifdef wxABORT_ON_CONFIG_ERROR
 #       error "wxUSE_SOCKETS must be defined."
 #ifndef wxUSE_SOCKETS
 #   ifdef wxABORT_ON_CONFIG_ERROR
 #       error "wxUSE_SOCKETS must be defined."
index d9ad71021d8554654fa7c21d8cfa30e48af79ecf..3f49f8256c53452e5eebb95a95dc20a175372c55 100644 (file)
@@ -376,6 +376,7 @@ typedef int wxWindowID;
 
     #define WXMAKINGDLL_CORE
     #define WXMAKINGDLL_HTML
 
     #define WXMAKINGDLL_CORE
     #define WXMAKINGDLL_HTML
+    #define WXMAKINGDLL_XML
 #endif // WXMAKINGDLL
 
 // WXDLLEXPORT maps to export declaration when building the DLL, to import
 #endif // WXMAKINGDLL
 
 // WXDLLEXPORT maps to export declaration when building the DLL, to import
@@ -421,6 +422,14 @@ typedef int wxWindowID;
     #define WXDLLIMPEXP_GL
 #endif
 
     #define WXDLLIMPEXP_GL
 #endif
 
+#ifdef WXMAKINGDLL_XML
+    #define WXDLLIMPEXP_XML WXEXPORT
+#elif defined(WXUSINGDLL)
+    #define WXDLLIMPEXP_XML WXIMPORT
+#else // not making nor using DLL
+    #define WXDLLIMPEXP_XML
+#endif
+
 // for backwards compatibility, define suffix-less versions too
 #define WXDLLEXPORT WXDLLIMPEXP_CORE
 #define WXDLLEXPORT_DATA WXDLLIMPEXP_DATA_CORE
 // for backwards compatibility, define suffix-less versions too
 #define WXDLLEXPORT WXDLLIMPEXP_CORE
 #define WXDLLEXPORT_DATA WXDLLIMPEXP_DATA_CORE
index 860e7d94ab11f0f2463866c1ba6d3cbf1989c715..0e32f556a420fdc021f1861bb7937acf468777d4 100644 (file)
 // wxWave class
 #define wxUSE_WAVE      1
 
 // wxWave class
 #define wxUSE_WAVE      1
 
+// XML parsing classes. Note that their API will change in the future, so
+// using wxXmlDocument and wxXmlNode in your app is not recommended.
+//
+// Default is 1
+//
+// Recommended setting: 1 (needed by XRC)
+#define wxUSE_XML       1
+
 // ----------------------------------------------------------------------------
 // Individual GUI controls
 // ----------------------------------------------------------------------------
 // ----------------------------------------------------------------------------
 // Individual GUI controls
 // ----------------------------------------------------------------------------
diff --git a/include/wx/xml/xml.h b/include/wx/xml/xml.h
new file mode 100644 (file)
index 0000000..a288989
--- /dev/null
@@ -0,0 +1,242 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        xml.h
+// Purpose:     wxXmlDocument - XML parser & data holder class
+// Author:      Vaclav Slavik
+// Created:     2000/03/05
+// RCS-ID:      $Id$
+// Copyright:   (c) 2000 Vaclav Slavik
+// Licence:     wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+
+/* ************************************************************************* *
+ *                                CAUTION!                                   *
+ *                                                                           *
+ *  The API defined in this header *WILL* change in the future and backward  *
+ *  compatibility will *not* be preserved. If you use these classes in your  *
+ *  application, it probably won't compile with future wxWindows releases.   *
+ *  Use on your own risk.                                                    *
+ *                                                                           *
+ * ************************************************************************* */
+
+#ifndef _WX_XML_H_
+#define _WX_XML_H_
+
+#if defined(__GNUG__) && !defined(__APPLE__)
+#pragma interface "xml.h"
+#endif
+
+#include "wx/defs.h"
+
+#if wxUSE_XML
+
+#include "wx/string.h"
+#include "wx/object.h"
+#include "wx/list.h"
+
+#ifdef WXMAKINGDLL_XML
+    #define WXDLLIMPEXP_XML WXEXPORT
+#elif defined(WXUSINGDLL)
+    #define WXDLLIMPEXP_XML WXIMPORT
+#else // not making nor using DLL
+    #define WXDLLIMPEXP_XML
+#endif
+
+class WXDLLIMPEXP_XML wxXmlNode;
+class WXDLLIMPEXP_XML wxXmlProperty;
+class WXDLLIMPEXP_XML wxXmlDocument;
+class WXDLLIMPEXP_XML wxXmlIOHandler;
+class WXDLLIMPEXP_BASE wxInputStream;
+class WXDLLIMPEXP_BASE wxOutputStream;
+
+
+// Represents XML node type.
+enum wxXmlNodeType
+{
+    // note: values are synchronized with xmlElementType from libxml
+    wxXML_ELEMENT_NODE       =  1,
+    wxXML_ATTRIBUTE_NODE     =  2,
+    wxXML_TEXT_NODE          =  3,
+    wxXML_CDATA_SECTION_NODE =  4,
+    wxXML_ENTITY_REF_NODE    =  5,
+    wxXML_ENTITY_NODE        =  6,
+    wxXML_PI_NODE            =  7,
+    wxXML_COMMENT_NODE       =  8,
+    wxXML_DOCUMENT_NODE      =  9,
+    wxXML_DOCUMENT_TYPE_NODE = 10,
+    wxXML_DOCUMENT_FRAG_NODE = 11,
+    wxXML_NOTATION_NODE      = 12,
+    wxXML_HTML_DOCUMENT_NODE = 13
+};
+
+
+// Represents node property(ies).
+// Example: in <img src="hello.gif" id="3"/> "src" is property with value
+//          "hello.gif" and "id" is prop. with value "3".
+
+class WXDLLIMPEXP_XML wxXmlProperty
+{
+public:
+    wxXmlProperty() : m_next(NULL) {}
+    wxXmlProperty(const wxString& name, const wxString& value,
+                  wxXmlProperty *next)
+            : m_name(name), m_value(value), m_next(next) {}
+
+    wxString GetName() const { return m_name; }
+    wxString GetValue() const { return m_value; }
+    wxXmlProperty *GetNext() const { return m_next; }
+
+    void SetName(const wxString& name) { m_name = name; }
+    void SetValue(const wxString& value) { m_value = value; }
+    void SetNext(wxXmlProperty *next) { m_next = next; }
+
+private:
+    wxString m_name;
+    wxString m_value;
+    wxXmlProperty *m_next;
+};
+
+
+
+// Represents node in XML document. Node has name and may have content
+// and properties. Most common node types are wxXML_TEXT_NODE (name and props
+// are irrelevant) and wxXML_ELEMENT_NODE (e.g. in <title>hi</title> there is
+// element with name="title", irrelevant content and one child (wxXML_TEXT_NODE
+// with content="hi").
+//
+// If wxUSE_UNICODE is 0, all strings are encoded in the encoding given to Load
+// (default is UTF-8).
+
+class WXDLLIMPEXP_XML wxXmlNode
+{
+public:
+    wxXmlNode() : m_properties(NULL), m_parent(NULL),
+                  m_children(NULL), m_next(NULL) {}
+    wxXmlNode(wxXmlNode *parent,wxXmlNodeType type,
+              const wxString& name, const wxString& content,
+              wxXmlProperty *props, wxXmlNode *next);
+    ~wxXmlNode();
+
+    // copy ctor & operator=. Note that this does NOT copy syblings
+    // and parent pointer, i.e. m_parent and m_next will be NULL
+    // after using copy ctor and are never unmodified by operator=.
+    // On the other hand, it DOES copy children and properties.
+    wxXmlNode(const wxXmlNode& node);
+    wxXmlNode& operator=(const wxXmlNode& node);
+
+    // user-friendly creation:
+    wxXmlNode(wxXmlNodeType type, const wxString& name,
+              const wxString& content = wxEmptyString);
+    void AddChild(wxXmlNode *child);
+    void InsertChild(wxXmlNode *child, wxXmlNode *before_node);
+    bool RemoveChild(wxXmlNode *child);
+    void AddProperty(const wxString& name, const wxString& value);
+    bool DeleteProperty(const wxString& name);
+
+    // access methods:
+    wxXmlNodeType GetType() const { return m_type; }
+    wxString GetName() const { return m_name; }
+    wxString GetContent() const { return m_content; }
+
+    wxXmlNode *GetParent() const { return m_parent; }
+    wxXmlNode *GetNext() const { return m_next; }
+    wxXmlNode *GetChildren() const { return m_children; }
+
+    wxXmlProperty *GetProperties() const { return m_properties; }
+    bool GetPropVal(const wxString& propName, wxString *value) const;
+    wxString GetPropVal(const wxString& propName,
+                        const wxString& defaultVal) const;
+    bool HasProp(const wxString& propName) const;
+
+    void SetType(wxXmlNodeType type) { m_type = type; }
+    void SetName(const wxString& name) { m_name = name; }
+    void SetContent(const wxString& con) { m_content = con; }
+
+    void SetParent(wxXmlNode *parent) { m_parent = parent; }
+    void SetNext(wxXmlNode *next) { m_next = next; }
+    void SetChildren(wxXmlNode *child) { m_children = child; }
+
+    void SetProperties(wxXmlProperty *prop) { m_properties = prop; }
+    void AddProperty(wxXmlProperty *prop);
+
+private:
+    wxXmlNodeType m_type;
+    wxString m_name;
+    wxString m_content;
+    wxXmlProperty *m_properties;
+    wxXmlNode *m_parent, *m_children, *m_next;
+
+    void DoCopy(const wxXmlNode& node);
+};
+
+
+
+
+
+
+
+// This class holds XML data/document as parsed by XML parser.
+
+class WXDLLIMPEXP_XML wxXmlDocument : public wxObject
+{
+public:
+    wxXmlDocument();
+    wxXmlDocument(const wxString& filename,
+                  const wxString& encoding = wxT("UTF-8"));
+    wxXmlDocument(wxInputStream& stream,
+                  const wxString& encoding = wxT("UTF-8"));
+    ~wxXmlDocument() { delete m_root; }
+
+    wxXmlDocument(const wxXmlDocument& doc);
+    wxXmlDocument& operator=(const wxXmlDocument& doc);
+
+    // Parses .xml file and loads data. Returns TRUE on success, FALSE
+    // otherwise.
+    bool Load(const wxString& filename,
+              const wxString& encoding = wxT("UTF-8"));
+    bool Load(wxInputStream& stream,
+              const wxString& encoding = wxT("UTF-8"));
+    
+    // Saves document as .xml file.
+    bool Save(const wxString& filename) const;
+    bool Save(wxOutputStream& stream) const;
+
+    bool IsOk() const { return m_root != NULL; }
+
+    // Returns root node of the document.
+    wxXmlNode *GetRoot() const { return m_root; }
+
+    // Returns version of document (may be empty).
+    wxString GetVersion() const { return m_version; }
+    // Returns encoding of document (may be empty).
+    // Note: this is the encoding original file was saved in, *not* the
+    // encoding of in-memory representation!
+    wxString GetFileEncoding() const { return m_fileEncoding; }
+
+    // Write-access methods:
+    void SetRoot(wxXmlNode *node) { delete m_root ; m_root = node; }
+    void SetVersion(const wxString& version) { m_version = version; }
+    void SetFileEncoding(const wxString& encoding) { m_fileEncoding = encoding; }
+
+#if !wxUSE_UNICODE
+    // Returns encoding of in-memory representation of the document
+    // (same as passed to Load or ctor, defaults to UTF-8).
+    // NB: this is meaningless in Unicode build where data are stored as wchar_t*
+    wxString GetEncoding() const { return m_encoding; }
+    void SetEncoding(const wxString& enc) { m_encoding = enc; }
+#endif
+
+private:
+    wxString   m_version;
+    wxString   m_fileEncoding;
+#if !wxUSE_UNICODE
+    wxString   m_encoding;
+#endif
+    wxXmlNode *m_root;
+
+    void DoCopy(const wxXmlDocument& doc);
+};
+
+#endif // wxUSE_XML
+
+#endif // _WX_XML_H_
diff --git a/src/xml/xml.cpp b/src/xml/xml.cpp
new file mode 100644 (file)
index 0000000..3b59fcf
--- /dev/null
@@ -0,0 +1,730 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        xml.cpp
+// Purpose:     wxXmlDocument - XML parser & data holder class
+// Author:      Vaclav Slavik
+// Created:     2000/03/05
+// RCS-ID:      $Id$
+// Copyright:   (c) 2000 Vaclav Slavik
+// Licence:     wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#ifdef __GNUG__
+#pragma implementation "xml.h"
+#endif
+
+// For compilers that support precompilation, includes "wx.h".
+#include "wx/wxprec.h"
+
+#ifdef __BORLANDC__
+    #pragma hdrstop
+#endif
+
+#include "wx/xml/xml.h"
+
+#if wxUSE_XML
+
+#include "wx/wfstream.h"
+#include "wx/datstrm.h"
+#include "wx/zstream.h"
+#include "wx/log.h"
+#include "wx/intl.h"
+#include "wx/strconv.h"
+
+#include "expat.h" // from Expat
+
+//-----------------------------------------------------------------------------
+//  wxXmlNode
+//-----------------------------------------------------------------------------
+
+wxXmlNode::wxXmlNode(wxXmlNode *parent,wxXmlNodeType type,
+                     const wxString& name, const wxString& content,
+                     wxXmlProperty *props, wxXmlNode *next)
+    : m_type(type), m_name(name), m_content(content),
+      m_properties(props), m_parent(parent),
+      m_children(NULL), m_next(next)
+{
+    if (m_parent)
+    {
+        if (m_parent->m_children)
+        {
+            m_next = m_parent->m_children;
+            m_parent->m_children = this;
+        }
+        else
+            m_parent->m_children = this;
+    }
+}
+
+wxXmlNode::wxXmlNode(wxXmlNodeType type, const wxString& name,
+                     const wxString& content)
+    : m_type(type), m_name(name), m_content(content),
+      m_properties(NULL), m_parent(NULL),
+      m_children(NULL), m_next(NULL)
+{}
+
+wxXmlNode::wxXmlNode(const wxXmlNode& node)
+{
+    m_next = NULL;
+    m_parent = NULL;
+    DoCopy(node);
+}
+
+wxXmlNode::~wxXmlNode()
+{
+    wxXmlNode *c, *c2;
+    for (c = m_children; c; c = c2)
+    {
+        c2 = c->m_next;
+        delete c;
+    }
+
+    wxXmlProperty *p, *p2;
+    for (p = m_properties; p; p = p2)
+    {
+        p2 = p->GetNext();
+        delete p;
+    }
+}
+
+wxXmlNode& wxXmlNode::operator=(const wxXmlNode& node)
+{
+    wxDELETE(m_properties);
+    wxDELETE(m_children);
+    DoCopy(node);
+    return *this;
+}
+
+void wxXmlNode::DoCopy(const wxXmlNode& node)
+{
+    m_type = node.m_type;
+    m_name = node.m_name;
+    m_content = node.m_content;
+    m_children = NULL;
+
+    wxXmlNode *n = node.m_children;
+    while (n)
+    {
+        AddChild(new wxXmlNode(*n));
+        n = n->GetNext();
+    }
+
+    m_properties = NULL;
+    wxXmlProperty *p = node.m_properties;
+    while (p)
+    {
+       AddProperty(p->GetName(), p->GetValue());
+       p = p->GetNext();
+    }
+}
+
+bool wxXmlNode::HasProp(const wxString& propName) const
+{
+    wxXmlProperty *prop = GetProperties();
+
+    while (prop)
+    {
+        if (prop->GetName() == propName) return TRUE;
+        prop = prop->GetNext();
+    }
+
+    return FALSE;
+}
+
+bool wxXmlNode::GetPropVal(const wxString& propName, wxString *value) const
+{
+    wxXmlProperty *prop = GetProperties();
+
+    while (prop)
+    {
+        if (prop->GetName() == propName)
+        {
+            *value = prop->GetValue();
+            return TRUE;
+        }
+        prop = prop->GetNext();
+    }
+
+    return FALSE;
+}
+
+wxString wxXmlNode::GetPropVal(const wxString& propName, const wxString& defaultVal) const
+{
+    wxString tmp;
+    if (GetPropVal(propName, &tmp))
+        return tmp;
+    else
+        return defaultVal;
+}
+
+void wxXmlNode::AddChild(wxXmlNode *child)
+{
+    if (m_children == NULL)
+        m_children = child;
+    else
+    {
+        wxXmlNode *ch = m_children;
+        while (ch->m_next) ch = ch->m_next;
+        ch->m_next = child;
+    }
+    child->m_next = NULL;
+    child->m_parent = this;
+}
+
+void wxXmlNode::InsertChild(wxXmlNode *child, wxXmlNode *before_node)
+{
+    wxASSERT_MSG(before_node->GetParent() == this, wxT("wxXmlNode::InsertChild - the node has incorrect parent"));
+
+    if (m_children == before_node)
+       m_children = child;
+    else
+    {
+        wxXmlNode *ch = m_children;
+        while (ch->m_next != before_node) ch = ch->m_next;
+        ch->m_next = child;
+    }
+
+    child->m_parent = this;
+    child->m_next = before_node;
+}
+
+bool wxXmlNode::RemoveChild(wxXmlNode *child)
+{
+    if (m_children == NULL)
+        return FALSE;
+    else if (m_children == child)
+    {
+        m_children = child->m_next;
+        child->m_parent = NULL;
+        child->m_next = NULL;
+        return TRUE;
+    }
+    else
+    {
+        wxXmlNode *ch = m_children;
+        while (ch->m_next)
+        {
+            if (ch->m_next == child)
+            {
+                ch->m_next = child->m_next;
+                child->m_parent = NULL;
+                child->m_next = NULL;
+                return TRUE;
+            }
+            ch = ch->m_next;
+        }
+        return FALSE;
+    }
+}
+
+void wxXmlNode::AddProperty(const wxString& name, const wxString& value)
+{
+    AddProperty(new wxXmlProperty(name, value, NULL));
+}
+
+void wxXmlNode::AddProperty(wxXmlProperty *prop)
+{
+    if (m_properties == NULL)
+        m_properties = prop;
+    else
+    {
+        wxXmlProperty *p = m_properties;
+        while (p->GetNext()) p = p->GetNext();
+        p->SetNext(prop);
+    }
+}
+
+bool wxXmlNode::DeleteProperty(const wxString& name)
+{
+    wxXmlProperty *prop;
+
+    if (m_properties == NULL)
+        return FALSE;
+
+    else if (m_properties->GetName() == name)
+    {
+        prop = m_properties;
+        m_properties = prop->GetNext();
+        prop->SetNext(NULL);
+        delete prop;
+        return TRUE;
+    }
+
+    else
+    {
+        wxXmlProperty *p = m_properties;
+        while (p->GetNext())
+        {
+            if (p->GetNext()->GetName() == name)
+            {
+                prop = p->GetNext();
+                p->SetNext(prop->GetNext());
+                prop->SetNext(NULL);
+                delete prop;
+                return TRUE;
+            }
+            p = p->GetNext();
+        }
+        return FALSE;
+    }
+}
+
+
+
+//-----------------------------------------------------------------------------
+//  wxXmlDocument
+//-----------------------------------------------------------------------------
+
+wxXmlDocument::wxXmlDocument()
+    : m_version(wxT("1.0")), m_fileEncoding(wxT("utf-8")), m_root(NULL)
+{
+#if !wxUSE_UNICODE
+    m_encoding = wxT("UTF-8");
+#endif
+}
+
+wxXmlDocument::wxXmlDocument(const wxString& filename, const wxString& encoding)
+                          : wxObject(), m_root(NULL)
+{
+    if ( !Load(filename, encoding) )
+    {
+        wxDELETE(m_root);
+    }
+}
+
+wxXmlDocument::wxXmlDocument(wxInputStream& stream, const wxString& encoding)
+                          : wxObject(), m_root(NULL)
+{
+    if ( !Load(stream, encoding) )
+    {
+        wxDELETE(m_root);
+    }
+}
+
+wxXmlDocument::wxXmlDocument(const wxXmlDocument& doc)
+{
+    DoCopy(doc);
+}
+
+wxXmlDocument& wxXmlDocument::operator=(const wxXmlDocument& doc)
+{
+    wxDELETE(m_root);
+    DoCopy(doc);
+    return *this;
+}
+
+void wxXmlDocument::DoCopy(const wxXmlDocument& doc)
+{
+    m_version = doc.m_version;
+#if !wxUSE_UNICODE
+    m_encoding = doc.m_encoding;
+#endif
+    m_fileEncoding = doc.m_fileEncoding;
+    m_root = new wxXmlNode(*doc.m_root);
+}
+
+bool wxXmlDocument::Load(const wxString& filename, const wxString& encoding)
+{
+    wxFileInputStream stream(filename);
+    return Load(stream, encoding);
+}
+
+bool wxXmlDocument::Save(const wxString& filename) const
+{
+    wxFileOutputStream stream(filename);
+    return Save(stream);
+}
+
+
+
+//-----------------------------------------------------------------------------
+//  wxXmlDocument loading routines
+//-----------------------------------------------------------------------------
+
+/*
+    FIXME:
+       - process all elements, including CDATA
+ */
+
+// converts Expat-produced string in UTF-8 into wxString.
+inline static wxString CharToString(wxMBConv *conv,
+                                    const char *s, size_t len = wxSTRING_MAXLEN)
+{
+#if wxUSE_UNICODE
+    (void)conv;
+    return wxString(s, wxConvUTF8, len);
+#else
+    if ( conv )
+    {
+        size_t nLen = (len != wxSTRING_MAXLEN) ? len :
+                          nLen = wxConvUTF8.MB2WC((wchar_t*) NULL, s, 0);
+
+        wchar_t *buf = new wchar_t[nLen+1];
+        wxConvUTF8.MB2WC(buf, s, nLen);
+        buf[nLen] = 0;
+        wxString str(buf, *conv, len);
+        delete[] buf;
+        return str;
+    }
+    else
+        return wxString(s, len);
+#endif
+}
+
+struct wxXmlParsingContext
+{
+    wxMBConv  *conv;
+    wxXmlNode *root;
+    wxXmlNode *node;
+    wxXmlNode *lastAsText;
+    wxString   encoding;
+    wxString   version;
+};
+
+static void StartElementHnd(void *userData, const char *name, const char **atts)
+{
+    wxXmlParsingContext *ctx = (wxXmlParsingContext*)userData;
+    wxXmlNode *node = new wxXmlNode(wxXML_ELEMENT_NODE, CharToString(ctx->conv, name));
+    const char **a = atts;
+    while (*a)
+    {
+        node->AddProperty(CharToString(ctx->conv, a[0]), CharToString(ctx->conv, a[1]));
+        a += 2;
+    }
+    if (ctx->root == NULL)
+        ctx->root = node;
+    else
+        ctx->node->AddChild(node);
+    ctx->node = node;
+    ctx->lastAsText = NULL;
+}
+
+static void EndElementHnd(void *userData, const char* WXUNUSED(name))
+{
+    wxXmlParsingContext *ctx = (wxXmlParsingContext*)userData;
+
+    ctx->node = ctx->node->GetParent();
+    ctx->lastAsText = NULL;
+}
+
+static void TextHnd(void *userData, const char *s, int len)
+{
+    wxXmlParsingContext *ctx = (wxXmlParsingContext*)userData;
+    char *buf = new char[len + 1];
+
+    buf[len] = '\0';
+    memcpy(buf, s, (size_t)len);
+
+    if (ctx->lastAsText)
+    {
+        ctx->lastAsText->SetContent(ctx->lastAsText->GetContent() +
+                                    CharToString(ctx->conv, buf));
+    }
+    else
+    {
+        bool whiteOnly = TRUE;
+        for (char *c = buf; *c != '\0'; c++)
+            if (*c != ' ' && *c != '\t' && *c != '\n' && *c != '\r')
+            {
+                whiteOnly = FALSE;
+                break;
+            }
+        if (!whiteOnly)
+        {
+            ctx->lastAsText = new wxXmlNode(wxXML_TEXT_NODE, wxT("text"),
+                                            CharToString(ctx->conv, buf));
+            ctx->node->AddChild(ctx->lastAsText);
+        }
+    }
+
+    delete[] buf;
+}
+
+static void CommentHnd(void *userData, const char *data)
+{
+    wxXmlParsingContext *ctx = (wxXmlParsingContext*)userData;
+
+    if (ctx->node)
+    {
+        // VS: ctx->node == NULL happens if there is a comment before
+        //     the root element (e.g. wxDesigner's output). We ignore such
+        //     comments, no big deal...
+        ctx->node->AddChild(new wxXmlNode(wxXML_COMMENT_NODE,
+                            wxT("comment"), CharToString(ctx->conv, data)));
+    }
+    ctx->lastAsText = NULL;
+}
+
+static void DefaultHnd(void *userData, const char *s, int len)
+{
+    // XML header:
+    if (len > 6 && memcmp(s, "<?xml ", 6) == 0)
+    {
+        wxXmlParsingContext *ctx = (wxXmlParsingContext*)userData;
+
+        wxString buf = CharToString(ctx->conv, s, (size_t)len);
+        int pos;
+        pos = buf.Find(wxT("encoding="));
+        if (pos != wxNOT_FOUND)
+            ctx->encoding = buf.Mid(pos + 10).BeforeFirst(buf[(size_t)pos+9]);
+        pos = buf.Find(wxT("version="));
+        if (pos != wxNOT_FOUND)
+            ctx->version = buf.Mid(pos + 9).BeforeFirst(buf[(size_t)pos+8]);
+    }
+}
+
+static int UnknownEncodingHnd(void * WXUNUSED(encodingHandlerData),
+                              const XML_Char *name, XML_Encoding *info)
+{
+    // We must build conversion table for expat. The easiest way to do so
+    // is to let wxCSConv convert as string containing all characters to
+    // wide character representation:
+    wxCSConv conv(wxString(name, wxConvLibc));
+    char mbBuf[2];
+    wchar_t wcBuf[10];
+    size_t i;
+
+    mbBuf[1] = 0;
+    info->map[0] = 0;
+    for (i = 0; i < 255; i++)
+    {
+        mbBuf[0] = (char)(i+1);
+        if (conv.MB2WC(wcBuf, mbBuf, 2) == (size_t)-1)
+        {
+            // invalid/undefined byte in the encoding:
+            info->map[i+1] = -1;
+        }
+        info->map[i+1] = (int)wcBuf[0];
+    }
+    
+    info->data = NULL;
+    info->convert = NULL;
+    info->release = NULL;
+
+    return 1;
+}
+
+bool wxXmlDocument::Load(wxInputStream& stream, const wxString& encoding)
+{
+#if wxUSE_UNICODE
+    (void)encoding;
+#else
+    m_encoding = encoding;
+#endif
+
+    const size_t BUFSIZE = 1024;
+    char buf[BUFSIZE];
+    wxXmlParsingContext ctx;
+    bool done;
+    XML_Parser parser = XML_ParserCreate(NULL);
+
+    ctx.root = ctx.node = NULL;
+    ctx.encoding = wxT("UTF-8"); // default in absence of encoding=""
+    ctx.conv = NULL;
+#if !wxUSE_UNICODE
+    if ( encoding != wxT("UTF-8") && encoding != wxT("utf-8") )
+        ctx.conv = new wxCSConv(encoding);
+#endif
+
+    XML_SetUserData(parser, (void*)&ctx);
+    XML_SetElementHandler(parser, StartElementHnd, EndElementHnd);
+    XML_SetCharacterDataHandler(parser, TextHnd);
+    XML_SetCommentHandler(parser, CommentHnd);
+    XML_SetDefaultHandler(parser, DefaultHnd);
+    XML_SetUnknownEncodingHandler(parser, UnknownEncodingHnd, NULL);
+
+    bool ok = true;
+    do
+    {
+        size_t len = stream.Read(buf, BUFSIZE).LastRead();
+        done = (len < BUFSIZE);
+        if (!XML_Parse(parser, buf, len, done))
+        {
+            wxLogError(_("XML parsing error: '%s' at line %d"),
+                       XML_ErrorString(XML_GetErrorCode(parser)),
+                       XML_GetCurrentLineNumber(parser));
+            ok = false;
+            break;
+        }
+    } while (!done);
+
+    if (ok)
+    {
+        SetVersion(ctx.version);
+        SetFileEncoding(ctx.encoding);
+        SetRoot(ctx.root);
+    }
+
+    XML_ParserFree(parser);
+#if !wxUSE_UNICODE
+    if ( ctx.conv )
+        delete ctx.conv;
+#endif
+
+    return ok;
+
+}
+
+
+
+//-----------------------------------------------------------------------------
+//  wxXmlDocument saving routines
+//-----------------------------------------------------------------------------
+
+// write string to output:
+inline static void OutputString(wxOutputStream& stream, const wxString& str,
+                                wxMBConv *convMem, wxMBConv *convFile)
+{
+    if (str.IsEmpty()) return;
+#if wxUSE_UNICODE
+    const wxWX2MBbuf buf(str.mb_str(convFile ? *convFile : wxConvUTF8));
+    stream.Write((const char*)buf, strlen((const char*)buf));
+#else
+    if ( convFile == NULL )
+        stream.Write(str.mb_str(), str.Len());
+    else
+    {
+        wxString str2(str.wc_str(*convMem), *convFile);
+        stream.Write(str2.mb_str(), str2.Len());
+    }
+#endif
+}
+
+// Same as above, but create entities first.
+// Translates '<' to "&lt;", '>' to "&gt;" and '&' to "&amp;"
+static void OutputStringEnt(wxOutputStream& stream, const wxString& str,
+                            wxMBConv *convMem, wxMBConv *convFile)
+{
+    wxString buf;
+    size_t i, last, len;
+    wxChar c;
+
+    len = str.Len();
+    last = 0;
+    for (i = 0; i < len; i++)
+    {
+        c = str.GetChar(i);
+        if (c == wxT('<') || c == wxT('>') ||
+            (c == wxT('&') && str.Mid(i+1, 4) != wxT("amp;")))
+        {
+            OutputString(stream, str.Mid(last, i - last), convMem, convFile);
+            switch (c)
+            {
+                case wxT('<'):
+                    OutputString(stream, wxT("&lt;"), NULL, NULL);
+                    break;
+                case wxT('>'):
+                    OutputString(stream, wxT("&gt;"), NULL, NULL);
+                    break;
+                case wxT('&'):
+                    OutputString(stream, wxT("&amp;"), NULL, NULL);
+                    break;
+                default: break;
+            }
+            last = i + 1;
+        }
+    }
+    OutputString(stream, str.Mid(last, i - last), convMem, convFile);
+}
+
+inline static void OutputIndentation(wxOutputStream& stream, int indent)
+{
+    wxString str = wxT("\n");
+    for (int i = 0; i < indent; i++)
+        str << wxT(' ') << wxT(' ');
+    OutputString(stream, str, NULL, NULL);
+}
+
+static void OutputNode(wxOutputStream& stream, wxXmlNode *node, int indent,
+                       wxMBConv *convMem, wxMBConv *convFile)
+{
+    wxXmlNode *n, *prev;
+    wxXmlProperty *prop;
+
+    switch (node->GetType())
+    {
+        case wxXML_TEXT_NODE:
+            OutputStringEnt(stream, node->GetContent(), convMem, convFile);
+            break;
+
+        case wxXML_ELEMENT_NODE:
+            OutputString(stream, wxT("<"), NULL, NULL);
+            OutputString(stream, node->GetName(), NULL, NULL);
+
+            prop = node->GetProperties();
+            while (prop)
+            {
+                OutputString(stream, wxT(" ") + prop->GetName() +
+                             wxT("=\"") + prop->GetValue() + wxT("\""),
+                             NULL, NULL);
+                // FIXME - what if prop contains '"'?
+                prop = prop->GetNext();
+            }
+
+            if (node->GetChildren())
+            {
+                OutputString(stream, wxT(">"), NULL, NULL);
+                prev = NULL;
+                n = node->GetChildren();
+                while (n)
+                {
+                    if (n && n->GetType() != wxXML_TEXT_NODE)
+                        OutputIndentation(stream, indent + 1);
+                    OutputNode(stream, n, indent + 1, convMem, convFile);
+                    prev = n;
+                    n = n->GetNext();
+                }
+                if (prev && prev->GetType() != wxXML_TEXT_NODE)
+                    OutputIndentation(stream, indent);
+                OutputString(stream, wxT("</"), NULL, NULL);
+                OutputString(stream, node->GetName(), NULL, NULL);
+                OutputString(stream, wxT(">"), NULL, NULL);
+            }
+            else
+                OutputString(stream, wxT("/>"), NULL, NULL);
+            break;
+
+        case wxXML_COMMENT_NODE:
+            OutputString(stream, wxT("<!--"), NULL, NULL);
+            OutputString(stream, node->GetContent(), convMem, convFile);
+            OutputString(stream, wxT("-->"), NULL, NULL);
+            break;
+
+        default:
+            wxFAIL_MSG(wxT("unsupported node type"));
+    }
+}
+
+bool wxXmlDocument::Save(wxOutputStream& stream) const
+{
+    if ( !IsOk() )
+        return FALSE;
+
+    wxString s;
+
+    wxMBConv *convMem = NULL, *convFile = NULL;
+#if wxUSE_UNICODE
+    convFile = new wxCSConv(GetFileEncoding());
+#else
+    if ( GetFileEncoding() != GetEncoding() )
+    {
+        convFile = new wxCSConv(GetFileEncoding());
+        convMem = new wxCSConv(GetEncoding());
+    }
+#endif
+
+    s.Printf(wxT("<?xml version=\"%s\" encoding=\"%s\"?>\n"),
+             GetVersion().c_str(), GetFileEncoding().c_str());
+    OutputString(stream, s, NULL, NULL);
+
+    OutputNode(stream, GetRoot(), 0, convMem, convFile);
+    OutputString(stream, wxT("\n"), NULL, NULL);
+
+    if ( convFile )
+        delete convFile;
+    if ( convMem )
+        delete convMem;
+
+    return TRUE;
+}
+
+#endif // wxUSE_XML