]> git.saurik.com Git - wxWidgets.git/blobdiff - src/xml/xml.cpp
fix other insert() overloads to work when inserting at the end of the list, continuat...
[wxWidgets.git] / src / xml / xml.cpp
index 37467fc122571b85ec012d75c549a0f7ad143c2f..0f5e61d83cc943082540b8aeb504f6fd88a618f3 100644 (file)
@@ -29,6 +29,7 @@
 #include "wx/datstrm.h"
 #include "wx/zstream.h"
 #include "wx/strconv.h"
+#include "wx/scopedptr.h"
 
 #include "expat.h" // from Expat
 
@@ -226,6 +227,33 @@ bool wxXmlNode::InsertChild(wxXmlNode *child, wxXmlNode *followingNode)
     return true;
 }
 
+// inserts a new node right after 'precedingNode'
+bool wxXmlNode::InsertChildAfter(wxXmlNode *child, wxXmlNode *precedingNode)
+{
+    wxCHECK_MSG( child, false, "cannot insert a NULL node!" );
+    wxCHECK_MSG( child->m_parent == NULL, false, "node already has a parent" );
+    wxCHECK_MSG( child->m_next == NULL, false, "node already has m_next" );
+    wxCHECK_MSG( precedingNode == NULL || precedingNode->m_parent == this, false,
+                 "precedingNode has wrong parent" );
+
+    if ( precedingNode )
+    {
+        child->m_next = precedingNode->m_next;
+        precedingNode->m_next = child;
+    }
+    else // precedingNode == NULL
+    {
+        wxCHECK_MSG( m_children == NULL, false,
+                     "NULL precedingNode only makes sense when there are no children" );
+
+        child->m_next = m_children;
+        m_children = child;
+    }
+
+    child->m_parent = this;
+    return true;
+}
+
 bool wxXmlNode::RemoveChild(wxXmlNode *child)
 {
     if (m_children == NULL)
@@ -365,10 +393,10 @@ bool wxXmlNode::IsWhitespaceOnly() const
 //-----------------------------------------------------------------------------
 
 wxXmlDocument::wxXmlDocument()
-    : m_version(wxT("1.0")), m_fileEncoding(wxT("utf-8")), m_root(NULL)
+    : m_version(wxS("1.0")), m_fileEncoding(wxS("utf-8")), m_root(NULL)
 {
 #if !wxUSE_UNICODE
-    m_encoding = wxT("UTF-8");
+    m_encoding = wxS("UTF-8");
 #endif
 }
 
@@ -458,7 +486,7 @@ static wxString CharToString(wxMBConv *conv,
 #endif // !wxUSE_UNICODE
 
     wxUnusedVar(conv);
-    return wxString::FromUTF8(s, len);
+    return wxString::FromUTF8Unchecked(s, len);
 }
 
 // returns true if the given string contains only whitespaces
@@ -467,7 +495,7 @@ bool wxIsWhiteOnly(const wxString& buf)
     for ( wxString::const_iterator i = buf.begin(); i != buf.end(); ++i )
     {
         wxChar c = *i;
-        if ( c != wxT(' ') && c != wxT('\t') && c != wxT('\n') && c != wxT('\r'))
+        if ( c != wxS(' ') && c != wxS('\t') && c != wxS('\n') && c != wxS('\r'))
             return false;
     }
     return true;
@@ -476,16 +504,33 @@ bool wxIsWhiteOnly(const wxString& buf)
 
 struct wxXmlParsingContext
 {
+    wxXmlParsingContext()
+        : conv(NULL),
+          root(NULL),
+          node(NULL),
+          lastChild(NULL),
+          lastAsText(NULL),
+          removeWhiteOnlyNodes(false)
+    {}
+
     XML_Parser parser;
     wxMBConv  *conv;
     wxXmlNode *root;
-    wxXmlNode *node;
-    wxXmlNode *lastAsText;
+    wxXmlNode *node;                    // the node being parsed
+    wxXmlNode *lastChild;               // the last child of "node"
+    wxXmlNode *lastAsText;              // the last _text_ child of "node"
     wxString   encoding;
     wxString   version;
     bool       removeWhiteOnlyNodes;
 };
 
+// checks that ctx->lastChild is in consistent state
+#define ASSERT_LAST_CHILD_OK(ctx)                                   \
+    wxASSERT( ctx->lastChild == NULL ||                             \
+              ctx->lastChild->GetNext() == NULL );                  \
+    wxASSERT( ctx->lastChild == NULL ||                             \
+              ctx->lastChild->GetParent() == ctx->node )
+
 extern "C" {
 static void StartElementHnd(void *userData, const char *name, const char **atts)
 {
@@ -496,23 +541,38 @@ static void StartElementHnd(void *userData, const char *name, const char **atts)
                                     XML_GetCurrentLineNumber(ctx->parser));
     const char **a = atts;
 
+    // add node attributes
     while (*a)
     {
         node->AddAttribute(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;
+    {
+        ASSERT_LAST_CHILD_OK(ctx);
+        ctx->node->InsertChildAfter(node, ctx->lastChild);
+    }
+
     ctx->lastAsText = NULL;
+    ctx->lastChild = NULL; // our new node "node" has no children yet
+
+    ctx->node = node;
 }
 
 static void EndElementHnd(void *userData, const char* WXUNUSED(name))
 {
     wxXmlParsingContext *ctx = (wxXmlParsingContext*)userData;
 
+    // we're exiting the last children of ctx->node->GetParent() and going
+    // back one level up, so current value of ctx->node points to the last
+    // child of ctx->node->GetParent()
+    ctx->lastChild = ctx->node;
+
     ctx->node = ctx->node->GetParent();
     ctx->lastAsText = NULL;
 }
@@ -534,10 +594,13 @@ static void TextHnd(void *userData, const char *s, int len)
 
         if (!whiteOnly)
         {
-            ctx->lastAsText =
-                new wxXmlNode(wxXML_TEXT_NODE, wxT("text"), str,
+            wxXmlNode *textnode =
+                new wxXmlNode(wxXML_TEXT_NODE, wxS("text"), str,
                               XML_GetCurrentLineNumber(ctx->parser));
-            ctx->node->AddChild(ctx->lastAsText);
+
+            ASSERT_LAST_CHILD_OK(ctx);
+            ctx->node->InsertChildAfter(textnode, ctx->lastChild);
+            ctx->lastChild= ctx->lastAsText = textnode;
         }
     }
 }
@@ -546,10 +609,13 @@ static void StartCdataHnd(void *userData)
 {
     wxXmlParsingContext *ctx = (wxXmlParsingContext*)userData;
 
-    ctx->lastAsText =
-        new wxXmlNode(wxXML_CDATA_SECTION_NODE, wxT("cdata"), wxT(""),
+    wxXmlNode *textnode =
+        new wxXmlNode(wxXML_CDATA_SECTION_NODE, wxS("cdata"), wxS(""),
                       XML_GetCurrentLineNumber(ctx->parser));
-    ctx->node->AddChild(ctx->lastAsText);
+
+    ASSERT_LAST_CHILD_OK(ctx);
+    ctx->node->InsertChildAfter(textnode, ctx->lastChild);
+    ctx->lastChild= ctx->lastAsText = textnode;
 }
 
 static void CommentHnd(void *userData, const char *data)
@@ -558,14 +624,19 @@ static void CommentHnd(void *userData, const char *data)
 
     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(
+        wxXmlNode *commentnode =
             new wxXmlNode(wxXML_COMMENT_NODE,
-                          wxT("comment"), CharToString(ctx->conv, data),
-                          XML_GetCurrentLineNumber(ctx->parser)));
+                          wxS("comment"), CharToString(ctx->conv, data),
+                          XML_GetCurrentLineNumber(ctx->parser));
+
+        ASSERT_LAST_CHILD_OK(ctx);
+        ctx->node->InsertChildAfter(commentnode, ctx->lastChild);
+        ctx->lastChild = commentnode;
     }
+    //else: ctx->node == NULL happens if there is a comment before
+    //      the root element. We current don't have a way to represent
+    //      these in wxXmlDocument (FIXME).
+
     ctx->lastAsText = NULL;
 }
 
