13
0
livetrax/libs/pbd/xml++.cc

801 lines
15 KiB
C++
Raw Normal View History

/* xml++.cc
* libxml++ and this file are copyright (C) 2000 by Ari Johnson, and
* are covered by the GNU Lesser General Public License, which should be
* included with libxml++ as the file COPYING.
* Modified for Ardour and released under the same terms.
*/
#include <iostream>
#include "pbd/stacktrace.h"
#include "pbd/xml++.h"
#include <libxml/debugXML.h>
#include <libxml/xpath.h>
#include <libxml/xpathInternals.h>
2013-03-24 09:55:56 -04:00
xmlChar* xml_version = xmlCharStrdup("1.0");
using namespace std;
static XMLNode* readnode(xmlNodePtr);
static void writenode(xmlDocPtr, XMLNode*, xmlNodePtr, int);
static XMLSharedNodeList* find_impl(xmlXPathContext* ctxt, const string& xpath);
XMLTree::XMLTree()
: _filename()
, _root(0)
, _doc (0)
, _compression(0)
{
}
XMLTree::XMLTree(const string& fn, bool validate)
: _filename(fn)
, _root(0)
, _doc (0)
, _compression(0)
{
read_internal(validate);
}
XMLTree::XMLTree(const XMLTree* from)
: _filename(from->filename())
, _root(new XMLNode(*from->root()))
, _doc (xmlCopyDoc (from->_doc, 1))
, _compression(from->compression())
{
2015-10-05 10:17:49 -04:00
}
XMLTree::~XMLTree()
{
delete _root;
if (_doc) {
xmlFreeDoc (_doc);
}
}
int
XMLTree::set_compression(int c)
{
if (c > 9) {
c = 9;
} else if (c < 0) {
c = 0;
}
_compression = c;
return _compression;
}
bool
XMLTree::read_internal(bool validate)
{
//shouldnt be used anywhere ATM, remove if so!
assert(!validate);
delete _root;
_root = 0;
if (_doc) {
xmlFreeDoc (_doc);
_doc = 0;
}
/* create a parser context */
xmlParserCtxtPtr ctxt = xmlNewParserCtxt();
if (ctxt == NULL) {
return false;
}
xmlKeepBlanksDefault(0);
/* parse the file, activating the DTD validation option */
if (validate) {
_doc = xmlCtxtReadFile(ctxt, _filename.c_str(), NULL, XML_PARSE_DTDVALID);
} else {
_doc = xmlCtxtReadFile(ctxt, _filename.c_str(), NULL, XML_PARSE_HUGE);
}
2015-10-05 10:17:49 -04:00
/* check if parsing suceeded */
if (_doc == NULL) {
xmlFreeParserCtxt(ctxt);
return false;
} else {
/* check if validation suceeded */
if (validate && ctxt->valid == 0) {
xmlFreeParserCtxt(ctxt);
throw XMLException("Failed to validate document " + _filename);
}
}
_root = readnode(xmlDocGetRootElement(_doc));
/* free up the parser context */
xmlFreeParserCtxt(ctxt);
2015-10-05 10:17:49 -04:00
return true;
}
bool
XMLTree::read_buffer(const string& buffer, bool to_tree_doc)
{
xmlDocPtr doc;
_filename = "";
delete _root;
_root = 0;
doc = xmlParseMemory(const_cast<char*>(buffer.c_str()), buffer.length());
if (!doc) {
return false;
}
_root = readnode(xmlDocGetRootElement(doc));
if (to_tree_doc) {
if (_doc) {
xmlFreeDoc (_doc);
}
_doc = doc;
} else {
xmlFreeDoc (doc);
}
return true;
}
bool
XMLTree::write() const
{
xmlDocPtr doc;
XMLNodeList children;
int result;
xmlKeepBlanksDefault(0);
2013-03-24 09:55:56 -04:00
doc = xmlNewDoc(xml_version);
xmlSetDocCompressMode(doc, _compression);
writenode(doc, _root, doc->children, 1);
result = xmlSaveFormatFileEnc(_filename.c_str(), doc, "UTF-8", 1);
#ifndef NDEBUG
if (result == -1) {
xmlErrorPtr xerr = xmlGetLastError ();
if (!xerr) {
std::cerr << "unknown XML error during xmlSaveFormatFileEnc()." << std::endl;
} else {
std::cerr << "xmlSaveFormatFileEnc: error"
<< " domain: " << xerr->domain
<< " code: " << xerr->code
<< " msg: " << xerr->message
<< std::endl;
}
}
#endif
xmlFreeDoc(doc);
if (result == -1) {
return false;
}
return true;
}
void
XMLTree::debug(FILE* out) const
{
#ifdef LIBXML_DEBUG_ENABLED
xmlDocPtr doc;
XMLNodeList children;
xmlKeepBlanksDefault(0);
2013-03-24 09:55:56 -04:00
doc = xmlNewDoc(xml_version);
xmlSetDocCompressMode(doc, _compression);
writenode(doc, _root, doc->children, 1);
xmlDebugDumpDocument (out, doc);
xmlFreeDoc(doc);
#endif
}
const string&
XMLTree::write_buffer() const
{
static string retval;
char* ptr;
int len;
xmlDocPtr doc;
XMLNodeList children;
xmlKeepBlanksDefault(0);
2013-03-24 09:55:56 -04:00
doc = xmlNewDoc(xml_version);
xmlSetDocCompressMode(doc, _compression);
writenode(doc, _root, doc->children, 1);
xmlDocDumpMemory(doc, (xmlChar **) & ptr, &len);
xmlFreeDoc(doc);
retval = ptr;
free(ptr);
return retval;
}
Use std::vector::reserve to improve performance of adding properties The number of properties per node roughly corresponds to the number of members of the class the node is representing and should be fairly low. Use std::vector::reserve to prevent reallocation on insert for most node types, there are exceptions like Region(~40 properties). This seems worth it as part(maybe 1/10th of the total time) of saving a Session is a combination of what occurs in "Create" and "Write" in this test. Perf results before changes: XMLTest::testPerfMediumXMLDocumentTiming Create : Count: 10 Min: 30610 Max: 42656 Total: 376672 Avg: 37667 (37 msecs) Write : Count: 10 Min: 42804 Max: 54277 Total: 460455 Avg: 46045 (46 msecs) Read : Count: 10 Min: 70364 Max: 85484 Total: 750909 Avg: 75090 (75 msecs) XMLTest::testPerfLargeXMLDocumentTiming Create : Count: 10 Min: 164360 Max: 356995 Total: 3064482 Avg: 306448 (306 msecs) Write : Count: 10 Min: 308655 Max: 372953 Total: 3226707 Avg: 322670 (322 msecs) Read : Count: 10 Min: 517243 Max: 541839 Total: 5289950 Avg: 528995 (528 msecs) Perf results after changes: XMLTest::testPerfMediumXMLDocumentTiming Create : Count: 10 Min: 30375 Max: 48253 Total: 431727 Avg: 43172 (43 msecs) Write : Count: 10 Min: 42553 Max: 49163 Total: 453353 Avg: 45335 (45 msecs) Read : Count: 10 Min: 70307 Max: 75987 Total: 734923 Avg: 73492 (73 msecs) XMLTest::testPerfLargeXMLDocumentTiming Create : Count: 10 Min: 154486 Max: 307856 Total: 2678989 Avg: 267898 (267 msecs) Write : Count: 10 Min: 304273 Max: 343274 Total: 3169158 Avg: 316915 (316 msecs) Read : Count: 10 Min: 496920 Max: 541394 Total: 5260410 Avg: 526041 (526 msecs)
2016-09-23 09:01:18 -04:00
static const int PROPERTY_RESERVE_COUNT = 16;
XMLNode::XMLNode(const string& n)
: _name(n)
, _is_content(false)
{
Use std::vector::reserve to improve performance of adding properties The number of properties per node roughly corresponds to the number of members of the class the node is representing and should be fairly low. Use std::vector::reserve to prevent reallocation on insert for most node types, there are exceptions like Region(~40 properties). This seems worth it as part(maybe 1/10th of the total time) of saving a Session is a combination of what occurs in "Create" and "Write" in this test. Perf results before changes: XMLTest::testPerfMediumXMLDocumentTiming Create : Count: 10 Min: 30610 Max: 42656 Total: 376672 Avg: 37667 (37 msecs) Write : Count: 10 Min: 42804 Max: 54277 Total: 460455 Avg: 46045 (46 msecs) Read : Count: 10 Min: 70364 Max: 85484 Total: 750909 Avg: 75090 (75 msecs) XMLTest::testPerfLargeXMLDocumentTiming Create : Count: 10 Min: 164360 Max: 356995 Total: 3064482 Avg: 306448 (306 msecs) Write : Count: 10 Min: 308655 Max: 372953 Total: 3226707 Avg: 322670 (322 msecs) Read : Count: 10 Min: 517243 Max: 541839 Total: 5289950 Avg: 528995 (528 msecs) Perf results after changes: XMLTest::testPerfMediumXMLDocumentTiming Create : Count: 10 Min: 30375 Max: 48253 Total: 431727 Avg: 43172 (43 msecs) Write : Count: 10 Min: 42553 Max: 49163 Total: 453353 Avg: 45335 (45 msecs) Read : Count: 10 Min: 70307 Max: 75987 Total: 734923 Avg: 73492 (73 msecs) XMLTest::testPerfLargeXMLDocumentTiming Create : Count: 10 Min: 154486 Max: 307856 Total: 2678989 Avg: 267898 (267 msecs) Write : Count: 10 Min: 304273 Max: 343274 Total: 3169158 Avg: 316915 (316 msecs) Read : Count: 10 Min: 496920 Max: 541394 Total: 5260410 Avg: 526041 (526 msecs)
2016-09-23 09:01:18 -04:00
_proplist.reserve (PROPERTY_RESERVE_COUNT);
}
XMLNode::XMLNode(const string& n, const string& c)
: _name(n)
, _is_content(true)
, _content(c)
{
Use std::vector::reserve to improve performance of adding properties The number of properties per node roughly corresponds to the number of members of the class the node is representing and should be fairly low. Use std::vector::reserve to prevent reallocation on insert for most node types, there are exceptions like Region(~40 properties). This seems worth it as part(maybe 1/10th of the total time) of saving a Session is a combination of what occurs in "Create" and "Write" in this test. Perf results before changes: XMLTest::testPerfMediumXMLDocumentTiming Create : Count: 10 Min: 30610 Max: 42656 Total: 376672 Avg: 37667 (37 msecs) Write : Count: 10 Min: 42804 Max: 54277 Total: 460455 Avg: 46045 (46 msecs) Read : Count: 10 Min: 70364 Max: 85484 Total: 750909 Avg: 75090 (75 msecs) XMLTest::testPerfLargeXMLDocumentTiming Create : Count: 10 Min: 164360 Max: 356995 Total: 3064482 Avg: 306448 (306 msecs) Write : Count: 10 Min: 308655 Max: 372953 Total: 3226707 Avg: 322670 (322 msecs) Read : Count: 10 Min: 517243 Max: 541839 Total: 5289950 Avg: 528995 (528 msecs) Perf results after changes: XMLTest::testPerfMediumXMLDocumentTiming Create : Count: 10 Min: 30375 Max: 48253 Total: 431727 Avg: 43172 (43 msecs) Write : Count: 10 Min: 42553 Max: 49163 Total: 453353 Avg: 45335 (45 msecs) Read : Count: 10 Min: 70307 Max: 75987 Total: 734923 Avg: 73492 (73 msecs) XMLTest::testPerfLargeXMLDocumentTiming Create : Count: 10 Min: 154486 Max: 307856 Total: 2678989 Avg: 267898 (267 msecs) Write : Count: 10 Min: 304273 Max: 343274 Total: 3169158 Avg: 316915 (316 msecs) Read : Count: 10 Min: 496920 Max: 541394 Total: 5260410 Avg: 526041 (526 msecs)
2016-09-23 09:01:18 -04:00
_proplist.reserve (PROPERTY_RESERVE_COUNT);
}
XMLNode::XMLNode(const XMLNode& from)
{
Use std::vector::reserve to improve performance of adding properties The number of properties per node roughly corresponds to the number of members of the class the node is representing and should be fairly low. Use std::vector::reserve to prevent reallocation on insert for most node types, there are exceptions like Region(~40 properties). This seems worth it as part(maybe 1/10th of the total time) of saving a Session is a combination of what occurs in "Create" and "Write" in this test. Perf results before changes: XMLTest::testPerfMediumXMLDocumentTiming Create : Count: 10 Min: 30610 Max: 42656 Total: 376672 Avg: 37667 (37 msecs) Write : Count: 10 Min: 42804 Max: 54277 Total: 460455 Avg: 46045 (46 msecs) Read : Count: 10 Min: 70364 Max: 85484 Total: 750909 Avg: 75090 (75 msecs) XMLTest::testPerfLargeXMLDocumentTiming Create : Count: 10 Min: 164360 Max: 356995 Total: 3064482 Avg: 306448 (306 msecs) Write : Count: 10 Min: 308655 Max: 372953 Total: 3226707 Avg: 322670 (322 msecs) Read : Count: 10 Min: 517243 Max: 541839 Total: 5289950 Avg: 528995 (528 msecs) Perf results after changes: XMLTest::testPerfMediumXMLDocumentTiming Create : Count: 10 Min: 30375 Max: 48253 Total: 431727 Avg: 43172 (43 msecs) Write : Count: 10 Min: 42553 Max: 49163 Total: 453353 Avg: 45335 (45 msecs) Read : Count: 10 Min: 70307 Max: 75987 Total: 734923 Avg: 73492 (73 msecs) XMLTest::testPerfLargeXMLDocumentTiming Create : Count: 10 Min: 154486 Max: 307856 Total: 2678989 Avg: 267898 (267 msecs) Write : Count: 10 Min: 304273 Max: 343274 Total: 3169158 Avg: 316915 (316 msecs) Read : Count: 10 Min: 496920 Max: 541394 Total: 5260410 Avg: 526041 (526 msecs)
2016-09-23 09:01:18 -04:00
_proplist.reserve (PROPERTY_RESERVE_COUNT);
*this = from;
}
XMLNode::~XMLNode()
{
clear_lists ();
}
void
XMLNode::clear_lists ()
{
XMLNodeIterator curchild;
XMLPropertyIterator curprop;
_selected_children.clear ();
for (curchild = _children.begin(); curchild != _children.end(); ++curchild) {
delete *curchild;
}
_children.clear ();
for (curprop = _proplist.begin(); curprop != _proplist.end(); ++curprop) {
delete *curprop;
}
_proplist.clear ();
}
XMLNode&
XMLNode::operator= (const XMLNode& from)
{
if (&from == this) {
return *this;
}
clear_lists ();
2015-10-05 10:17:49 -04:00
_name = from.name ();
set_content (from.content ());
const XMLPropertyList& props = from.properties ();
2015-10-05 10:17:49 -04:00
for (XMLPropertyConstIterator prop_iter = props.begin (); prop_iter != props.end (); ++prop_iter) {
add_property ((*prop_iter)->name ().c_str (), (*prop_iter)->value ());
}
2015-10-05 10:17:49 -04:00
const XMLNodeList& nodes = from.children ();
for (XMLNodeConstIterator child_iter = nodes.begin (); child_iter != nodes.end (); ++child_iter) {
add_child_copy (**child_iter);
}
return *this;
}
bool
XMLNode::operator== (const XMLNode& other) const
{
if (is_content () != other.is_content ()) {
return false;
}
if (is_content ()) {
if (content () != other.content ()) {
return false;
}
} else {
if (name () != other.name ()) {
return false;
}
}
XMLPropertyList const& other_properties = other.properties ();
if (_proplist.size () != other_properties.size ()) {
return false;
}
XMLPropertyConstIterator our_prop_iter = _proplist.begin();
XMLPropertyConstIterator other_prop_iter = other_properties.begin();
while (our_prop_iter != _proplist.end ()) {
XMLProperty const* our_prop = *our_prop_iter;
XMLProperty const* other_prop = *other_prop_iter;
if (our_prop->name () != other_prop->name () || our_prop->value () != other_prop->value ()) {
return false;
}
++our_prop_iter;
++other_prop_iter;
}
XMLNodeList const& other_children = other.children();
if (_children.size() != other_children.size()) {
return false;
}
XMLNodeConstIterator our_child_iter = _children.begin ();
XMLNodeConstIterator other_child_iter = other_children.begin ();
while (our_child_iter != _children.end()) {
XMLNode const* our_child = *our_child_iter;
XMLNode const* other_child = *other_child_iter;
if (*our_child != *other_child) {
return false;
}
++our_child_iter;
++other_child_iter;
}
return true;
}
bool
XMLNode::operator!= (const XMLNode& other) const
{
return !(*this == other);
}
const string&
XMLNode::set_content(const string& c)
{
if (c.empty()) {
_is_content = false;
} else {
_is_content = true;
}
_content = c;
return _content;
}
XMLNode*
XMLNode::child (const char* name) const
{
/* returns first child matching name */
XMLNodeConstIterator cur;
if (name == 0) {
return 0;
}
for (cur = _children.begin(); cur != _children.end(); ++cur) {
if ((*cur)->name() == name) {
return *cur;
}
}
return 0;
}
const XMLNodeList&
XMLNode::children(const string& n) const
{
/* returns all children matching name */
XMLNodeConstIterator cur;
if (n.empty()) {
return _children;
}
_selected_children.clear();
for (cur = _children.begin(); cur != _children.end(); ++cur) {
if ((*cur)->name() == n) {
_selected_children.insert(_selected_children.end(), *cur);
}
}
return _selected_children;
}
XMLNode*
XMLNode::add_child(const char* n)
{
return add_child_copy(XMLNode (n));
}
void
XMLNode::add_child_nocopy(XMLNode& n)
{
_children.insert(_children.end(), &n);
}
XMLNode*
XMLNode::add_child_copy(const XMLNode& n)
{
XMLNode *copy = new XMLNode(n);
_children.insert(_children.end(), copy);
return copy;
}
boost::shared_ptr<XMLSharedNodeList>
XMLTree::find(const string xpath, XMLNode* node) const
{
xmlXPathContext* ctxt;
xmlDocPtr doc = 0;
if (node) {
2013-03-24 09:55:56 -04:00
doc = xmlNewDoc(xml_version);
writenode(doc, node, doc->children, 1);
ctxt = xmlXPathNewContext(doc);
} else {
ctxt = xmlXPathNewContext(_doc);
}
2015-10-05 10:17:49 -04:00
boost::shared_ptr<XMLSharedNodeList> result =
boost::shared_ptr<XMLSharedNodeList>(find_impl(ctxt, xpath));
2015-10-05 10:17:49 -04:00
xmlXPathFreeContext(ctxt);
if (doc) {
xmlFreeDoc (doc);
}
return result;
}
std::string
XMLNode::attribute_value()
{
XMLNodeList children = this->children();
assert(!_is_content);
assert(children.size() == 1);
XMLNode* child = *(children.begin());
assert(child->is_content());
return child->content();
}
XMLNode*
XMLNode::add_content(const string& c)
{
return add_child_copy(XMLNode (string(), c));
}
XMLProperty const *
XMLNode::property(const char* name) const
{
XMLPropertyConstIterator iter = _proplist.begin();
while (iter != _proplist.end()) {
if ((*iter)->name() == name) {
return *iter;
}
++iter;
}
return 0;
}
XMLProperty const *
XMLNode::property(const string& name) const
{
XMLPropertyConstIterator iter = _proplist.begin();
while (iter != _proplist.end()) {
if ((*iter)->name() == name) {
return *iter;
}
++iter;
}
return 0;
}
XMLProperty *
XMLNode::property(const char* name)
{
XMLPropertyIterator iter = _proplist.begin();
while (iter != _proplist.end()) {
if ((*iter)->name() == name) {
return *iter;
}
++iter;
}
return 0;
}
XMLProperty *
XMLNode::property(const string& name)
{
XMLPropertyIterator iter = _proplist.begin();
while (iter != _proplist.end()) {
if ((*iter)->name() == name) {
return *iter;
}
++iter;
}
return 0;
}
bool
XMLNode::has_property_with_value (const string& name, const string& value) const
{
XMLPropertyConstIterator iter = _proplist.begin();
while (iter != _proplist.end()) {
if ((*iter)->name() == name && (*iter)->value() == value) {
return true;
}
++iter;
}
return false;
}
XMLProperty*
XMLNode::add_property(const char* name, const string& value)
{
XMLPropertyIterator iter = _proplist.begin();
2015-10-05 10:17:49 -04:00
while (iter != _proplist.end()) {
if ((*iter)->name() == name) {
(*iter)->set_value (value);
return *iter;
}
++iter;
}
XMLProperty* new_property = new XMLProperty(name, value);
if (!new_property) {
return 0;
}
_proplist.insert(_proplist.end(), new_property);
return new_property;
}
XMLProperty*
XMLNode::add_property(const char* n, const char* v)
{
string vs(v);
return add_property(n, vs);
}
XMLProperty*
XMLNode::add_property(const char* name, const long value)
{
char str[64];
snprintf(str, sizeof(str), "%ld", value);
return add_property(name, str);
}
void
XMLNode::remove_property(const string& name)
{
XMLPropertyIterator iter = _proplist.begin();
while (iter != _proplist.end()) {
if ((*iter)->name() == name) {
XMLProperty* property = *iter;
_proplist.erase (iter);
delete property;
break;
}
++iter;
}
}
/** Remove any property with the given name from this node and its children */
void
XMLNode::remove_property_recursively(const string& n)
{
remove_property (n);
for (XMLNodeIterator i = _children.begin(); i != _children.end(); ++i) {
(*i)->remove_property_recursively (n);
}
}
void
XMLNode::remove_nodes(const string& n)
{
XMLNodeIterator i = _children.begin();
while (i != _children.end()) {
if ((*i)->name() == n) {
i = _children.erase (i);
} else {
++i;
}
}
}
void
XMLNode::remove_nodes_and_delete(const string& n)
{
XMLNodeIterator i = _children.begin();
while (i != _children.end()) {
if ((*i)->name() == n) {
delete *i;
i = _children.erase (i);
} else {
++i;
}
}
}
void
XMLNode::remove_nodes_and_delete(const string& propname, const string& val)
{
XMLNodeIterator i = _children.begin();
XMLProperty const * prop;
while (i != _children.end()) {
prop = (*i)->property(propname);
if (prop && prop->value() == val) {
delete *i;
i = _children.erase(i);
} else {
++i;
}
}
}
XMLProperty::XMLProperty(const string& n, const string& v)
: _name(n)
, _value(v)
{
// Normalize property name (replace '_' with '-' as old session are inconsistent)
for (size_t i = 0; i < _name.length(); ++i) {
if (_name[i] == '_') {
_name[i] = '-';
}
}
}
XMLProperty::~XMLProperty()
{
}
static XMLNode*
readnode(xmlNodePtr node)
{
string name, content;
xmlNodePtr child;
XMLNode* tmp;
xmlAttrPtr attr;
if (node->name) {
2013-03-24 09:55:56 -04:00
name = (const char*)node->name;
}
tmp = new XMLNode(name);
for (attr = node->properties; attr; attr = attr->next) {
content = "";
if (attr->children) {
content = (char*)attr->children->content;
}
2013-03-24 09:55:56 -04:00
tmp->add_property((const char*)attr->name, content);
}
if (node->content) {
tmp->set_content((char*)node->content);
} else {
tmp->set_content(string());
}
for (child = node->children; child; child = child->next) {
tmp->add_child_nocopy (*readnode(child));
}
return tmp;
}
static void
writenode(xmlDocPtr doc, XMLNode* n, xmlNodePtr p, int root = 0)
{
xmlNodePtr node;
if (root) {
2013-03-24 09:55:56 -04:00
node = doc->children = xmlNewDocNode(doc, 0, (const xmlChar*) n->name().c_str(), 0);
} else {
2013-03-24 09:55:56 -04:00
node = xmlNewChild(p, 0, (const xmlChar*) n->name().c_str(), 0);
}
if (n->is_content()) {
node->type = XML_TEXT_NODE;
xmlNodeSetContentLen(node, (const xmlChar*)n->content().c_str(), n->content().length());
}
const XMLPropertyList& props = n->properties();
for (XMLPropertyConstIterator prop_iter = props.begin (); prop_iter != props.end ();
++prop_iter) {
xmlSetProp (node, (const xmlChar*)(*prop_iter)->name ().c_str (),
(const xmlChar*)(*prop_iter)->value ().c_str ());
}
const XMLNodeList& children = n->children ();
for (XMLNodeConstIterator child_iter = children.begin (); child_iter != children.end ();
++child_iter) {
writenode (doc, *child_iter, node);
}
}
static XMLSharedNodeList* find_impl(xmlXPathContext* ctxt, const string& xpath)
{
xmlXPathObject* result = xmlXPathEval((const xmlChar*)xpath.c_str(), ctxt);
if (!result) {
xmlXPathFreeContext(ctxt);
xmlFreeDoc(ctxt->doc);
throw XMLException("Invalid XPath: " + xpath);
}
if (result->type != XPATH_NODESET) {
xmlXPathFreeObject(result);
xmlXPathFreeContext(ctxt);
xmlFreeDoc(ctxt->doc);
throw XMLException("Only nodeset result types are supported.");
}
xmlNodeSet* nodeset = result->nodesetval;
XMLSharedNodeList* nodes = new XMLSharedNodeList();
if (nodeset) {
for (int i = 0; i < nodeset->nodeNr; ++i) {
XMLNode* node = readnode(nodeset->nodeTab[i]);
nodes->push_back(boost::shared_ptr<XMLNode>(node));
}
} else {
// return empty set
}
xmlXPathFreeObject(result);
return nodes;
}
/** Dump a node, its properties and children to a stream */
void
XMLNode::dump (ostream& s, string p) const
{
if (_is_content) {
s << p << " " << content() << "\n";
} else {
s << p << "<" << _name;
for (XMLPropertyList::const_iterator i = _proplist.begin(); i != _proplist.end(); ++i) {
s << " " << (*i)->name() << "=\"" << (*i)->value() << "\"";
}
s << ">\n";
2015-10-05 10:17:49 -04:00
for (XMLNodeList::const_iterator i = _children.begin(); i != _children.end(); ++i) {
(*i)->dump (s, p + " ");
}
2015-10-05 10:17:49 -04:00
s << p << "</" << _name << ">\n";
}
}