diff --git a/docs/changes.txt b/docs/changes.txt index 9dc1bfad84..2573fd90b9 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -96,6 +96,7 @@ All: - Fixed bug with parsing some dates in wxDateTime (Bob Pesner). - Fixed bug with parsing negative time zones in wxDateTime::ParseRfc822Date(). - Initialize current line in wxTextBuffer ctor (Suzuki Masahiro). +- Improved performance of XML parsing (Francesco Montorsi). All (GUI): diff --git a/docs/latex/wx/xmlnode.tex b/docs/latex/wx/xmlnode.tex index 4afe14b514..0c23db9ebe 100644 --- a/docs/latex/wx/xmlnode.tex +++ b/docs/latex/wx/xmlnode.tex @@ -232,6 +232,23 @@ to the list of children and becomes the first child of this node. Returns \true if {\it followingNode} has been found and the {\it child} node has been inserted. +\membersection{wxXmlNode::InsertChildAfter}\label{wxxmlnodeinsertchildafter} + +\func{bool}{InsertChildAfter}{\param{wxXmlNode* }{child}, \param{wxXmlNode* }{precedingNode}} + +Inserts the \arg{child} node immediately after \arg{precedingNode} in the +children list. Returns \true if \arg{precedingNode} has been found and the +\arg{child} node has been inserted. + +\wxheading{Parameters} + +\docparam{child}{Node to insert.} +\docparam{precedingNode}{The node to insert \arg{child} after. + As a special case, this can be \NULL if this node has no children yet -- + in that case, \arg{child} will become this node's only child node.} + +\newsince{2.8.8} + \membersection{wxXmlNode::IsWhitespaceOnly}\label{wxxmlnodecontainsiswhitespaceonly} \constfunc{bool}{IsWhitespaceOnly}{\void} diff --git a/include/wx/xml/xml.h b/include/wx/xml/xml.h index 502b7c27e9..4d33a5cb8d 100644 --- a/include/wx/xml/xml.h +++ b/include/wx/xml/xml.h @@ -116,6 +116,9 @@ public: const wxString& content = wxEmptyString); virtual void AddChild(wxXmlNode *child); virtual bool InsertChild(wxXmlNode *child, wxXmlNode *followingNode); +#if wxABI_VERSION >= 20808 + bool InsertChildAfter(wxXmlNode *child, wxXmlNode *precedingNode); +#endif virtual bool RemoveChild(wxXmlNode *child); virtual void AddProperty(const wxString& name, const wxString& value); virtual bool DeleteProperty(const wxString& name); diff --git a/src/xml/xml.cpp b/src/xml/xml.cpp index 21aa365003..7a2d12b541 100644 --- a/src/xml/xml.cpp +++ b/src/xml/xml.cpp @@ -219,6 +219,34 @@ bool wxXmlNode::InsertChild(wxXmlNode *child, wxXmlNode *before_node) return true; } +// inserts a new node right after 'precedingNode' +bool wxXmlNode::InsertChildAfter(wxXmlNode *child, wxXmlNode *precedingNode) +{ + wxCHECK_MSG( child, false, wxT("cannot insert a NULL node!") ); + wxCHECK_MSG( child->m_parent == NULL, false, wxT("node already has a parent") ); + wxCHECK_MSG( child->m_next == NULL, false, wxT("node already has m_next") ); + wxCHECK_MSG( precedingNode == NULL || precedingNode->m_parent == this, false, + wxT("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, + wxT("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) @@ -455,15 +483,32 @@ bool wxIsWhiteOnly(const wxChar *buf) struct wxXmlParsingContext { + wxXmlParsingContext() + : conv(NULL), + root(NULL), + node(NULL), + lastChild(NULL), + lastAsText(NULL), + removeWhiteOnlyNodes(false) + {} + 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) { @@ -476,11 +521,19 @@ static void StartElementHnd(void *userData, const char *name, const char **atts) 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; } } @@ -489,6 +542,11 @@ 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; } @@ -512,8 +570,12 @@ static void TextHnd(void *userData, const char *s, int len) if (!whiteOnly) { - ctx->lastAsText = new wxXmlNode(wxXML_TEXT_NODE, wxT("text"), str); - ctx->node->AddChild(ctx->lastAsText); + wxXmlNode *textnode = + new wxXmlNode(wxXML_TEXT_NODE, wxT("text"), str); + + ASSERT_LAST_CHILD_OK(ctx); + ctx->node->InsertChildAfter(textnode, ctx->lastChild); + ctx->lastChild= ctx->lastAsText = textnode; } } } @@ -524,8 +586,12 @@ static void StartCdataHnd(void *userData) { wxXmlParsingContext *ctx = (wxXmlParsingContext*)userData; - ctx->lastAsText = new wxXmlNode(wxXML_CDATA_SECTION_NODE, wxT("cdata"),wxT("")); - ctx->node->AddChild(ctx->lastAsText); + wxXmlNode *textnode = + new wxXmlNode(wxXML_CDATA_SECTION_NODE, wxT("cdata"),wxT("")); + + ASSERT_LAST_CHILD_OK(ctx); + ctx->node->InsertChildAfter(textnode, ctx->lastChild); + ctx->lastChild= ctx->lastAsText = textnode; } } @@ -539,8 +605,12 @@ 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))); + wxXmlNode *commentnode = + new wxXmlNode(wxXML_COMMENT_NODE, + wxT("comment"), CharToString(ctx->conv, data)); + ASSERT_LAST_CHILD_OK(ctx); + ctx->node->InsertChildAfter(commentnode, ctx->lastChild); + ctx->lastChild = commentnode; } ctx->lastAsText = NULL; } diff --git a/version-script.in b/version-script.in index 5948e7915c..4b999c71f9 100644 --- a/version-script.in +++ b/version-script.in @@ -44,6 +44,7 @@ *wxWindowBase*ClientToWindowSize*; *wxWindowBase*Get*Sibling*; *wxWindowBase*WindowToClientSize*; + *wxXmlNode*InsertChildAfter*; }; # public symbols added in 2.8.7 (please keep in alphabetical order):