]> git.saurik.com Git - wxWidgets.git/blobdiff - src/xml/xml.cpp
Halfway reasonable implementation of wxFont for wxCocoa.
[wxWidgets.git] / src / xml / xml.cpp
index 28b83e829b4afc33432ce7d17f0b2b756ef9d4b7..d8fe265936e9e94e44891b8ac8795d764d7da58f 100644 (file)
@@ -22,6 +22,7 @@
 #ifndef WX_PRECOMP
     #include "wx/intl.h"
     #include "wx/log.h"
+    #include "wx/app.h"
 #endif
 
 #include "wx/wfstream.h"
 #include "expat.h" // from Expat
 
 // DLL options compatibility check:
-#include "wx/app.h"
 WX_CHECK_BUILD_OPTIONS("wxXML")
 
 
 IMPLEMENT_CLASS(wxXmlDocument, wxObject)
 
 
+// a private utility used by wxXML
+static bool wxIsWhiteOnly(const wxString& buf);
+
 
 //-----------------------------------------------------------------------------
 //  wxXmlNode
@@ -46,10 +49,11 @@ IMPLEMENT_CLASS(wxXmlDocument, wxObject)
 
 wxXmlNode::wxXmlNode(wxXmlNode *parent,wxXmlNodeType type,
                      const wxString& name, const wxString& content,
-                     wxXmlProperty *props, wxXmlNode *next)
+                     wxXmlAttribute *attrs, wxXmlNode *next, int lineNo)
     : m_type(type), m_name(name), m_content(content),
-      m_properties(props), m_parent(parent),
-      m_children(NULL), m_next(next)
+      m_attrs(attrs), m_parent(parent),
+      m_children(NULL), m_next(next),
+      m_lineNo(lineNo)
 {
     if (m_parent)
     {
@@ -64,10 +68,12 @@ wxXmlNode::wxXmlNode(wxXmlNode *parent,wxXmlNodeType type,
 }
 
 wxXmlNode::wxXmlNode(wxXmlNodeType type, const wxString& name,
-                     const wxString& content)
+                     const wxString& content,
+                     int lineNo)
     : m_type(type), m_name(name), m_content(content),
-      m_properties(NULL), m_parent(NULL),
-      m_children(NULL), m_next(NULL)
+      m_attrs(NULL), m_parent(NULL),
+      m_children(NULL), m_next(NULL),
+      m_lineNo(lineNo)
 {}
 
 wxXmlNode::wxXmlNode(const wxXmlNode& node)
@@ -86,8 +92,8 @@ wxXmlNode::~wxXmlNode()
         delete c;
     }
 
-    wxXmlProperty *p, *p2;
-    for (p = m_properties; p; p = p2)
+    wxXmlAttribute *p, *p2;
+    for (p = m_attrs; p; p = p2)
     {
         p2 = p->GetNext();
         delete p;
@@ -96,7 +102,7 @@ wxXmlNode::~wxXmlNode()
 
 wxXmlNode& wxXmlNode::operator=(const wxXmlNode& node)
 {
-    wxDELETE(m_properties);
+    wxDELETE(m_attrs);
     wxDELETE(m_children);
     DoCopy(node);
     return *this;
@@ -107,6 +113,7 @@ void wxXmlNode::DoCopy(const wxXmlNode& node)
     m_type = node.m_type;
     m_name = node.m_name;
     m_content = node.m_content;
+    m_lineNo = node.m_lineNo;
     m_children = NULL;
 
     wxXmlNode *n = node.m_children;
@@ -116,49 +123,51 @@ void wxXmlNode::DoCopy(const wxXmlNode& node)
         n = n->GetNext();
     }
 
-    m_properties = NULL;
-    wxXmlProperty *p = node.m_properties;
+    m_attrs = NULL;
+    wxXmlAttribute *p = node.m_attrs;
     while (p)
     {
-       AddProperty(p->GetName(), p->GetValue());
+       AddAttribute(p->GetName(), p->GetValue());
        p = p->GetNext();
     }
 }
 
