2 * libxml++ and this file are copyright (C) 2000 by Ari Johnson, and
3 * are covered by the GNU Lesser General Public License, which should be
4 * included with libxml++ as the file COPYING.
5 * Modified for Ardour and released under the same terms.
9 #include <libxml/debugXML.h>
10 #include <libxml/xpath.h>
11 #include <libxml/xpathInternals.h>
13 #define XML_VERSION "1.0"
17 static XMLNode* readnode(xmlNodePtr);
18 static void writenode(xmlDocPtr, XMLNode*, xmlNodePtr, int);
19 static XMLSharedNodeList* find_impl(xmlXPathContext* ctxt, const string& xpath);
28 XMLTree::XMLTree(const string& fn, bool validate)
33 read_internal(validate);
36 XMLTree::XMLTree(const XMLTree* from)
37 : _filename(from->filename())
38 , _root(new XMLNode(*from->root()))
39 , _compression(from->compression())
49 XMLTree::set_compression(int c)
63 XMLTree::read_internal(bool validate)
65 //shouldnt be used anywhere ATM, remove if so!
71 xmlParserCtxtPtr ctxt = NULL; /* the parser context */
72 xmlDocPtr doc; /* the resulting document tree */
74 xmlKeepBlanksDefault(0);
75 /* parse the file, activating the DTD validation option */
77 /* create a parser context */
78 ctxt = xmlNewParserCtxt();
82 doc = xmlCtxtReadFile(ctxt, _filename.c_str(), NULL, XML_PARSE_DTDVALID);
84 doc = xmlParseFile(_filename.c_str());
87 /* check if parsing suceeded */
90 xmlFreeParserCtxt(ctxt);
94 /* check if validation suceeded */
95 if (validate && ctxt->valid == 0) {
96 xmlFreeParserCtxt(ctxt);
99 throw XMLException("Failed to validate document " + _filename);
103 _root = readnode(xmlDocGetRootElement(doc));
105 /* free up the parser context */
107 xmlFreeParserCtxt(ctxt);
116 XMLTree::read_buffer(const string& buffer)
125 doc = xmlParseMemory((char*)buffer.c_str(), buffer.length());
130 _root = readnode(xmlDocGetRootElement(doc));
138 XMLTree::write() const
141 XMLNodeList children;
144 xmlKeepBlanksDefault(0);
145 doc = xmlNewDoc((xmlChar*) XML_VERSION);
146 xmlSetDocCompressMode(doc, _compression);
147 writenode(doc, _root, doc->children, 1);
148 result = xmlSaveFormatFileEnc(_filename.c_str(), doc, "UTF-8", 1);
159 XMLTree::debug(FILE* out) const
162 XMLNodeList children;
164 xmlKeepBlanksDefault(0);
165 doc = xmlNewDoc((xmlChar*) XML_VERSION);
166 xmlSetDocCompressMode(doc, _compression);
167 writenode(doc, _root, doc->children, 1);
168 xmlDebugDumpDocument (out, doc);
173 XMLTree::write_buffer() const
175 static string retval;
179 XMLNodeList children;
181 xmlKeepBlanksDefault(0);
182 doc = xmlNewDoc((xmlChar*) XML_VERSION);
183 xmlSetDocCompressMode(doc, _compression);
184 writenode(doc, _root, doc->children, 1);
185 xmlDocDumpMemory(doc, (xmlChar **) & ptr, &len);
195 XMLNode::XMLNode(const string& n)
201 XMLNode::XMLNode(const string& n, const string& c)
208 XMLNode::XMLNode(const XMLNode& from)
210 XMLPropertyList props;
211 XMLPropertyIterator curprop;
213 XMLNodeIterator curnode;
216 set_content(from.content());
218 props = from.properties();
219 for (curprop = props.begin(); curprop != props.end(); ++curprop) {
220 add_property((*curprop)->name().c_str(), (*curprop)->value());
223 nodes = from.children();
224 for (curnode = nodes.begin(); curnode != nodes.end(); ++curnode) {
225 add_child_copy(**curnode);
231 XMLNodeIterator curchild;
232 XMLPropertyIterator curprop;
234 for (curchild = _children.begin(); curchild != _children.end(); ++curchild) {
238 for (curprop = _proplist.begin(); curprop != _proplist.end(); ++curprop) {
244 XMLNode::set_content(const string& c)
258 XMLNode::child (const char* name) const
260 /* returns first child matching name */
262 XMLNodeConstIterator cur;
268 for (cur = _children.begin(); cur != _children.end(); ++cur) {
269 if ((*cur)->name() == name) {
278 XMLNode::children(const string& n) const
280 /* returns all children matching name */
282 XMLNodeConstIterator cur;
288 _selected_children.clear();
290 for (cur = _children.begin(); cur != _children.end(); ++cur) {
291 if ((*cur)->name() == n) {
292 _selected_children.insert(_selected_children.end(), *cur);
296 return _selected_children;
300 XMLNode::add_child(const char* n)
302 return add_child_copy(XMLNode (n));
306 XMLNode::add_child_nocopy(XMLNode& n)
308 _children.insert(_children.end(), &n);
312 XMLNode::add_child_copy(const XMLNode& n)
314 XMLNode *copy = new XMLNode(n);
315 _children.insert(_children.end(), copy);
319 boost::shared_ptr<XMLSharedNodeList>
320 XMLNode::find(const string xpath) const
322 xmlDocPtr doc = xmlNewDoc((xmlChar*) XML_VERSION);
323 writenode(doc, (XMLNode*)this, doc->children, 1);
324 xmlXPathContext* ctxt = xmlXPathNewContext(doc);
326 boost::shared_ptr<XMLSharedNodeList> result =
327 boost::shared_ptr<XMLSharedNodeList>(find_impl(ctxt, xpath));
329 xmlXPathFreeContext(ctxt);
336 XMLNode::attribute_value()
338 XMLNodeList children = this->children();
339 assert(!_is_content);
340 assert(children.size() == 1);
341 XMLNode* child = *(children.begin());
342 assert(child->is_content());
343 return child->content();
347 XMLNode::add_content(const string& c)
349 return add_child_copy(XMLNode (string(), c));
353 XMLNode::property(const char* n)
356 map<string,XMLProperty*>::iterator iter;
358 if ((iter = _propmap.find(ns)) != _propmap.end()) {
366 XMLNode::property(const string& ns)
368 map<string,XMLProperty*>::iterator iter;
370 if ((iter = _propmap.find(ns)) != _propmap.end()) {
378 XMLNode::add_property(const char* n, const string& v)
381 if (_propmap.find(ns) != _propmap.end()) {
385 XMLProperty* tmp = new XMLProperty(ns, v);
391 _propmap[tmp->name()] = tmp;
392 _proplist.insert(_proplist.end(), tmp);
398 XMLNode::add_property(const char* n, const char* v)
401 return add_property(n, vs);
405 XMLNode::add_property(const char* name, const long value)
407 static char str[1024];
408 snprintf(str, 1024, "%ld", value);
409 return add_property(name, str);
413 XMLNode::remove_property(const string& n)
415 if (_propmap.find(n) != _propmap.end()) {
416 XMLProperty* p = _propmap[n];
417 _proplist.remove (p);
424 XMLNode::remove_nodes(const string& n)
426 XMLNodeIterator i = _children.begin();
429 while (i != _children.end()) {
432 if ((*i)->name() == n) {
440 XMLNode::remove_nodes_and_delete(const string& n)
442 XMLNodeIterator i = _children.begin();
445 while (i != _children.end()) {
448 if ((*i)->name() == n) {
457 XMLNode::remove_nodes_and_delete(const string& propname, const string& val)
459 XMLNodeIterator i = _children.begin();
463 while (i != _children.end()) {
467 prop = (*i)->property(propname);
468 if (prop && prop->value() == val) {
477 XMLProperty::XMLProperty(const string& n, const string& v)
481 // Normalize property name (replace '_' with '-' as old session are inconsistent)
482 for (size_t i = 0; i < _name.length(); ++i) {
483 if (_name[i] == '_') {
489 XMLProperty::~XMLProperty()
494 readnode(xmlNodePtr node)
496 string name, content;
502 name = (char*)node->name;
505 tmp = new XMLNode(name);
507 for (attr = node->properties; attr; attr = attr->next) {
509 if (attr->children) {
510 content = (char*)attr->children->content;
512 tmp->add_property((char*)attr->name, content);
516 tmp->set_content((char*)node->content);
518 tmp->set_content(string());
521 for (child = node->children; child; child = child->next) {
522 tmp->add_child_nocopy (*readnode(child));
529 writenode(xmlDocPtr doc, XMLNode* n, xmlNodePtr p, int root = 0)
531 XMLPropertyList props;
532 XMLPropertyIterator curprop;
533 XMLNodeList children;
534 XMLNodeIterator curchild;
538 node = doc->children = xmlNewDocNode(doc, 0, (xmlChar*) n->name().c_str(), 0);
540 node = xmlNewChild(p, 0, (xmlChar*) n->name().c_str(), 0);
543 if (n->is_content()) {
544 node->type = XML_TEXT_NODE;
545 xmlNodeSetContentLen(node, (const xmlChar*)n->content().c_str(), n->content().length());
548 props = n->properties();
549 for (curprop = props.begin(); curprop != props.end(); ++curprop) {
550 xmlSetProp(node, (xmlChar*) (*curprop)->name().c_str(), (xmlChar*) (*curprop)->value().c_str());
553 children = n->children();
554 for (curchild = children.begin(); curchild != children.end(); ++curchild) {
555 writenode(doc, *curchild, node);
559 static XMLSharedNodeList* find_impl(xmlXPathContext* ctxt, const string& xpath)
561 xmlXPathObject* result = xmlXPathEval((const xmlChar*)xpath.c_str(), ctxt);
564 xmlXPathFreeContext(ctxt);
565 xmlFreeDoc(ctxt->doc);
567 throw XMLException("Invalid XPath: " + xpath);
570 if (result->type != XPATH_NODESET) {
571 xmlXPathFreeObject(result);
572 xmlXPathFreeContext(ctxt);
573 xmlFreeDoc(ctxt->doc);
575 throw XMLException("Only nodeset result types are supported.");
578 xmlNodeSet* nodeset = result->nodesetval;
579 XMLSharedNodeList* nodes = new XMLSharedNodeList();
581 for (int i = 0; i < nodeset->nodeNr; ++i) {
582 XMLNode* node = readnode(nodeset->nodeTab[i]);
583 nodes->push_back(boost::shared_ptr<XMLNode>(node));
589 xmlXPathFreeObject(result);