#include "wx/zstream.h"
#include "wx/log.h"
#include "wx/intl.h"
+#include "wx/strconv.h"
#include "wx/xrc/xml.h"
-#include "wx/xrc/xmlio.h"
+#include "xmlparse.h" // from Expat
+//-----------------------------------------------------------------------------
+// wxXmlNode
+//-----------------------------------------------------------------------------
wxXmlNode::wxXmlNode(wxXmlNode *parent,wxXmlNodeType type,
const wxString& name, const wxString& content,
}
}
-
-
wxXmlNode::wxXmlNode(wxXmlNodeType type, const wxString& name,
const wxString& content)
: m_type(type), m_name(name), m_content(content),
m_children(NULL), m_next(NULL)
{}
-
-
wxXmlNode::wxXmlNode(const wxXmlNode& node)
{
m_next = NULL;
DoCopy(node);
}
-
-
wxXmlNode::~wxXmlNode()
{
wxXmlNode *c, *c2;
}
}
-
-
wxXmlNode& wxXmlNode::operator=(const wxXmlNode& node)
{
- delete m_properties;
- delete m_children;
+ wxDELETE(m_properties);
+ wxDELETE(m_children);
DoCopy(node);
return *this;
}
-
-
void wxXmlNode::DoCopy(const wxXmlNode& node)
{
m_type = node.m_type;
}
}
-
bool wxXmlNode::HasProp(const wxString& propName) const
{
wxXmlProperty *prop = GetProperties();
return FALSE;
}
-
-
bool wxXmlNode::GetPropVal(const wxString& propName, wxString *value) const
{
wxXmlProperty *prop = GetProperties();
return FALSE;
}
-
-
wxString wxXmlNode::GetPropVal(const wxString& propName, const wxString& defaultVal) const
{
wxString tmp;
return defaultVal;
}
-
-
void wxXmlNode::AddChild(wxXmlNode *child)
{
if (m_children == 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"));
child->m_next = before_node;
}
-
-
bool wxXmlNode::RemoveChild(wxXmlNode *child)
{
if (m_children == NULL)
}
}
-
-
void wxXmlNode::AddProperty(const wxString& name, const wxString& value)
{
AddProperty(new wxXmlProperty(name, value, NULL));
}
}
-
-
bool wxXmlNode::DeleteProperty(const wxString& name)
{
+ wxXmlProperty *prop;
+
if (m_properties == NULL)
return FALSE;
else if (m_properties->GetName() == name)
{
- wxXmlProperty *prop = m_properties;
+ prop = m_properties;
m_properties = prop->GetNext();
prop->SetNext(NULL);
delete prop;
{
if (p->GetNext()->GetName() == name)
{
- wxXmlProperty *prop = p->GetNext();
+ prop = p->GetNext();
p->SetNext(prop->GetNext());
prop->SetNext(NULL);
delete prop;
+//-----------------------------------------------------------------------------
+// wxXmlDocument
+//-----------------------------------------------------------------------------
-
-
-
-
-wxList *wxXmlDocument::sm_handlers = NULL;
-
-
-
-wxXmlDocument::wxXmlDocument(const wxString& filename, wxXmlIOType io_type)
+wxXmlDocument::wxXmlDocument(const wxString& filename, const wxString& encoding)
: wxObject(), m_root(NULL)
{
- if (!Load(filename, io_type))
+ if ( !Load(filename, encoding) )
{
- delete m_root;
- m_root = NULL;
+ wxDELETE(m_root);
}
}
-
-
-wxXmlDocument::wxXmlDocument(wxInputStream& stream, wxXmlIOType io_type)
+wxXmlDocument::wxXmlDocument(wxInputStream& stream, const wxString& encoding)
: wxObject(), m_root(NULL)
{
- if (!Load(stream, io_type))
+ if ( !Load(stream, encoding) )
{
- delete m_root;
- m_root = NULL;
+ wxDELETE(m_root);
}
}
-
-
wxXmlDocument::wxXmlDocument(const wxXmlDocument& doc)
{
DoCopy(doc);
}
-
-
wxXmlDocument& wxXmlDocument::operator=(const wxXmlDocument& doc)
{
- delete m_root;
+ 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, wxXmlIOType io_type)
+bool wxXmlDocument::Load(const wxString& filename, const wxString& encoding)
{
wxFileInputStream stream(filename);
- return Load(stream, io_type);
+ return Load(stream, encoding);
+}
+
+bool wxXmlDocument::Save(const wxString& filename) const
+{
+ wxFileOutputStream stream(filename);
+ return Save(stream);
}
-bool wxXmlDocument::Load(wxInputStream& stream, wxXmlIOType io_type)
+//-----------------------------------------------------------------------------
+// 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)
{
- wxNode *n = sm_handlers->GetFirst();
- while (n)
+#if wxUSE_UNICODE
+ (void)conv;
+ return wxString(s, wxConvUTF8, len);
+#else
+ if ( conv )
{
- wxXmlIOHandler *h = (wxXmlIOHandler*) n->GetData();
-
- if ((io_type == wxXML_IO_AUTO || io_type == h->GetType()) &&
- h->CanLoad(stream))
- {
- return h->Load(stream, *this);
- }
- n = n->GetNext();
+ 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;
+ delete[] buf;
+ return wxString(buf, *conv, len);
}
- wxLogError(_("Cannot find XML I/O handler capable of loading this format."));
- return FALSE;
+ else
+ return wxString(s, len);
+#endif
}
+struct wxXmlParsingContext
+{
+ wxMBConv *conv;
+ wxXmlNode *root;
+ wxXmlNode *node;
+ wxXmlNode *lastAsText;
+ wxString encoding;
+ wxString version;
+};
-
-bool wxXmlDocument::Save(const wxString& filename, wxXmlIOType io_type) const
+static void StartElementHnd(void *userData, const char *name, const char **atts)
{
- wxFileOutputStream stream(filename);
- return Save(stream, io_type);
+ 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;
+}
-bool wxXmlDocument::Save(wxOutputStream& stream, wxXmlIOType io_type) const
+static void TextHnd(void *userData, const char *s, int len)
{
- wxNode *n = sm_handlers->GetFirst();
- while (n)
+ 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
{
- wxXmlIOHandler *h = (wxXmlIOHandler*) n->GetData();
- if (io_type == h->GetType() && h->CanSave())
+ bool whiteOnly = TRUE;
+ for (char *c = buf; *c != '\0'; c++)
+ if (*c != ' ' && *c != '\t' && *c != '\n' && *c != '\r')
+ {
+ whiteOnly = FALSE;
+ break;
+ }
+ if (!whiteOnly)
{
- return h->Save(stream, *this);
+ ctx->lastAsText = new wxXmlNode(wxXML_TEXT_NODE, wxT("text"),
+ CharToString(ctx->conv, buf));
+ ctx->node->AddChild(ctx->lastAsText);
}
- n = n->GetNext();
}
- wxLogError(_("Cannot find XML I/O handler capable of saving in this format."));
- return FALSE;
-}
-
-
+ 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;
+}
-void wxXmlDocument::AddHandler(wxXmlIOHandler *handler)
+static void DefaultHnd(void *userData, const char *s, int len)
{
- if (sm_handlers == NULL)
+ // XML header:
+ if (len > 6 && memcmp(s, "<?xml ", 6) == 0)
{
- sm_handlers = new wxList;
- sm_handlers->DeleteContents(TRUE);
+ 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]);
}
- sm_handlers->Append(handler);
}
+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[255];
+ wchar_t wcBuf[255];
+ size_t i;
+
+ for (i = 0; i < 255; i++)
+ mbBuf[i] = (char) (i+1);
+ mbBuf[255] = 0;
+ conv.MB2WC(wcBuf, mbBuf, 255);
+ wcBuf[255] = 0;
+
+ info->map[0] = 0;
+ for (i = 0; i < 255; i++)
+ info->map[i+1] = (int)wcBuf[i];
+
+ info->data = NULL;
+ info->convert = NULL;
+ info->release = NULL;
+
+ return 1;
+}
-void wxXmlDocument::CleanUpHandlers()
+bool wxXmlDocument::Load(wxInputStream& stream, const wxString& encoding)
{
- delete sm_handlers;
- sm_handlers = NULL;
+#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);
+
+ 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));
+ return FALSE;
+ }
+ } while (!done);
+
+ SetVersion(ctx.version);
+ SetFileEncoding(ctx.encoding);
+ SetRoot(ctx.root);
+
+ XML_ParserFree(parser);
+#if !wxUSE_UNICODE
+ if ( ctx.conv )
+ delete ctx.conv;
+#endif
+
+ return TRUE;
+
}
-void wxXmlDocument::InitStandardHandlers()
+
+//-----------------------------------------------------------------------------
+// wxXmlDocument saving routines
+//-----------------------------------------------------------------------------
+
+// write string to output:
+inline static void OutputString(wxOutputStream& stream, const wxString& str,
+ wxMBConv *convMem, wxMBConv *convFile)
{
- AddHandler(new wxXmlIOHandlerBin);
-#if wxUSE_ZLIB
- AddHandler(new wxXmlIOHandlerBinZ);
+ 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
- AddHandler(new wxXmlIOHandlerExpat);
- AddHandler(new wxXmlIOHandlerWriter);
}
+// 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;
-#include "wx/module.h"
+ 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);
+}
-class wxXmlModule: public wxModule
+inline static void OutputIndentation(wxOutputStream& stream, int indent)
{
- DECLARE_DYNAMIC_CLASS(wxXmlModule)
- public:
- wxXmlModule() {}
- bool OnInit() { wxXmlDocument::InitStandardHandlers(); return TRUE; };
- void OnExit() { wxXmlDocument::CleanUpHandlers(); };
-};
+ wxString str = wxT("\n");
+ for (int i = 0; i < indent; i++)
+ str << wxT(' ') << wxT(' ');
+ OutputString(stream, str, NULL, NULL);
+}
-IMPLEMENT_DYNAMIC_CLASS(wxXmlModule, wxModule)
+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();
+ }
-// When wxXml is loaded dynamically after the application is already running
-// then the built-in module system won't pick this one up. Add it manually.
-void wxXmlInitXmlModule()
-{
- wxModule* module = new wxXmlModule;
- module->Init();
- wxModule::RegisterModule(module);
+ 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;
+}