Make Bundle::connected_to() optionally check for exclusivity
[ardour.git] / libs / pbd / xml++.cc
1 /* xml++.cc
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.
6  */
7
8 #include <iostream>
9
10 #include "pbd/stacktrace.h"
11 #include "pbd/xml++.h"
12
13 #include <libxml/debugXML.h>
14 #include <libxml/xpath.h>
15 #include <libxml/xpathInternals.h>
16
17 xmlChar* xml_version = xmlCharStrdup("1.0");
18
19 using namespace std;
20
21 static XMLNode*           readnode(xmlNodePtr);
22 static void               writenode(xmlDocPtr, XMLNode*, xmlNodePtr, int);
23 static XMLSharedNodeList* find_impl(xmlXPathContext* ctxt, const string& xpath);
24
25 XMLTree::XMLTree()
26         : _filename()
27         , _root(0)
28         , _doc (0)
29         , _compression(0)
30 {
31 }
32
33 XMLTree::XMLTree(const string& fn, bool validate)
34         : _filename(fn)
35         , _root(0)
36         , _doc (0)
37         , _compression(0)
38 {
39         read_internal(validate);
40 }
41
42 XMLTree::XMLTree(const XMLTree* from)
43         : _filename(from->filename())
44         , _root(new XMLNode(*from->root()))
45         , _doc (xmlCopyDoc (from->_doc, 1))
46         , _compression(from->compression())
47 {
48
49 }
50
51 XMLTree::~XMLTree()
52 {
53         delete _root;
54
55         if (_doc) {
56                 xmlFreeDoc (_doc);
57         }
58 }
59
60 int
61 XMLTree::set_compression(int c)
62 {
63         if (c > 9) {
64                 c = 9;
65         } else if (c < 0) {
66                 c = 0;
67         }
68
69         _compression = c;
70
71         return _compression;
72 }
73
74 bool
75 XMLTree::read_internal(bool validate)
76 {
77         //shouldnt be used anywhere ATM, remove if so!
78         assert(!validate);
79
80         delete _root;
81         _root = 0;
82
83         if (_doc) {
84                 xmlFreeDoc (_doc);
85                 _doc = 0;
86         }
87
88         /* create a parser context */
89         xmlParserCtxtPtr ctxt = xmlNewParserCtxt();
90         if (ctxt == NULL) {
91                 return false;
92         }
93
94         xmlKeepBlanksDefault(0);
95         /* parse the file, activating the DTD validation option */
96         if (validate) {
97                 _doc = xmlCtxtReadFile(ctxt, _filename.c_str(), NULL, XML_PARSE_DTDVALID);
98         } else {
99                 _doc = xmlCtxtReadFile(ctxt, _filename.c_str(), NULL, XML_PARSE_HUGE);
100         }
101
102         /* check if parsing suceeded */
103         if (_doc == NULL) {
104                 xmlFreeParserCtxt(ctxt);
105                 return false;
106         } else {
107                 /* check if validation suceeded */
108                 if (validate && ctxt->valid == 0) {
109                         xmlFreeParserCtxt(ctxt);
110                         throw XMLException("Failed to validate document " + _filename);
111                 }
112         }
113
114         _root = readnode(xmlDocGetRootElement(_doc));
115
116         /* free up the parser context */
117         xmlFreeParserCtxt(ctxt);
118
119         return true;
120 }
121
122 bool
123 XMLTree::read_buffer(const string& buffer, bool to_tree_doc)
124 {
125         xmlDocPtr doc;
126
127         _filename = "";
128
129         delete _root;
130         _root = 0;
131
132         doc = xmlParseMemory(const_cast<char*>(buffer.c_str()), buffer.length());
133         if (!doc) {
134                 return false;
135         }
136
137         _root = readnode(xmlDocGetRootElement(doc));
138         if (to_tree_doc) {
139                 if (_doc) {
140                         xmlFreeDoc (_doc);
141                 }
142                 _doc = doc;
143         } else {
144                 xmlFreeDoc (doc);
145         }
146
147         return true;
148 }
149
150
151 bool
152 XMLTree::write() const
153 {
154         xmlDocPtr doc;
155         XMLNodeList children;
156         int result;
157
158         xmlKeepBlanksDefault(0);
159         doc = xmlNewDoc(xml_version);
160         xmlSetDocCompressMode(doc, _compression);
161         writenode(doc, _root, doc->children, 1);
162         result = xmlSaveFormatFileEnc(_filename.c_str(), doc, "UTF-8", 1);
163 #ifndef NDEBUG
164         if (result == -1) {
165                 xmlErrorPtr xerr = xmlGetLastError ();
166                 if (!xerr) {
167                         std::cerr << "unknown XML error during xmlSaveFormatFileEnc()." << std::endl;
168                 } else {
169                         std::cerr << "xmlSaveFormatFileEnc: error"
170                                 << " domain: " << xerr->domain
171                                 << " code: " << xerr->code
172                                 << " msg: " << xerr->message
173                                 << std::endl;
174                 }
175         }
176 #endif
177         xmlFreeDoc(doc);
178
179         if (result == -1) {
180                 return false;
181         }
182
183         return true;
184 }
185
186 void
187 XMLTree::debug(FILE* out) const
188 {
189 #ifdef LIBXML_DEBUG_ENABLED
190         xmlDocPtr doc;
191         XMLNodeList children;
192
193         xmlKeepBlanksDefault(0);
194         doc = xmlNewDoc(xml_version);
195         xmlSetDocCompressMode(doc, _compression);
196         writenode(doc, _root, doc->children, 1);
197         xmlDebugDumpDocument (out, doc);
198         xmlFreeDoc(doc);
199 #endif
200 }
201
202 const string&
203 XMLTree::write_buffer() const
204 {
205         static string retval;
206         char* ptr;
207         int len;
208         xmlDocPtr doc;
209         XMLNodeList children;
210
211         xmlKeepBlanksDefault(0);
212         doc = xmlNewDoc(xml_version);
213         xmlSetDocCompressMode(doc, _compression);
214         writenode(doc, _root, doc->children, 1);
215         xmlDocDumpMemory(doc, (xmlChar **) & ptr, &len);
216         xmlFreeDoc(doc);
217
218         retval = ptr;
219
220         free(ptr);
221
222         return retval;
223 }
224
225 static const int PROPERTY_RESERVE_COUNT = 16;
226
227 XMLNode::XMLNode(const string& n)
228         : _name(n)
229         , _is_content(false)
230 {
231         _proplist.reserve (PROPERTY_RESERVE_COUNT);
232 }
233
234 XMLNode::XMLNode(const string& n, const string& c)
235         : _name(n)
236         , _is_content(true)
237         , _content(c)
238 {
239         _proplist.reserve (PROPERTY_RESERVE_COUNT);
240 }
241
242 XMLNode::XMLNode(const XMLNode& from)
243 {
244         _proplist.reserve (PROPERTY_RESERVE_COUNT);
245         *this = from;
246 }
247
248 XMLNode::~XMLNode()
249 {
250         clear_lists ();
251 }
252
253 void
254 XMLNode::clear_lists ()
255 {
256         XMLNodeIterator curchild;
257         XMLPropertyIterator curprop;
258
259         _selected_children.clear ();
260
261         for (curchild = _children.begin(); curchild != _children.end(); ++curchild) {
262                 delete *curchild;
263         }
264
265         _children.clear ();
266
267         for (curprop = _proplist.begin(); curprop != _proplist.end(); ++curprop) {
268                 delete *curprop;
269         }
270
271         _proplist.clear ();
272 }
273
274 XMLNode&
275 XMLNode::operator= (const XMLNode& from)
276 {
277         if (&from == this) {
278                 return *this;
279         }
280
281         clear_lists ();
282
283         _name = from.name ();
284         set_content (from.content ());
285
286         const XMLPropertyList& props = from.properties ();
287
288         for (XMLPropertyConstIterator prop_iter = props.begin (); prop_iter != props.end (); ++prop_iter) {
289                 set_property ((*prop_iter)->name ().c_str (), (*prop_iter)->value ());
290         }
291
292         const XMLNodeList& nodes = from.children ();
293         for (XMLNodeConstIterator child_iter = nodes.begin (); child_iter != nodes.end (); ++child_iter) {
294                 add_child_copy (**child_iter);
295         }
296
297         return *this;
298 }
299
300 bool
301 XMLNode::operator== (const XMLNode& other) const
302 {
303         if (is_content () != other.is_content ()) {
304                 return false;
305         }
306
307         if (is_content ()) {
308                 if (content () != other.content ()) {
309                         return false;
310                 }
311         } else {
312                 if (name () != other.name ()) {
313                         return false;
314                 }
315         }
316
317         XMLPropertyList const& other_properties = other.properties ();
318
319         if (_proplist.size () != other_properties.size ()) {
320                 return false;
321         }
322
323         XMLPropertyConstIterator our_prop_iter = _proplist.begin();
324         XMLPropertyConstIterator other_prop_iter = other_properties.begin();
325
326         while (our_prop_iter != _proplist.end ()) {
327                 XMLProperty const* our_prop = *our_prop_iter;
328                 XMLProperty const* other_prop = *other_prop_iter;
329                 if (our_prop->name () != other_prop->name () || our_prop->value () != other_prop->value ()) {
330                         return false;
331                 }
332                 ++our_prop_iter;
333                 ++other_prop_iter;
334         }
335
336         XMLNodeList const& other_children = other.children();
337
338         if (_children.size() != other_children.size()) {
339                 return false;
340         }
341
342         XMLNodeConstIterator our_child_iter = _children.begin ();
343         XMLNodeConstIterator other_child_iter = other_children.begin ();
344
345         while (our_child_iter != _children.end()) {
346                 XMLNode const* our_child = *our_child_iter;
347                 XMLNode const* other_child = *other_child_iter;
348
349                 if (*our_child != *other_child) {
350                         return false;
351                 }
352                 ++our_child_iter;
353                 ++other_child_iter;
354         }
355         return true;
356 }
357
358 bool
359 XMLNode::operator!= (const XMLNode& other) const
360 {
361         return !(*this == other);
362 }
363
364 const string&
365 XMLNode::set_content(const string& c)
366 {
367         if (c.empty()) {
368                 _is_content = false;
369         } else {
370                 _is_content = true;
371         }
372
373         _content = c;
374
375         return _content;
376 }
377
378 XMLNode*
379 XMLNode::child (const char* name) const
380 {
381         /* returns first child matching name */
382
383         XMLNodeConstIterator cur;
384
385         if (name == 0) {
386                 return 0;
387         }
388
389         for (cur = _children.begin(); cur != _children.end(); ++cur) {
390                 if ((*cur)->name() == name) {
391                         return *cur;
392                 }
393         }
394
395         return 0;
396 }
397
398 const XMLNodeList&
399 XMLNode::children(const string& n) const
400 {
401         /* returns all children matching name */
402
403         XMLNodeConstIterator cur;
404
405         if (n.empty()) {
406                 return _children;
407         }
408
409         _selected_children.clear();
410
411         for (cur = _children.begin(); cur != _children.end(); ++cur) {
412                 if ((*cur)->name() == n) {
413                         _selected_children.insert(_selected_children.end(), *cur);
414                 }
415         }
416
417         return _selected_children;
418 }
419
420 XMLNode*
421 XMLNode::add_child(const char* n)
422 {
423         return add_child_copy(XMLNode (n));
424 }
425
426 void
427 XMLNode::add_child_nocopy(XMLNode& n)
428 {
429         _children.insert(_children.end(), &n);
430 }
431
432 XMLNode*
433 XMLNode::add_child_copy(const XMLNode& n)
434 {
435         XMLNode *copy = new XMLNode(n);
436         _children.insert(_children.end(), copy);
437         return copy;
438 }
439
440 boost::shared_ptr<XMLSharedNodeList>
441 XMLTree::find(const string xpath, XMLNode* node) const
442 {
443         xmlXPathContext* ctxt;
444         xmlDocPtr doc = 0;
445
446         if (node) {
447                 doc = xmlNewDoc(xml_version);
448                 writenode(doc, node, doc->children, 1);
449                 ctxt = xmlXPathNewContext(doc);
450         } else {
451                 ctxt = xmlXPathNewContext(_doc);
452         }
453
454         boost::shared_ptr<XMLSharedNodeList> result =
455                 boost::shared_ptr<XMLSharedNodeList>(find_impl(ctxt, xpath));
456
457         xmlXPathFreeContext(ctxt);
458         if (doc) {
459                 xmlFreeDoc (doc);
460         }
461
462         return result;
463 }
464
465 std::string
466 XMLNode::attribute_value()
467 {
468         XMLNodeList children = this->children();
469         assert(!_is_content);
470         assert(children.size() == 1);
471         XMLNode* child = *(children.begin());
472         assert(child->is_content());
473         return child->content();
474 }
475
476 XMLNode*
477 XMLNode::add_content(const string& c)
478 {
479         return add_child_copy(XMLNode (string(), c));
480 }
481
482 XMLProperty const *
483 XMLNode::property(const char* name) const
484 {
485         XMLPropertyConstIterator iter = _proplist.begin();
486
487         while (iter != _proplist.end()) {
488                 if ((*iter)->name() == name) {
489                         return *iter;
490                 }
491                 ++iter;
492         }
493
494         return 0;
495 }
496
497 XMLProperty const *
498 XMLNode::property(const string& name) const
499 {
500         XMLPropertyConstIterator iter = _proplist.begin();
501
502         while (iter != _proplist.end()) {
503                 if ((*iter)->name() == name) {
504                         return *iter;
505                 }
506                 ++iter;
507         }
508         return 0;
509 }
510
511 XMLProperty *
512 XMLNode::property(const char* name)
513 {
514         XMLPropertyIterator iter = _proplist.begin();
515
516         while (iter != _proplist.end()) {
517                 if ((*iter)->name() == name) {
518                         return *iter;
519                 }
520                 ++iter;
521         }
522         return 0;
523 }
524
525 XMLProperty *
526 XMLNode::property(const string& name)
527 {
528         XMLPropertyIterator iter = _proplist.begin();
529
530         while (iter != _proplist.end()) {
531                 if ((*iter)->name() == name) {
532                         return *iter;
533                 }
534                 ++iter;
535         }
536
537         return 0;
538 }
539
540 bool
541 XMLNode::has_property_with_value (const string& name, const string& value) const
542 {
543         XMLPropertyConstIterator iter = _proplist.begin();
544
545         while (iter != _proplist.end()) {
546                 if ((*iter)->name() == name && (*iter)->value() == value) {
547                         return true;
548                 }
549                 ++iter;
550         }
551         return false;
552 }
553
554 bool
555 XMLNode::set_property(const char* name, const string& value)
556 {
557         XMLPropertyIterator iter = _proplist.begin();
558
559         while (iter != _proplist.end()) {
560                 if ((*iter)->name() == name) {
561                         (*iter)->set_value (value);
562                         return *iter;
563                 }
564                 ++iter;
565         }
566
567         XMLProperty* new_property = new XMLProperty(name, value);
568
569         if (!new_property) {
570                 return 0;
571         }
572
573         _proplist.insert(_proplist.end(), new_property);
574
575         return new_property;
576 }
577
578 bool
579 XMLNode::get_property(const char* name, std::string& value) const
580 {
581         XMLProperty const* const prop = property (name);
582         if (!prop)
583                 return false;
584
585         value = prop->value ();
586
587         return true;
588 }
589
590 void
591 XMLNode::remove_property(const string& name)
592 {
593         XMLPropertyIterator iter = _proplist.begin();
594
595         while (iter != _proplist.end()) {
596                 if ((*iter)->name() == name) {
597                         XMLProperty* property = *iter;
598                         _proplist.erase (iter);
599                         delete property;
600                         break;
601                 }
602                 ++iter;
603         }
604 }
605
606 /** Remove any property with the given name from this node and its children */
607 void
608 XMLNode::remove_property_recursively(const string& n)
609 {
610         remove_property (n);
611         for (XMLNodeIterator i = _children.begin(); i != _children.end(); ++i) {
612                 (*i)->remove_property_recursively (n);
613         }
614 }
615
616 void
617 XMLNode::remove_nodes(const string& n)
618 {
619         XMLNodeIterator i = _children.begin();
620         while (i != _children.end()) {
621                 if ((*i)->name() == n) {
622                         i = _children.erase (i);
623                 } else {
624                         ++i;
625                 }
626         }
627 }
628
629 void
630 XMLNode::remove_nodes_and_delete(const string& n)
631 {
632         XMLNodeIterator i = _children.begin();
633
634         while (i != _children.end()) {
635                 if ((*i)->name() == n) {
636                         delete *i;
637                         i = _children.erase (i);
638                 } else {
639                         ++i;
640                 }
641         }
642 }
643
644 void
645 XMLNode::remove_nodes_and_delete(const string& propname, const string& val)
646 {
647         XMLNodeIterator i = _children.begin();
648         XMLProperty const * prop;
649
650         while (i != _children.end()) {
651                 prop = (*i)->property(propname);
652                 if (prop && prop->value() == val) {
653                         delete *i;
654                         i = _children.erase(i);
655                 } else {
656                         ++i;
657                 }
658         }
659 }
660
661 void
662 XMLNode::remove_node_and_delete(const string& n, const string& propname,
663  const string& val)
664 {
665         for (XMLNodeIterator i = _children.begin(); i != _children.end(); ++i) {
666                 if ((*i)->name() == n) {
667                         XMLProperty const * prop = (*i)->property (propname);
668                         if (prop && prop->value() == val) {
669                                 delete *i;
670                                 _children.erase (i);
671                                 break;
672                         }
673                 }
674         }
675 }
676
677 XMLProperty::XMLProperty(const string& n, const string& v)
678         : _name(n)
679         , _value(v)
680 {
681 }
682
683 XMLProperty::~XMLProperty()
684 {
685 }
686
687 static XMLNode*
688 readnode(xmlNodePtr node)
689 {
690         string name, content;
691         xmlNodePtr child;
692         XMLNode* tmp;
693         xmlAttrPtr attr;
694
695         if (node->name) {
696                 name = (const char*)node->name;
697         }
698
699         tmp = new XMLNode(name);
700
701         for (attr = node->properties; attr; attr = attr->next) {
702                 content = "";
703                 if (attr->children) {
704                         content = (char*)attr->children->content;
705                 }
706                 tmp->set_property((const char*)attr->name, content);
707         }
708
709         if (node->content) {
710                 tmp->set_content((char*)node->content);
711         } else {
712                 tmp->set_content(string());
713         }
714
715         for (child = node->children; child; child = child->next) {
716                 tmp->add_child_nocopy (*readnode(child));
717         }
718
719         return tmp;
720 }
721
722 static void
723 writenode(xmlDocPtr doc, XMLNode* n, xmlNodePtr p, int root = 0)
724 {
725         xmlNodePtr node;
726
727         if (root) {
728                 node = doc->children = xmlNewDocNode(doc, 0, (const xmlChar*) n->name().c_str(), 0);
729         } else {
730                 node = xmlNewChild(p, 0, (const xmlChar*) n->name().c_str(), 0);
731         }
732
733         if (n->is_content()) {
734                 node->type = XML_TEXT_NODE;
735                 xmlNodeSetContentLen(node, (const xmlChar*)n->content().c_str(), n->content().length());
736         }
737
738         const XMLPropertyList& props = n->properties();
739
740         for (XMLPropertyConstIterator prop_iter = props.begin (); prop_iter != props.end ();
741              ++prop_iter) {
742                 xmlSetProp (node, (const xmlChar*)(*prop_iter)->name ().c_str (),
743                             (const xmlChar*)(*prop_iter)->value ().c_str ());
744         }
745
746         const XMLNodeList& children = n->children ();
747         for (XMLNodeConstIterator child_iter = children.begin (); child_iter != children.end ();
748              ++child_iter) {
749                 writenode (doc, *child_iter, node);
750         }
751 }
752
753 static XMLSharedNodeList* find_impl(xmlXPathContext* ctxt, const string& xpath)
754 {
755         xmlXPathObject* result = xmlXPathEval((const xmlChar*)xpath.c_str(), ctxt);
756
757         if (!result) {
758                 xmlXPathFreeContext(ctxt);
759                 xmlFreeDoc(ctxt->doc);
760
761                 throw XMLException("Invalid XPath: " + xpath);
762         }
763
764         if (result->type != XPATH_NODESET) {
765                 xmlXPathFreeObject(result);
766                 xmlXPathFreeContext(ctxt);
767                 xmlFreeDoc(ctxt->doc);
768
769                 throw XMLException("Only nodeset result types are supported.");
770         }
771
772         xmlNodeSet* nodeset = result->nodesetval;
773         XMLSharedNodeList* nodes = new XMLSharedNodeList();
774         if (nodeset) {
775                 for (int i = 0; i < nodeset->nodeNr; ++i) {
776                         XMLNode* node = readnode(nodeset->nodeTab[i]);
777                         nodes->push_back(boost::shared_ptr<XMLNode>(node));
778                 }
779         } else {
780                 // return empty set
781         }
782
783         xmlXPathFreeObject(result);
784
785         return nodes;
786 }
787
788 /** Dump a node, its properties and children to a stream */
789 void
790 XMLNode::dump (ostream& s, string p) const
791 {
792         if (_is_content) {
793                 s << p << "  " << content() << "\n";
794         } else {
795                 s << p << "<" << _name;
796                 for (XMLPropertyList::const_iterator i = _proplist.begin(); i != _proplist.end(); ++i) {
797                         s << " " << (*i)->name() << "=\"" << (*i)->value() << "\"";
798                 }
799                 s << ">\n";
800
801                 for (XMLNodeList::const_iterator i = _children.begin(); i != _children.end(); ++i) {
802                         (*i)->dump (s, p + "  ");
803                 }
804
805                 s << p << "</" << _name << ">\n";
806         }
807 }