-bool wxXmlNode::HasProp(const wxString& propName) const
+bool wxXmlNode::HasAttribute(const wxString& attrName) const
 {
-    wxXmlProperty *prop = GetProperties();
+    wxXmlAttribute *attr = GetAttributes();
 
-    while (prop)
+    while (attr)
     {
-        if (prop->GetName() == propName) return true;
-        prop = prop->GetNext();
+        if (attr->GetName() == attrName) return true;
+        attr = attr->GetNext();
     }
 
     return false;
 }
 
-bool wxXmlNode::GetPropVal(const wxString& propName, wxString *value) const
+bool wxXmlNode::GetAttribute(const wxString& attrName, wxString *value) const
 {
-    wxXmlProperty *prop = GetProperties();
+    wxCHECK_MSG( value, false, "value argument must not be NULL" );
+
+    wxXmlAttribute *attr = GetAttributes();
 
-    while (prop)
+    while (attr)
     {
-        if (prop->GetName() == propName)
+        if (attr->GetName() == attrName)
         {
-            *value = prop->GetValue();
+            *value = attr->GetValue();
             return true;
         }
-        prop = prop->GetNext();
+        attr = attr->GetNext();
     }
 
     return false;
 }
 
-wxString wxXmlNode::GetPropVal(const wxString& propName, const wxString& defaultVal) const
+wxString wxXmlNode::GetAttribute(const wxString& attrName, const wxString& defaultVal) const
 {
     wxString tmp;
-    if (GetPropVal(propName, &tmp))
+    if (GetAttribute(attrName, &tmp))
         return tmp;
 
     return defaultVal;
@@ -178,21 +187,40 @@ void wxXmlNode::AddChild(wxXmlNode *child)
     child->m_parent = this;
 }
 
-void wxXmlNode::InsertChild(wxXmlNode *child, wxXmlNode *before_node)
+bool wxXmlNode::InsertChild(wxXmlNode *child, wxXmlNode *before_node)
 {
-    wxASSERT_MSG(before_node->GetParent() == this, wxT("wxXmlNode::InsertChild - the node has incorrect parent"));
+    wxCHECK_MSG(before_node == NULL || before_node->GetParent() == this, false,
+                 wxT("wxXmlNode::InsertChild - the node has incorrect parent"));
+    wxCHECK_MSG(child, false, wxT("Cannot insert a NULL pointer!"));
 
     if (m_children == before_node)
        m_children = child;
+    else if (m_children == NULL)
+    {
+        if (before_node != NULL)
+            return false;       // we have no children so we don't need to search
+        m_children = child;
+    }
+    else if (before_node == NULL)
+    {
+        // prepend child
+        child->m_parent = this;
+        child->m_next = m_children;
+        m_children = child;
+        return true;
+    }
     else
     {
         wxXmlNode *ch = m_children;
-        while (ch->m_next != before_node) ch = ch->m_next;
+        while (ch && ch->m_next != before_node) ch = ch->m_next;
+        if (!ch)
+            return false;       // before_node not found
         ch->m_next = child;
     }
 
     child->m_parent = this;
     child->m_next = before_node;
+    return true;
 }
 
 bool wxXmlNode::RemoveChild(wxXmlNode *child)
@@ -226,48 +254,48 @@ bool wxXmlNode::RemoveChild(wxXmlNode *child)
 
 void wxXmlNode::AddProperty(const wxString& name, const wxString& value)
 {
-    AddProperty(new wxXmlProperty(name, value, NULL));
+    AddProperty(new wxXmlAttribute(name, value, NULL));
 }
 
