PBD::Signal<...>::connect() is already thread safe, so drop intermediate proxy/call_s...
[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                 add_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 XMLProperty*
555 XMLNode::add_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 XMLProperty*
579 XMLNode::add_property(const char* n, const char* v)
580 {
581         string vs(v);
582         return add_property(n, vs);
583 }
584
585 XMLProperty*
586 XMLNode::add_property(const char* name, const long value)
587 {
588         char str[64];
589         snprintf(str, sizeof(str), "%ld", value);
590         return add_property(name, str);
591 }
592
593 void
594 XMLNode::remove_property(const string& name)
595 {
596         XMLPropertyIterator iter = _proplist.begin();
597
598         while (iter != _proplist.end()) {
599                 if ((*iter)->name() == name) {
600                         XMLProperty* property = *iter;
601                         _proplist.erase (iter);
602                         delete property;
603                         break;
604                 }
605                 ++iter;
606         }
607 }
608
609 /** Remove any property with the given name from this node and its children */
610 void
611 XMLNode::remove_property_recursively(const string& n)
612 {
613         remove_property (n);
614         for (XMLNodeIterator i = _children.begin(); i != _children.end(); ++i) {
615                 (*i)->remove_property_recursively (n);
616         }
617 }
618
619 void
620 XMLNode::remove_nodes(const string& n)
621 {
622         XMLNodeIterator i = _children.begin();
623         while (i != _children.end()) {
624                 if ((*i)->name() == n) {
625                         i = _children.erase (i);
626                 } else {
627                         ++i;
628                 }
629         }
630 }
631
632 void
633 XMLNode::remove_nodes_and_delete(const string& n)
634 {
635         XMLNodeIterator i = _children.begin();
636
637         while (i != _children.end()) {
638                 if ((*i)->name() == n) {
639                         delete *i;
640                         i = _children.erase (i);
641                 } else {
642                         ++i;
643                 }
644         }
645 }
646
647 void
648 XMLNode::remove_nodes_and_delete(const string& propname, const string& val)
649 {
650         XMLNodeIterator i = _children.begin();
651         XMLProperty const * prop;
652
653         while (i != _children.end()) {
654                 prop = (*i)->property(propname);
655                 if (prop && prop->value() == val) {
656                         delete *i;
657                         i = _children.erase(i);
658                 } else {
659                         ++i;
660                 }
661         }
662 }
663
664 XMLProperty::XMLProperty(const string& n, const string& v)
665         : _name(n)
666         , _value(v)
667 {
668         // Normalize property name (replace '_' with '-' as old session are inconsistent)
669         for (size_t i = 0; i < _name.length(); ++i) {
670                 if (_name[i] == '_') {
671                         _name[i] = '-';
672                 }
673         }
674 }
675
676 XMLProperty::~XMLProperty()
677 {
678 }
679
680 static XMLNode*
681 readnode(xmlNodePtr node)
682 {
683         string name, content;
684         xmlNodePtr child;
685         XMLNode* tmp;
686         xmlAttrPtr attr;
687
688         if (node->name) {
689                 name = (const char*)node->name;
690         }
691
692         tmp = new XMLNode(name);
693
694         for (attr = node->properties; attr; attr = attr->next) {
695                 content = "";
696                 if (attr->children) {
697                         content = (char*)attr->children->content;
698                 }
699                 tmp->add_property((const char*)attr->name, content);
700         }
701
702         if (node->content) {
703                 tmp->set_content((char*)node->content);
704         } else {
705                 tmp->set_content(string());
706         }
707
708         for (child = node->children; child; child = child->next) {
709                 tmp->add_child_nocopy (*readnode(child));
710         }
711
712         return tmp;
713 }
714
715 static void
716 writenode(xmlDocPtr doc, XMLNode* n, xmlNodePtr p, int root = 0)
717 {
718         xmlNodePtr node;
719
720         if (root) {
721                 node = doc->children = xmlNewDocNode(doc, 0, (const xmlChar*) n->name().c_str(), 0);
722         } else {
723                 node = xmlNewChild(p, 0, (const xmlChar*) n->name().c_str(), 0);
724         }
725
726         if (n->is_content()) {
727                 node->type = XML_TEXT_NODE;
728                 xmlNodeSetContentLen(node, (const xmlChar*)n->content().c_str(), n->content().length());
729         }
730
731         const XMLPropertyList& props = n->properties();
732
733         for (XMLPropertyConstIterator prop_iter = props.begin (); prop_iter != props.end ();
734              ++prop_iter) {
735                 xmlSetProp (node, (const xmlChar*)(*prop_iter)->name ().c_str (),
736                             (const xmlChar*)(*prop_iter)->value ().c_str ());
737         }
738
739         const XMLNodeList& children = n->children ();
740         for (XMLNodeConstIterator child_iter = children.begin (); child_iter != children.end ();
741              ++child_iter) {
742                 writenode (doc, *child_iter, node);
743         }
744 }
745
746 static XMLSharedNodeList* find_impl(xmlXPathContext* ctxt, const string& xpath)
747 {
748         xmlXPathObject* result = xmlXPathEval((const xmlChar*)xpath.c_str(), ctxt);
749
750         if (!result) {
751                 xmlXPathFreeContext(ctxt);
752                 xmlFreeDoc(ctxt->doc);
753
754                 throw XMLException("Invalid XPath: " + xpath);
755         }
756
757         if (result->type != XPATH_NODESET) {
758                 xmlXPathFreeObject(result);
759                 xmlXPathFreeContext(ctxt);
760                 xmlFreeDoc(ctxt->doc);
761
762                 throw XMLException("Only nodeset result types are supported.");
763         }
764
765         xmlNodeSet* nodeset = result->nodesetval;
766         XMLSharedNodeList* nodes = new XMLSharedNodeList();
767         if (nodeset) {
768                 for (int i = 0; i < nodeset->nodeNr; ++i) {
769                         XMLNode* node = readnode(nodeset->nodeTab[i]);
770                         nodes->push_back(boost::shared_ptr<XMLNode>(node));
771                 }
772         } else {
773                 // return empty set
774         }
775
776         xmlXPathFreeObject(result);
777
778         return nodes;
779 }
780
781 /** Dump a node, its properties and children to a stream */
782 void
783 XMLNode::dump (ostream& s, string p) const
784 {
785         if (_is_content) {
786                 s << p << "  " << content() << "\n";
787         } else {
788                 s << p << "<" << _name;
789                 for (XMLPropertyList::const_iterator i = _proplist.begin(); i != _proplist.end(); ++i) {
790                         s << " " << (*i)->name() << "=\"" << (*i)->value() << "\"";
791                 }
792                 s << ">\n";
793
794                 for (XMLNodeList::const_iterator i = _children.begin(); i != _children.end(); ++i) {
795                         (*i)->dump (s, p + "  ");
796                 }
797
798                 s << p << "</" << _name << ">\n";
799         }
800 }