@@ -578,10 +649,10 @@ static void DefaultHnd(void *userData, const char *s, int len)
 
         wxString buf = CharToString(ctx->conv, s, (size_t)len);
         int pos;
-        pos = buf.Find(wxT("encoding="));
+        pos = buf.Find(wxS("encoding="));
         if (pos != wxNOT_FOUND)
             ctx->encoding = buf.Mid(pos + 10).BeforeFirst(buf[(size_t)pos+9]);
-        pos = buf.Find(wxT("version="));
+        pos = buf.Find(wxS("version="));
         if (pos != wxNOT_FOUND)
             ctx->version = buf.Mid(pos + 9).BeforeFirst(buf[(size_t)pos+8]);
     }
@@ -634,11 +705,10 @@ bool wxXmlDocument::Load(wxInputStream& stream, const wxString& encoding, int fl
     bool done;
     XML_Parser parser = XML_ParserCreate(NULL);
 
-    ctx.root = ctx.node = NULL;
-    ctx.encoding = wxT("UTF-8"); // default in absence of encoding=""
+    ctx.encoding = wxS("UTF-8"); // default in absence of encoding=""
     ctx.conv = NULL;
 #if !wxUSE_UNICODE
-    if ( encoding.CmpNoCase(wxT("UTF-8")) != 0 )
+    if ( encoding.CmpNoCase(wxS("UTF-8")) != 0 )
         ctx.conv = new wxCSConv(encoding);
 #endif
     ctx.removeWhiteOnlyNodes = (flags & wxXMLDOC_KEEP_WHITESPACE_NODES) == 0;
@@ -698,30 +768,40 @@ bool wxXmlDocument::Load(wxInputStream& stream, const wxString& encoding, int fl
 //  wxXmlDocument saving routines
 //-----------------------------------------------------------------------------
 
+// helpers for XML generation
+namespace
+{
+
 // write string to output:
-inline static void OutputString(wxOutputStream& stream, const wxString& str,
-                                wxMBConv *convMem = NULL,
-                                wxMBConv *convFile = NULL)
+bool OutputString(wxOutputStream& stream,
+                  const wxString& str,
+                  wxMBConv *convMem,
+                  wxMBConv *convFile)
 {
     if (str.empty())
-        return;
+        return true;
 
 #if wxUSE_UNICODE
     wxUnusedVar(convMem);
 
     const wxWX2MBbuf buf(str.mb_str(*(convFile ? convFile : &wxConvUTF8)));
-    stream.Write((const char*)buf, strlen((const char*)buf));
+    if ( !buf )
+        return false;
+
+    stream.Write(buf, strlen(buf));
 #else // !wxUSE_UNICODE
     if ( convFile && convMem )
     {
         wxString str2(str.wc_str(*convMem), *convFile);
-        stream.Write(str2.mb_str(), str2.Len());
+        stream.Write(str2.mb_str(), str2.length());
     }
     else // no conversions to do
     {
-        stream.Write(str.mb_str(), str.Len());
+        stream.Write(str.mb_str(), str.length());
     }
 #endif // wxUSE_UNICODE/!wxUSE_UNICODE
+
+    return stream.IsOk();
 }
 
 // flags for OutputStringEnt()
@@ -732,159 +812,193 @@ enum
 
 // 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 = NULL,
-                            wxMBConv *convFile = NULL,
-                            int flags = 0)
-{
-    wxString buf;
-    size_t i, last, len;
-    wxChar c;
-
-    len = str.Len();
-    last = 0;
+bool OutputStringEnt(wxOutputStream& stream,
+                     const wxString& str,
+                     wxMBConv *convMem,
+                     wxMBConv *convFile,
+                     int flags = 0)
+{
+    const size_t len = str.length();
+    size_t i,
+           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;")) ||
-            ((flags & XML_ESCAPE_QUOTES) && c == wxT('"')))
+        wxChar c = str.GetChar(i);
+        if (c == wxS('<') || c == wxS('>') ||
+            (c == wxS('&') && str.substr(i+1, 4) != wxS("amp;")) ||
+            ((flags & XML_ESCAPE_QUOTES) && c == wxS('"')))
         {
-            OutputString(stream, str.Mid(last, i - last), convMem, convFile);
-            switch (c)
+            if ( !OutputString(stream, str.substr(last, i - last),
+                               convMem, convFile) )
+                return false;
+
+            const char *escaped;
+            switch ( c )
             {
-                case wxT('<'):
-                    OutputString(stream, wxT("&lt;"));
+                case wxS('<'):
+                    escaped = "&lt;";
                     break;
-                case wxT('>'):
-                    OutputString(stream, wxT("&gt;"));
+                case wxS('>'):
+                    escaped = "&gt;";
                     break;
-                case wxT('&'):
-                    OutputString(stream, wxT("&amp;"));
+                case wxS('&'):
+                    escaped = "&amp;";
                     break;
-                case wxT('"'):
-                    OutputString(stream, wxT("&quot;"));
+                case wxS('"'):
+                    escaped = "&quot;";
                     break;
                 default:
-                    break;
+                    wxFAIL_MSG( "logic error in the code" );
+                    return false;
             }
+
+            if ( !OutputString(stream, escaped, convMem, convFile) )
+                return false;
+
             last = i + 1;
         }
     }
-    OutputString(stream, str.Mid(last, i - last), convMem, convFile);
+
+    return OutputString(stream, str.substr(last, i - last), convMem, convFile);
 }
 
-inline static void OutputIndentation(wxOutputStream& stream, int indent)
+bool OutputIndentation(wxOutputStream& stream,
+                       int indent,
+                       wxMBConv *convMem,
+                       wxMBConv *convFile)
 {
-    wxString str = wxT("\n");
-    for (int i = 0; i < indent; i++)
-        str << wxT(' ') << wxT(' ');
-    OutputString(stream, str);
+    wxString str(wxS("\n"));
+    str += wxString(2*indent, wxS(' '));
+    return OutputString(stream, str, convMem, convFile);
 }
 
-static void OutputNode(wxOutputStream& stream, wxXmlNode *node, int indent,
-                       wxMBConv *convMem, wxMBConv *convFile, int indentstep)
+bool OutputNode(wxOutputStream& stream,
+                wxXmlNode *node,
+                int indent,
+                wxMBConv *convMem,
+                wxMBConv *convFile,
+                int indentstep)
 {
-    wxXmlNode *n, *prev;
-    wxXmlAttribute *attr;
-
+    bool rc;
     switch (node->GetType())
     {
         case wxXML_CDATA_SECTION_NODE:
-            OutputString( stream, wxT("<![CDATA["));
-            OutputString( stream, node->GetContent() );
-            OutputString( stream, wxT("]]>") );
+            rc = OutputString(stream, wxS("<![CDATA["), convMem, convFile) &&
+                 OutputString(stream, node->GetContent(), convMem, convFile) &&
+                 OutputString(stream, wxS("]]>"), convMem, convFile);
             break;
 
         case wxXML_TEXT_NODE:
-            OutputStringEnt(stream, node->GetContent(), convMem, convFile);
+            rc = OutputStringEnt(stream, node->GetContent(), convMem, convFile);
             break;
 
         case wxXML_ELEMENT_NODE:
-            OutputString(stream, wxT("<"));
-            OutputString(stream, node->GetName());
+            rc = OutputString(stream, wxS("<"), convMem, convFile) &&
+                 OutputString(stream, node->GetName(), convMem, convFile);
 
-            attr = node->GetAttributes();
-            while (attr)
+            if ( rc )
             {
-                OutputString(stream, wxT(" ") + attr->GetName() +  wxT("=\""));
-                OutputStringEnt(stream, attr->GetValue(), convMem, convFile,
-                                XML_ESCAPE_QUOTES);
-                OutputString(stream, wxT("\""));
-                attr = attr->GetNext();
+                for ( wxXmlAttribute *attr = node->GetAttributes();
+                      attr && rc;
+                      attr = attr->GetNext() )
+                {
+                    rc = OutputString(stream,
+                                      wxS(" ") + attr->GetName() +  wxS("=\""),
+                                      convMem, convFile) &&
+                         OutputStringEnt(stream, attr->GetValue(),
+                                         convMem, convFile,
+                                         XML_ESCAPE_QUOTES) &&
+                         OutputString(stream, wxS("\""), convMem, convFile);
+                }
             }
 
-            if (node->GetChildren())
+            if ( node->GetChildren() )
             {
-                OutputString(stream, wxT(">"));
-                prev = NULL;
-                n = node->GetChildren();
-                while (n)
+                rc = OutputString(stream, wxS(">"), convMem, convFile);
+
+                wxXmlNode *prev = NULL;
+                for ( wxXmlNode *n = node->GetChildren();
+                      n && rc;
+                      n = n->GetNext() )
                 {
-                    if (indentstep >= 0 && n && n->GetType() != wxXML_TEXT_NODE)
-                        OutputIndentation(stream, indent + indentstep);
-                    OutputNode(stream, n, indent + indentstep, convMem, convFile, indentstep);
+                    if ( indentstep >= 0 && n->GetType() != wxXML_TEXT_NODE )
+                    {
+                        rc = OutputIndentation(stream, indent + indentstep,
+                                               convMem, convFile);
+                    }
+
+                    if ( rc )
+                        rc = OutputNode(stream, n, indent + indentstep,
+                                        convMem, convFile, indentstep);
+
                     prev = n;
-                    n = n->GetNext();
                 }
-                if (indentstep >= 0 && prev && prev->GetType() != wxXML_TEXT_NODE)
-                    OutputIndentation(stream, indent);
-                OutputString(stream, wxT("</"));
-                OutputString(stream, node->GetName());
-                OutputString(stream, wxT(">"));
+
+                if ( rc && indentstep >= 0 &&
+                        prev && prev->GetType() != wxXML_TEXT_NODE )
+                {
+                    rc = OutputIndentation(stream, indent, convMem, convFile);
+                }
+
+                if ( rc )
+                {
+                    rc = OutputString(stream, wxS("</"), convMem, convFile) &&
+                         OutputString(stream, node->GetName(),
+                                      convMem, convFile) &&
+                         OutputString(stream, wxS(">"), convMem, convFile);
+                }
+            }
+            else // no children, output "<foo/>"
+            {
+                rc = OutputString(stream, wxS("/>"), convMem, convFile);
             }
-            else
-                OutputString(stream, wxT("/>"));
             break;
 
         case wxXML_COMMENT_NODE:
-            OutputString(stream, wxT("<!--"));
-            OutputString(stream, node->GetContent(), convMem, convFile);
-            OutputString(stream, wxT("-->"));
+            rc = OutputString(stream, wxS("<!--"), convMem, convFile) &&
+                 OutputString(stream, node->GetContent(), convMem, convFile) &&
+                 OutputString(stream, wxS("-->"), convMem, convFile);
             break;
 
         default:
-            wxFAIL_MSG(wxT("unsupported node type"));
+            wxFAIL_MSG("unsupported node type");
+            rc = false;
     }
+
+    return rc;
 }
 
+} // anonymous namespace
+
 bool wxXmlDocument::Save(wxOutputStream& stream, int indentstep) const
 {
     if ( !IsOk() )
         return false;
 
-    wxString s;
-
-    wxMBConv *convMem = NULL,
-             *convFile;
+    wxScopedPtr<wxMBConv> convMem, convFile;
 
 #if wxUSE_UNICODE
-    convFile = new wxCSConv(GetFileEncoding());
-    convMem = NULL;
+    convFile.reset(new wxCSConv(GetFileEncoding()));
 #else
     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;
+        convFile.reset(new wxCSConv(GetFileEncoding()));
+        convMem.reset(new wxCSConv(GetEncoding()));
     }
+    //else: file and in-memory encodings are the same, no conversion needed
 #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, indentstep);
-    OutputString(stream, wxT("\n"));
-
-    delete convFile;
-    delete convMem;
-
-    return true;
+    return OutputString(stream,
+                        wxString::Format
+                        (
+                         wxS("<?xml version=\"%s\" encoding=\"%s\"?>\n"),
+                         GetVersion(), GetFileEncoding()
+                        ),
+                        convMem.get(),
+                        convFile.get()) &&
+           OutputNode(stream, GetRoot(), 0,
+                      convMem.get(), convFile.get(), indentstep) &&
+           OutputString(stream, wxS("\n"), convMem.get(), convFile.get());
 }
 
 #endif // wxUSE_XML