-void wxXmlNode::AddProperty(wxXmlProperty *prop)
+void wxXmlNode::AddProperty(wxXmlAttribute *attr)
 {
-    if (m_properties == NULL)
-        m_properties = prop;
+    if (m_attrs == NULL)
+        m_attrs = attr;
     else
     {
-        wxXmlProperty *p = m_properties;
+        wxXmlAttribute *p = m_attrs;
         while (p->GetNext()) p = p->GetNext();
-        p->SetNext(prop);
+        p->SetNext(attr);
     }
 }
 
 bool wxXmlNode::DeleteProperty(const wxString& name)
 {
-    wxXmlProperty *prop;
+    wxXmlAttribute *attr;
 
-    if (m_properties == NULL)
+    if (m_attrs == NULL)
         return false;
 
-    else if (m_properties->GetName() == name)
+    else if (m_attrs->GetName() == name)
     {
-        prop = m_properties;
-        m_properties = prop->GetNext();
-        prop->SetNext(NULL);
-        delete prop;
+        attr = m_attrs;
+        m_attrs = attr->GetNext();
+        attr->SetNext(NULL);
+        delete attr;
         return true;
     }
 
     else
     {
-        wxXmlProperty *p = m_properties;
+        wxXmlAttribute *p = m_attrs;
         while (p->GetNext())
         {
             if (p->GetNext()->GetName() == name)
             {
-                prop = p->GetNext();
-                p->SetNext(prop->GetNext());
-                prop->SetNext(NULL);
-                delete prop;
+                attr = p->GetNext();
+                p->SetNext(attr->GetNext());
+                attr->SetNext(NULL);
+                delete attr;
                 return true;
             }
             p = p->GetNext();
@@ -290,6 +318,28 @@ wxString wxXmlNode::GetNodeContent() const
     return wxEmptyString;
 }
 
+int wxXmlNode::GetDepth(wxXmlNode *grandparent) const
+{
+    const wxXmlNode *n = this;
+    int ret = -1;
+
+    do
+    {
+        ret++;
+        n = n->GetParent();
+        if (n == grandparent)
+            return ret;
+
+    } while (n);
+
+    return wxNOT_FOUND;
+}
+
+bool wxXmlNode::IsWhitespaceOnly() const
+{
+    return wxIsWhiteOnly(m_content);
+}
+
 
 
 //-----------------------------------------------------------------------------
@@ -342,19 +392,27 @@ void wxXmlDocument::DoCopy(const wxXmlDocument& doc)
     m_encoding = doc.m_encoding;
 #endif
     m_fileEncoding = doc.m_fileEncoding;
-    m_root = new wxXmlNode(*doc.m_root);
+
+    if (doc.m_root)
+        m_root = new wxXmlNode(*doc.m_root);
+    else
+        m_root = NULL;
 }
 
-bool wxXmlDocument::Load(const wxString& filename, const wxString& encoding)
+bool wxXmlDocument::Load(const wxString& filename, const wxString& encoding, int flags)
 {
     wxFileInputStream stream(filename);
-    return Load(stream, encoding);
+    if (!stream.Ok())
+        return false;
+    return Load(stream, encoding, flags);
 }
 
-bool wxXmlDocument::Save(const wxString& filename) const
+bool wxXmlDocument::Save(const wxString& filename, int indentstep) const
 {
     wxFileOutputStream stream(filename);
-    return Save(stream);
+    if (!stream.Ok())
+        return false;
+    return Save(stream, indentstep);
 }
 
 
@@ -363,56 +421,66 @@ bool wxXmlDocument::Save(const wxString& filename) const
 //  wxXmlDocument loading routines
 //-----------------------------------------------------------------------------
 
-/*
-    FIXME:
-       - process all elements, including CDATA
- */
-
 // converts Expat-produced string in UTF-8 into wxString using the specified
 // conv or keep in UTF-8 if conv is NULL
 static wxString CharToString(wxMBConv *conv,
-                                    const char *s, size_t len = wxSTRING_MAXLEN)
+                             const char *s, size_t len = wxString::npos)
 {
-#if wxUSE_UNICODE
-    wxUnusedVar(conv);
-
-    return wxString(s, wxConvUTF8, len);
-#else // !wxUSE_UNICODE
+#if !wxUSE_UNICODE
     if ( conv )
     {
         // there can be no embedded NULs in this string so we don't need the
         // output length, it will be NUL-terminated
         const wxWCharBuffer wbuf(
-            wxConvUTF8.cMB2WC(s, len == wxSTRING_MAXLEN ? wxNO_LEN : len, NULL));
+            wxConvUTF8.cMB2WC(s, len == wxString::npos ? wxNO_LEN : len, NULL));
 
-        return wxString(wbuf, conv);
+        return wxString(wbuf, *conv);
     }
