- Added precision parameter to wxString::From[C]Double().
- Added wxThread::Wait() and Delete() "wait mode" parameter (Catalin Raceanu).
- Allow showing milliseconds in wxLog time stamps (tan).
+- Added support for processing instructions in wxXmlDocument (Nick Matthews).
Unix:
const wxString& encoding = wxT("UTF-8"));
wxXmlDocument(wxInputStream& stream,
const wxString& encoding = wxT("UTF-8"));
- virtual ~wxXmlDocument() { wxDELETE(m_root); }
+ virtual ~wxXmlDocument() { wxDELETE(m_docNode); }
wxXmlDocument(const wxXmlDocument& doc);
wxXmlDocument& operator=(const wxXmlDocument& doc);
virtual bool Save(const wxString& filename, int indentstep = 2) const;
virtual bool Save(wxOutputStream& stream, int indentstep = 2) const;
- bool IsOk() const { return m_root != NULL; }
+ bool IsOk() const { return GetRoot() != NULL; }
// Returns root node of the document.
- wxXmlNode *GetRoot() const { return m_root; }
+ wxXmlNode *GetRoot() const;
+ // Returns the document node.
+ wxXmlNode *GetDocumentNode() const { return m_docNode; }
+
// Returns version of document (may be empty).
const wxString& GetVersion() const { return m_version; }
const wxString& GetFileEncoding() const { return m_fileEncoding; }
// Write-access methods:
- wxXmlNode *DetachRoot() { wxXmlNode *old=m_root; m_root=NULL; return old; }
- void SetRoot(wxXmlNode *node) { wxDELETE(m_root); m_root = node; }
+ wxXmlNode *DetachDocumentNode() { wxXmlNode *old=m_docNode; m_docNode=NULL; return old; }
+ void SetDocumentNode(wxXmlNode *node) { wxDELETE(m_docNode); m_docNode = node; }
+ wxXmlNode *DetachRoot();
+ void SetRoot(wxXmlNode *node);
void SetVersion(const wxString& version) { m_version = version; }
void SetFileEncoding(const wxString& encoding) { m_fileEncoding = encoding; }
+ void AppendToProlog(wxXmlNode *node);
#if !wxUSE_UNICODE
// Returns encoding of in-memory representation of the document
#if !wxUSE_UNICODE
wxString m_encoding;
#endif
- wxXmlNode *m_root;
+ wxXmlNode *m_docNode;
void DoCopy(const wxXmlDocument& doc);
@c title and irrelevant content and one child of type @c wxXML_TEXT_NODE
with @c hi as content.
+ The @c wxXML_PI_NODE type sets the name to the PI target and the contents to
+ the instructions. Note that whilst the PI instructions are often in the form
+ of pseudo-attributes these do not use the nodes attribute system. It is the users
+ responsibility to code and decode the instruction text.
+
If @c wxUSE_UNICODE is 0, all strings are encoded in the encoding given to
wxXmlDocument::Load (default is UTF-8).
This function searches only the parents of this node until it finds
@a grandparent or the @NULL node (which is the parent of non-linked
- nodes or the parent of a wxXmlDocument's root node).
+ nodes or the parent of a wxXmlDocument's root element node).
*/
int GetDepth(wxXmlNode* grandparent = NULL) const;
is represented by expat with the following tag tree:
@code
- wxXML_ENTITY_NODE name="tagname", content=""
+ wxXML_ELEMENT_NODE name="tagname", content=""
|-- wxXML_TEXT_NODE name="", content="tagcontent"
@endcode
or eventually:
@code
- wxXML_ENTITY_NODE name="tagname", content=""
+ wxXML_ELEMENT_NODE name="tagname", content=""
|-- wxXML_CDATA_SECTION_NODE name="", content="tagcontent"
@endcode
if (doc.GetRoot()->GetName() != "myroot-node")
return false;
+ // examine prologue
+ wxXmlNode *prolog = doc.GetDocumentNode()->GetChildren();
+ while (prolog) {
+
+ if (prolog->GetType() == wxXML_PI_NODE && prolog->GetName() == "target") {
+
+ // process Process Instruction contents
+ wxString pi = prolog->GetContent();
+
+ ...
+
+ }
+ }
+
wxXmlNode *child = doc.GetRoot()->GetChildren();
while (child) {
virtual ~wxXmlDocument();
/**
- Detaches the document root node and returns it.
+ Appends a Process Instruction or Comment node to the document prologue.
+
+ Calling this function will create a prologue or attach the node to the
+ end of an existing prologue.
+
+ @since 2.9.2
+ */
+ void AppendToProlog(wxXmlNode* node);
+
+ /**
+ Detaches the document node and returns it.
- The document root node will be set to @NULL and thus IsOk() will
+ The document node will be set to @NULL and thus IsOk() will
return @false after calling this function.
Note that the caller is responsible for deleting the returned node in order
to avoid memory leaks.
+
+ @since 2.9.2
+ */
+ wxXmlNode* DetachDocumentNode();
+
+ /**
+ Detaches the root entity node and returns it.
+
+ After calling this function, the document node will remain together with
+ any prologue nodes, but IsOk() will return @false since the root entity
+ will be missing.
+
+ Note that the caller is reponsible for deleting the returned node in order
+ to avoid memory leaks.
*/
wxXmlNode* DetachRoot();
const wxString& GetFileEncoding() const;
/**
- Returns the root node of the document.
+ Returns the document node of the document.
+
+ @since 2.9.2
+ */
+ wxXmlNode* GetDocumentNode() const;
+
+ /**
+ Returns the root element node of the document.
*/
wxXmlNode* GetRoot() const;
virtual bool Save(wxOutputStream& stream, int indentstep = 2) const;
/**
- Sets the enconding of the document.
+ Sets the document node of this document.
+
+ Deletes any previous document node.
+ Use DetachDocumentNode() and then SetDocumentNode() if you want to
+ replace the document node without deleting the old document tree.
+
+ @since 2.9.2
+ */
+ void SetDocumentNode(wxXmlNode* node);
+
+ /**
+ Sets the encoding of the document.
*/
void SetEncoding(const wxString& enc);
void SetFileEncoding(const wxString& encoding);
/**
- Sets the root node of this document. Deletes previous root node.
- Use DetachRoot() and then SetRoot() if you want to replace the root
- node without deleting the old document tree.
+ Sets the root element node of this document.
+
+ Will create the document node if necessary. Any previous
+ root element node is deleted.
*/
void SetRoot(wxXmlNode* node);
//-----------------------------------------------------------------------------
wxXmlDocument::wxXmlDocument()
- : m_version(wxS("1.0")), m_fileEncoding(wxS("utf-8")), m_root(NULL)
+ : m_version(wxS("1.0")), m_fileEncoding(wxS("UTF-8")), m_docNode(NULL)
{
#if !wxUSE_UNICODE
m_encoding = wxS("UTF-8");
}
wxXmlDocument::wxXmlDocument(const wxString& filename, const wxString& encoding)
- :wxObject(), m_root(NULL)
+ :wxObject(), m_docNode(NULL)
{
if ( !Load(filename, encoding) )
{
- wxDELETE(m_root);
+ wxDELETE(m_docNode);
}
}
wxXmlDocument::wxXmlDocument(wxInputStream& stream, const wxString& encoding)
- :wxObject(), m_root(NULL)
+ :wxObject(), m_docNode(NULL)
{
if ( !Load(stream, encoding) )
{
- wxDELETE(m_root);
+ wxDELETE(m_docNode);
}
}
wxXmlDocument& wxXmlDocument::operator=(const wxXmlDocument& doc)
{
- wxDELETE(m_root);
+ wxDELETE(m_docNode);
DoCopy(doc);
return *this;
}
#endif
m_fileEncoding = doc.m_fileEncoding;
- if (doc.m_root)
- m_root = new wxXmlNode(*doc.m_root);
+ if (doc.m_docNode)
+ m_docNode = new wxXmlNode(*doc.m_docNode);
else
- m_root = NULL;
+ m_docNode = NULL;
}
bool wxXmlDocument::Load(const wxString& filename, const wxString& encoding, int flags)
return Save(stream, indentstep);
}
+wxXmlNode *wxXmlDocument::GetRoot() const
+{
+ wxXmlNode *node = m_docNode;
+ if (node)
+ {
+ node = m_docNode->GetChildren();
+ while (node != NULL && node->GetType() != wxXML_ELEMENT_NODE)
+ node = node->GetNext();
+ }
+ return node;
+}
+
+wxXmlNode *wxXmlDocument::DetachRoot()
+{
+ wxXmlNode *node = m_docNode;
+ if (node)
+ {
+ node = m_docNode->GetChildren();
+ wxXmlNode *prev = NULL;
+ while (node != NULL && node->GetType() != wxXML_ELEMENT_NODE)
+ {
+ prev = node;
+ node = node->GetNext();
+ }
+ if (node)
+ {
+ if (node == m_docNode->GetChildren())
+ m_docNode->SetChildren(node->GetNext());
+
+ if (prev)
+ prev->SetNext(node->GetNext());
+
+ node->SetParent(NULL);
+ node->SetNext(NULL);
+ }
+ }
+ return node;
+}
+void wxXmlDocument::SetRoot(wxXmlNode *root)
+{
+ wxXmlNode *node = m_docNode;
+ if (node)
+ {
+ node = m_docNode->GetChildren();
+ wxXmlNode *prev = NULL;
+ while (node != NULL && node->GetType() != wxXML_ELEMENT_NODE)
+ {
+ prev = node;
+ node = node->GetNext();
+ }
+ if (node)
+ {
+ root->SetNext( node->GetNext() );
+ wxDELETE(node);
+ }
+ if (prev)
+ prev->SetNext(root);
+ else
+ m_docNode->SetChildren(root);
+ }
+ else
+ {
+ m_docNode = new wxXmlNode(wxXML_DOCUMENT_NODE, wxEmptyString);
+ }
+ root->SetParent(m_docNode);
+}
+
+void wxXmlDocument::AppendToProlog(wxXmlNode *node)
+{
+ if (!m_docNode)
+ m_docNode = new wxXmlNode(wxXML_DOCUMENT_NODE, wxEmptyString);
+ if (IsOk())
+ m_docNode->InsertChild( node, GetRoot() );
+ else
+ m_docNode->AddChild( node );
+}
//-----------------------------------------------------------------------------
// wxXmlDocument loading routines
{
wxXmlParsingContext()
: conv(NULL),
- root(NULL),
node(NULL),
lastChild(NULL),
lastAsText(NULL),
XML_Parser parser;
wxMBConv *conv;
- wxXmlNode *root;
wxXmlNode *node; // the node being parsed
wxXmlNode *lastChild; // the last child of "node"
wxXmlNode *lastAsText; // the last _text_ child of "node"
a += 2;
}
- if (ctx->root == NULL)
- {
- ctx->root = node;
- }
- else
- {
- ASSERT_LAST_CHILD_OK(ctx);
- ctx->node->InsertChildAfter(node, ctx->lastChild);
- }
-
+ 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
{
wxXmlParsingContext *ctx = (wxXmlParsingContext*)userData;
- if (ctx->node)
- {
- wxXmlNode *commentnode =
- new wxXmlNode(wxXML_COMMENT_NODE,
- 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).
+ wxXmlNode *commentnode =
+ new wxXmlNode(wxXML_COMMENT_NODE,
+ wxS("comment"), CharToString(ctx->conv, data),
+ XML_GetCurrentLineNumber(ctx->parser));
+ ASSERT_LAST_CHILD_OK(ctx);
+ ctx->node->InsertChildAfter(commentnode, ctx->lastChild);
+ ctx->lastChild = commentnode;
+ ctx->lastAsText = NULL;
+}
+
+static void PIHnd(void *userData, const char *target, const char *data)
+{
+ wxXmlParsingContext *ctx = (wxXmlParsingContext*)userData;
+
+ wxXmlNode *pinode =
+ new wxXmlNode(wxXML_PI_NODE, CharToString(ctx->conv, target),
+ CharToString(ctx->conv, data),
+ XML_GetCurrentLineNumber(ctx->parser));
+
+ ASSERT_LAST_CHILD_OK(ctx);
+ ctx->node->InsertChildAfter(pinode, ctx->lastChild);
+ ctx->lastChild = pinode;
ctx->lastAsText = NULL;
}
wxXmlParsingContext ctx;
bool done;
XML_Parser parser = XML_ParserCreate(NULL);
+ wxXmlNode *root = new wxXmlNode(wxXML_DOCUMENT_NODE, wxEmptyString);
ctx.encoding = wxS("UTF-8"); // default in absence of encoding=""
ctx.conv = NULL;
#endif
ctx.removeWhiteOnlyNodes = (flags & wxXMLDOC_KEEP_WHITESPACE_NODES) == 0;
ctx.parser = parser;
+ ctx.node = root;
XML_SetUserData(parser, (void*)&ctx);
XML_SetElementHandler(parser, StartElementHnd, EndElementHnd);
XML_SetCharacterDataHandler(parser, TextHnd);
XML_SetCdataSectionHandler(parser, StartCdataHnd, EndCdataHnd);;
XML_SetCommentHandler(parser, CommentHnd);
+ XML_SetProcessingInstructionHandler(parser, PIHnd);
XML_SetDefaultHandler(parser, DefaultHnd);
XML_SetUnknownEncodingHandler(parser, UnknownEncodingHnd, NULL);
SetVersion(ctx.version);
if (!ctx.encoding.empty())
SetFileEncoding(ctx.encoding);
- SetRoot(ctx.root);
+ SetDocumentNode(root);
}
else
{
- delete ctx.root;
+ delete root;
}
XML_ParserFree(parser);
OutputString(stream, wxS("-->"), convMem, convFile);
break;
+ case wxXML_PI_NODE:
+ rc = OutputString(stream, wxT("<?"), convMem, convFile) &&
+ OutputString(stream, node->GetName(), convMem, convFile) &&
+ OutputString(stream, wxT(" "), convMem, convFile) &&
+ OutputString(stream, node->GetContent(), convMem, convFile) &&
+ OutputString(stream, wxT("?>"), convMem, convFile);
+ break;
+
default:
wxFAIL_MSG("unsupported node type");
rc = false;
//else: file and in-memory encodings are the same, no conversion needed
#endif
- 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());
+ wxString dec = wxString::Format(
+ wxS("<?xml version=\"%s\" encoding=\"%s\"?>\n"),
+ GetVersion(), GetFileEncoding()
+ );
+ bool rc = OutputString(stream, dec, convMem.get(), convFile.get());
+
+ wxXmlNode *node = GetDocumentNode();
+ if ( node )
+ node = node->GetChildren();
+
+ while( rc && node )
+ {
+ rc = OutputNode(stream, node, 0, convMem.get(),
+ convFile.get(), indentstep) &&
+ OutputString(stream, wxS("\n"), convMem.get(), convFile.get());
+ node = node->GetNext();
+ }
+ return rc;
}
/*static*/ wxVersionInfo wxXmlDocument::GetLibraryVersionInfo()
CPPUNIT_TEST( InsertChildAfter );
CPPUNIT_TEST( LoadSave );
CPPUNIT_TEST( CDATA );
+ CPPUNIT_TEST( PI );
CPPUNIT_TEST( Escaping );
+ CPPUNIT_TEST( DetachRoot );
+ CPPUNIT_TEST( AppendToProlog );
CPPUNIT_TEST_SUITE_END();
void InsertChild();
void InsertChildAfter();
void LoadSave();
void CDATA();
+ void PI();
void Escaping();
+ void DetachRoot();
+ void AppendToProlog();
DECLARE_NO_COPY_CLASS(XmlTestCase)
};
const char *xmlText =
"<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n"
"<resource xmlns=\"http://www.wxwidgets.org/wxxrc\" version=\"2.3.0.1\">\n"
+" <!-- Test comment -->\n"
" <object class=\"wxDialog\" name=\"my_dialog\">\n"
" <children>\n"
" <grandchild id=\"1\"/>\n"
CPPUNIT_ASSERT_EQUAL( wxString(utf8xmlText),
wxString(sos8.GetString().ToUTF8()) );
#endif // wxUSE_UNICODE
+
+ const char *xmlTextProlog =
+"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+"<!-- Prolog comment -->\n"
+"<?xml-stylesheet href=\"style.css\" type=\"text/css\"?>\n"
+"<resource xmlns=\"http://www.wxwidgets.org/wxxrc\" version=\"2.3.0.1\">\n"
+" <!-- Test comment -->\n"
+" <object class=\"wxDialog\" name=\"my_dialog\">\n"
+" <children>\n"
+" <grandchild id=\"1\"/>\n"
+" </children>\n"
+" <subobject/>\n"
+" </object>\n"
+"</resource>\n"
+"<!-- Trailing comment -->\n"
+ ;
+
+ wxStringInputStream sisp(xmlTextProlog);
+ CPPUNIT_ASSERT( doc.Load(sisp, "UTF-8") );
+
+ wxStringOutputStream sosp;
+ CPPUNIT_ASSERT( doc.Save(sosp) );
+
+ CPPUNIT_ASSERT_EQUAL( xmlTextProlog, sosp.GetString() );
}
void XmlTestCase::CDATA()
CPPUNIT_ASSERT_EQUAL( "Giovanni Mittone", n->GetContent() );
}
+void XmlTestCase::PI()
+{
+ const char *xmlText =
+ "<?xml version=\"1.0\" encoding=\"windows-1252\"?>\n"
+ "<root>\n"
+ " <?robot index=\"no\" follow=\"no\"?>\n"
+ "</root>\n"
+ ;
+
+ wxStringInputStream sis(xmlText);
+ wxXmlDocument doc;
+ CPPUNIT_ASSERT( doc.Load(sis) );
+
+ wxXmlNode *n = doc.GetRoot();
+ CPPUNIT_ASSERT( n );
+
+ n = n->GetChildren();
+ CPPUNIT_ASSERT( n );
+
+ CPPUNIT_ASSERT_EQUAL( "index=\"no\" follow=\"no\"", n->GetContent() );
+}
+
void XmlTestCase::Escaping()
{
// Verify that attribute values are escaped correctly, see
CPPUNIT_ASSERT_EQUAL( xmlText, sos.GetString() );
}
+
+void XmlTestCase::DetachRoot()
+{
+ const char *xmlTextProlog =
+"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+"<!-- Prolog comment -->\n"
+"<?xml-stylesheet href=\"style.css\" type=\"text/css\"?>\n"
+"<resource xmlns=\"http://www.wxwidgets.org/wxxrc\" version=\"2.3.0.1\">\n"
+" <!-- Test comment -->\n"
+" <object class=\"wxDialog\" name=\"my_dialog\">\n"
+" <children>\n"
+" <grandchild id=\"1\"/>\n"
+" </children>\n"
+" <subobject/>\n"
+" </object>\n"
+"</resource>\n"
+"<!-- Trailing comment -->\n"
+ ;
+ const char *xmlTextHtm =
+"<?xml version=\"1.0\" encoding=\"windows-1252\"?>\n"
+"<html>\n"
+" <head>\n"
+" <title>Testing wxXml</title>\n"
+" </head>\n"
+" <body>\n"
+" <p>Some body text</p>\n"
+" </body>\n"
+"</html>\n"
+ ;
+ wxXmlDocument doc;
+
+ wxStringInputStream sish(xmlTextHtm);
+ CPPUNIT_ASSERT( doc.Load(sish) );
+
+ wxXmlNode *root = doc.DetachRoot();
+
+ wxStringInputStream sisp(xmlTextProlog);
+ CPPUNIT_ASSERT( doc.Load(sisp) );
+
+ doc.SetRoot(root);
+
+ wxStringOutputStream sos;
+ CPPUNIT_ASSERT( doc.Save(sos) );
+
+ const char *xmlTextResult1 =
+"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+"<!-- Prolog comment -->\n"
+"<?xml-stylesheet href=\"style.css\" type=\"text/css\"?>\n"
+"<html>\n"
+" <head>\n"
+" <title>Testing wxXml</title>\n"
+" </head>\n"
+" <body>\n"
+" <p>Some body text</p>\n"
+" </body>\n"
+"</html>\n"
+"<!-- Trailing comment -->\n"
+ ;
+ CPPUNIT_ASSERT_EQUAL( xmlTextResult1, sos.GetString() );
+
+ wxStringInputStream sisp2(xmlTextProlog);
+ CPPUNIT_ASSERT( doc.Load(sisp2) );
+
+ root = doc.DetachRoot();
+
+ wxStringInputStream sish2(xmlTextHtm);
+ CPPUNIT_ASSERT( doc.Load(sish2) );
+
+ doc.SetRoot(root);
+
+ wxStringOutputStream sos2;
+ CPPUNIT_ASSERT( doc.Save(sos2) );
+
+ const char *xmlTextResult2 =
+"<?xml version=\"1.0\" encoding=\"windows-1252\"?>\n"
+"<resource xmlns=\"http://www.wxwidgets.org/wxxrc\" version=\"2.3.0.1\">\n"
+" <!-- Test comment -->\n"
+" <object class=\"wxDialog\" name=\"my_dialog\">\n"
+" <children>\n"
+" <grandchild id=\"1\"/>\n"
+" </children>\n"
+" <subobject/>\n"
+" </object>\n"
+"</resource>\n"
+ ;
+ CPPUNIT_ASSERT_EQUAL( xmlTextResult2, sos2.GetString() );
+}
+
+void XmlTestCase::AppendToProlog()
+{
+ const char *xmlText =
+"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+"<root>\n"
+" <p>Some text</p>\n"
+"</root>\n"
+ ;
+ wxXmlDocument rootdoc;
+ wxStringInputStream sis(xmlText);
+ CPPUNIT_ASSERT( rootdoc.Load(sis) );
+ wxXmlNode *root = rootdoc.DetachRoot();
+
+ wxXmlNode *comment1 = new wxXmlNode(wxXML_COMMENT_NODE, "comment",
+ " 1st prolog entry ");
+ wxXmlNode *pi = new wxXmlNode(wxXML_PI_NODE, "xml-stylesheet",
+ "href=\"style.css\" type=\"text/css\"");
+ wxXmlNode *comment2 = new wxXmlNode(wxXML_COMMENT_NODE, "comment",
+ " 3rd prolog entry ");
+
+ wxXmlDocument doc;
+ doc.AppendToProlog( comment1 );
+ doc.AppendToProlog( pi );
+ doc.SetRoot( root );
+ doc.AppendToProlog( comment2 );
+
+ wxStringOutputStream sos;
+ CPPUNIT_ASSERT( doc.Save(sos) );
+
+ const char *xmlTextResult =
+"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+"<!-- 1st prolog entry -->\n"
+"<?xml-stylesheet href=\"style.css\" type=\"text/css\"?>\n"
+"<!-- 3rd prolog entry -->\n"
+"<root>\n"
+" <p>Some text</p>\n"
+"</root>\n"
+ ;
+ CPPUNIT_ASSERT_EQUAL( xmlTextResult, sos.GetString() );
+}