From 27b0c286444b5f2404f29fb2bc233bfcfaaa6bba Mon Sep 17 00:00:00 2001 From: =?utf8?q?V=C3=A1clav=20Slav=C3=ADk?= Date: Sun, 20 Jul 2003 21:37:03 +0000 Subject: [PATCH] moved XML classes to the core git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@22172 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- include/wx/chkconf.h | 8 + include/wx/defs.h | 9 + include/wx/msw/setup0.h | 8 + include/wx/xml/xml.h | 242 +++++++++++++ src/xml/xml.cpp | 730 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 997 insertions(+) create mode 100644 include/wx/xml/xml.h create mode 100644 src/xml/xml.cpp diff --git a/include/wx/chkconf.h b/include/wx/chkconf.h index 0b48bd7559..bd489fd7d6 100644 --- a/include/wx/chkconf.h +++ b/include/wx/chkconf.h @@ -155,6 +155,14 @@ # 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." diff --git a/include/wx/defs.h b/include/wx/defs.h index d9ad71021d..3f49f8256c 100644 --- a/include/wx/defs.h +++ b/include/wx/defs.h @@ -376,6 +376,7 @@ typedef int wxWindowID; #define WXMAKINGDLL_CORE #define WXMAKINGDLL_HTML + #define WXMAKINGDLL_XML #endif // WXMAKINGDLL // WXDLLEXPORT maps to export declaration when building the DLL, to import @@ -421,6 +422,14 @@ typedef int wxWindowID; #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 diff --git a/include/wx/msw/setup0.h b/include/wx/msw/setup0.h index 860e7d94ab..0e32f556a4 100644 --- a/include/wx/msw/setup0.h +++ b/include/wx/msw/setup0.h @@ -441,6 +441,14 @@ // 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 // ---------------------------------------------------------------------------- diff --git a/include/wx/xml/xml.h b/include/wx/xml/xml.h new file mode 100644 index 0000000000..a288989d2e --- /dev/null +++ b/include/wx/xml/xml.h @@ -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 "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 hi 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 index 0000000000..3b59fcf271 --- /dev/null +++ b/src/xml/xml.cpp @@ -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, "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 "<", '>' to ">" and '&' to "&" +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("<"), NULL, NULL); + break; + case wxT('>'): + OutputString(stream, wxT(">"), NULL, NULL); + break; + case wxT('&'): + OutputString(stream, wxT("&"), 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("GetName(), NULL, NULL); + OutputString(stream, wxT(">"), NULL, NULL); + } + else + OutputString(stream, wxT("/>"), NULL, NULL); + break; + + case wxXML_COMMENT_NODE: + 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("\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 -- 2.45.2