-    else // already in UTF-8, no conversion needed
+    // else: the string is wanted in UTF-8
+#endif // !wxUSE_UNICODE
+
+    wxUnusedVar(conv);
+    return wxString::FromUTF8(s, len);
+}
+
+// returns true if the given string contains only whitespaces
+bool wxIsWhiteOnly(const wxString& buf)
+{
+    for ( wxString::const_iterator i = buf.begin(); i != buf.end(); ++i )
     {
-        return wxString(s, len != wxSTRING_MAXLEN ? len : strlen(s));
+        wxChar c = *i;
+        if ( c != wxT(' ') && c != wxT('\t') && c != wxT('\n') && c != wxT('\r'))
+            return false;
     }
-#endif // wxUSE_UNICODE/!wxUSE_UNICODE
+    return true;
 }
 
+
 struct wxXmlParsingContext
 {
+    XML_Parser parser;
     wxMBConv  *conv;
     wxXmlNode *root;
     wxXmlNode *node;
     wxXmlNode *lastAsText;
     wxString   encoding;
     wxString   version;
+    bool       removeWhiteOnlyNodes;
 };
 
 extern "C" {
 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));
+    wxXmlNode *node = new wxXmlNode(wxXML_ELEMENT_NODE,
+                                    CharToString(ctx->conv, name),
+                                    wxEmptyString,
+                                    XML_GetCurrentLineNumber(ctx->parser));
     const char **a = atts;
+
     while (*a)
     {
-        node->AddProperty(CharToString(ctx->conv, a[0]), CharToString(ctx->conv, a[1]));
+        node->AddAttribute(CharToString(ctx->conv, a[0]), CharToString(ctx->conv, a[1]));
         a += 2;
     }
     if (ctx->root == NULL)
@@ -422,9 +490,7 @@ static void StartElementHnd(void *userData, const char *name, const char **atts)
     ctx->node = node;
     ctx->lastAsText = NULL;
 }
-}
 
-extern "C" {
 static void EndElementHnd(void *userData, const char* WXUNUSED(name))
 {
     wxXmlParsingContext *ctx = (wxXmlParsingContext*)userData;
@@ -432,44 +498,42 @@ static void EndElementHnd(void *userData, const char* WXUNUSED(name))
     ctx->node = ctx->node->GetParent();
     ctx->lastAsText = NULL;
 }
-}
 
-extern "C" {
 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);
+    wxString str = CharToString(ctx->conv, s, len);
 
     if (ctx->lastAsText)
     {
-        ctx->lastAsText->SetContent(ctx->lastAsText->GetContent() +
-                                    CharToString(ctx->conv, buf));
+        ctx->lastAsText->SetContent(ctx->lastAsText->GetContent() + str);
     }
     else
     {
-        bool whiteOnly = true;
-        for (char *c = buf; *c != '\0'; c++)
-            if (*c != ' ' && *c != '\t' && *c != '\n' && *c != '\r')
-            {
-                whiteOnly = false;
-                break;
-            }
+        bool whiteOnly = false;
+        if (ctx->removeWhiteOnlyNodes)
+            whiteOnly = wxIsWhiteOnly(str);
+
         if (!whiteOnly)
         {
-            ctx->lastAsText = new wxXmlNode(wxXML_TEXT_NODE, wxT("text"),
-                                            CharToString(ctx->conv, buf));
+            ctx->lastAsText =
+                new wxXmlNode(wxXML_TEXT_NODE, wxT("text"), str,
+                              XML_GetCurrentLineNumber(ctx->parser));
             ctx->node->AddChild(ctx->lastAsText);
         }
     }
-
-    delete[] buf;
 }
+
+static void StartCdataHnd(void *userData)
+{
+    wxXmlParsingContext *ctx = (wxXmlParsingContext*)userData;
+
+    ctx->lastAsText =
+        new wxXmlNode(wxXML_CDATA_SECTION_NODE, wxT("cdata"), wxT(""),
+                      XML_GetCurrentLineNumber(ctx->parser));
+    ctx->node->AddChild(ctx->lastAsText);
 }
 
-extern "C" {
 static void CommentHnd(void *userData, const char *data)
 {
     wxXmlParsingContext *ctx = (wxXmlParsingContext*)userData;
@@ -479,14 +543,14 @@ static void CommentHnd(void *userData, const char *data)
         // 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->node->AddChild(
+            new wxXmlNode(wxXML_COMMENT_NODE,
+                          wxT("comment"), CharToString(ctx->conv, data),
+                          XML_GetCurrentLineNumber(ctx->parser)));
     }
     ctx->lastAsText = NULL;
 }
-}
 
-extern "C" {
 static void DefaultHnd(void *userData, const char *s, int len)
 {
     // XML header:
@@ -504,17 +568,14 @@ static void DefaultHnd(void *userData, const char *s, int len)
             ctx->version = buf.Mid(pos + 9).BeforeFirst(buf[(size_t)pos+8]);
     }
 }
-}
 
-extern "C" {
 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:
-    wxString str(name, wxConvLibc);
-    wxCSConv conv(str);
+    wxCSConv conv(name);
     char mbBuf[2];
     wchar_t wcBuf[10];
     size_t i;
@@ -538,9 +599,10 @@ static int UnknownEncodingHnd(void * WXUNUSED(encodingHandlerData),
 
     return 1;
 }
-}
 
-bool wxXmlDocument::Load(wxInputStream& stream, const wxString& encoding)
+} // extern "C"
+
+bool wxXmlDocument::Load(wxInputStream& stream, const wxString& encoding, int flags)
 {
 #if wxUSE_UNICODE
     (void)encoding;
@@ -558,13 +620,16 @@ bool wxXmlDocument::Load(wxInputStream& stream, const wxString& encoding)
     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") )
+    if ( encoding.CmpNoCase(wxT("UTF-8")) != 0 )
         ctx.conv = new wxCSConv(encoding);
 #endif
+    ctx.removeWhiteOnlyNodes = (flags & wxXMLDOC_KEEP_WHITESPACE_NODES) == 0;
+    ctx.parser = parser;
 
     XML_SetUserData(parser, (void*)&ctx);
     XML_SetElementHandler(parser, StartElementHnd, EndElementHnd);
     XML_SetCharacterDataHandler(parser, TextHnd);
+    XML_SetStartCdataSectionHandler(parser, StartCdataHnd);
     XML_SetCommentHandler(parser, CommentHnd);
     XML_SetDefaultHandler(parser, DefaultHnd);
     XML_SetUnknownEncodingHandler(parser, UnknownEncodingHnd, NULL);
@@ -700,13 +765,19 @@ inline static void OutputIndentation(wxOutputStream& stream, int indent)
 }
 
 static void OutputNode(wxOutputStream& stream, wxXmlNode *node, int indent,
-                       wxMBConv *convMem, wxMBConv *convFile)
+                       wxMBConv *convMem, wxMBConv *convFile, int indentstep)
 {
     wxXmlNode *n, *prev;
-    wxXmlProperty *prop;
+    wxXmlAttribute *attr;
 
     switch (node->GetType())
     {
+        case wxXML_CDATA_SECTION_NODE:
+            OutputString( stream, wxT("<![CDATA["));
+            OutputString( stream, node->GetContent() );
+            OutputString( stream, wxT("]]>") );
+            break;
+
         case wxXML_TEXT_NODE:
             OutputStringEnt(stream, node->GetContent(), convMem, convFile);
             break;
@@ -715,14 +786,14 @@ static void OutputNode(wxOutputStream& stream, wxXmlNode *node, int indent,
             OutputString(stream, wxT("<"));
             OutputString(stream, node->GetName());
 
-            prop = node->GetProperties();
-            while (prop)
+            attr = node->GetAttributes();
+            while (attr)
             {
-                OutputString(stream, wxT(" ") + prop->GetName() +  wxT("=\""));
-                OutputStringEnt(stream, prop->GetValue(), convMem, convFile,
+                OutputString(stream, wxT(" ") + attr->GetName() +  wxT("=\""));
+                OutputStringEnt(stream, attr->GetValue(), convMem, convFile,
                                 XML_ESCAPE_QUOTES);
                 OutputString(stream, wxT("\""));
-                prop = prop->GetNext();
+                attr = attr->GetNext();
             }
 
             if (node->GetChildren())
@@ -732,13 +803,13 @@ static void OutputNode(wxOutputStream& stream, wxXmlNode *node, int indent,
                 n = node->GetChildren();
                 while (n)
                 {
-                    if (n && n->GetType() != wxXML_TEXT_NODE)
-                        OutputIndentation(stream, indent + 1);
-                    OutputNode(stream, n, indent + 1, convMem, convFile);
+                    if (indentstep >= 0 && n && n->GetType() != wxXML_TEXT_NODE)
+                        OutputIndentation(stream, indent + indentstep);
+                    OutputNode(stream, n, indent + indentstep, convMem, convFile, indentstep);
                     prev = n;
                     n = n->GetNext();
                 }
-                if (prev && prev->GetType() != wxXML_TEXT_NODE)
+                if (indentstep >= 0 && prev && prev->GetType() != wxXML_TEXT_NODE)
                     OutputIndentation(stream, indent);
                 OutputString(stream, wxT("</"));
                 OutputString(stream, node->GetName());
@@ -759,37 +830,41 @@ static void OutputNode(wxOutputStream& stream, wxXmlNode *node, int indent,
     }
 }
 
-bool wxXmlDocument::Save(wxOutputStream& stream) const
+bool wxXmlDocument::Save(wxOutputStream& stream, int indentstep) const
 {
     if ( !IsOk() )
         return false;
 
     wxString s;
 
-    wxMBConv *convMem = NULL;
+    wxMBConv *convMem = NULL,
+             *convFile;
 
 #if wxUSE_UNICODE
-    wxMBConv *convFile = new wxCSConv(GetFileEncoding());
+    convFile = new wxCSConv(GetFileEncoding());
+    convMem = NULL;
 #else
-    wxMBConv *convFile = NULL;
-    if ( GetFileEncoding() != GetEncoding() )
+    if ( GetFileEncoding().CmpNoCase(GetEncoding()) != 0 )
     {
         convFile = new wxCSConv(GetFileEncoding());
         convMem = new wxCSConv(GetEncoding());
     }
+    else // file and in-memory encodings are the same, no conversion needed
+    {
+        convFile =
+        convMem = NULL;
+    }
 #endif
 
     s.Printf(wxT("<?xml version=\"%s\" encoding=\"%s\"?>\n"),
              GetVersion().c_str(), GetFileEncoding().c_str());
     OutputString(stream, s);
 
-    OutputNode(stream, GetRoot(), 0, convMem, convFile);
+    OutputNode(stream, GetRoot(), 0, convMem, convFile, indentstep);
     OutputString(stream, wxT("\n"));
 
-    if ( convFile )
-        delete convFile;
-    if ( convMem )
-        delete convMem;
+    delete convFile;
+    delete convMem;
 
     return true;
 }