move at-exit messages about pool utilization to DEBUG_TRACE
[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         if (_is_content)
470                 throw XMLException("XMLNode: attribute_value failed (is_content) for requested node: " + name());
471
472         if (children.size() != 1)
473                 throw XMLException("XMLNode: attribute_value failed (children.size != 1) for requested node: " + name());
474
475         XMLNode* child = *(children.begin());
476         if (!child->is_content())
477                 throw XMLException("XMLNode: attribute_value failed (!child->is_content()) for requested node: " + name());
478
479         return child->content();
480 }
481
482 XMLNode*
483 XMLNode::add_content(const string& c)
484 {
485         if (c.empty ()) {
486                 /* this would add a "</>" child, leading to invalid XML.
487                  * Also in XML, empty string content is equivalent to no content.
488                  */
489                 return NULL;
490         }
491         return add_child_copy(XMLNode (string(), c));
492 }
493
494 XMLProperty const *
495 XMLNode::property(const char* name) const
496 {
497         XMLPropertyConstIterator iter = _proplist.begin();
498
499         while (iter != _proplist.end()) {
500                 if ((*iter)->name() == name) {
501                         return *iter;
502                 }
503                 ++iter;
504         }
505
506         return 0;
507 }
508
509 XMLProperty const *
510 XMLNode::property(const string& name) const
511 {
512         XMLPropertyConstIterator iter = _proplist.begin();
513
514         while (iter != _proplist.end()) {
515                 if ((*iter)->name() == name) {
516                         return *iter;
517                 }
518                 ++iter;
519         }
520         return 0;
521 }
522
523 XMLProperty *
524 XMLNode::property(const char* name)
525 {
526         XMLPropertyIterator iter = _proplist.begin();
527
528         while (iter != _proplist.end()) {
529                 if ((*iter)->name() == name) {
530                         return *iter;
531                 }
532                 ++iter;
533         }
534         return 0;
535 }
536
537 XMLProperty *
538 XMLNode::property(const string& name)
539 {
540         XMLPropertyIterator iter = _proplist.begin();
541
542         while (iter != _proplist.end()) {
543                 if ((*iter)->name() == name) {
544                         return *iter;
545                 }
546                 ++iter;
547         }
548
549         return 0;
550 }
551
552 bool
553 XMLNode::has_property_with_value (const string& name, const string& value) const
554 {
555         XMLPropertyConstIterator iter = _proplist.begin();
556
557         while (iter != _proplist.end()) {
558                 if ((*iter)->name() == name && (*iter)->value() == value) {
559                         return true;
560                 }
561                 ++iter;
562         }
563         return false;
564 }
565
566 bool
567 XMLNode::set_property(const char* name, const string& value)
568 {
569         XMLPropertyIterator iter = _proplist.begin();
570
571         while (iter != _proplist.end()) {
572                 if ((*iter)->name() == name) {
573                         (*iter)->set_value (value);
574                         return *iter;
575                 }
576                 ++iter;
577         }
578
579         XMLProperty* new_property = new XMLProperty(name, value);
580
581         if (!new_property) {
582                 return 0;
583         }
584
585         _proplist.insert(_proplist.end(), new_property);
586
587         return new_property;
588 }
589
590 bool
591 XMLNode::get_property(const char* name, std::string& value) const
592 {
593         XMLProperty const* const prop = property (name);
594         if (!prop)
595                 return false;
596
597         value = prop->value ();
598
599         return true;
600 }
601
602 void
603 XMLNode::remove_property(const string& name)
604 {
605         XMLPropertyIterator iter = _proplist.begin();
606
607         while (iter != _proplist.end()) {
608                 if ((*iter)->name() == name) {
609                         XMLProperty* property = *iter;
610                         _proplist.erase (iter);
611                         delete property;
612                         break;
613                 }
614                 ++iter;
615         }
616 }
617
618 /** Remove any property with the given name from this node and its children */
619 void
620 XMLNode::remove_property_recursively(const string& n)
621 {
622         remove_property (n);
623         for (XMLNodeIterator i = _children.begin(); i != _children.end(); ++i) {
624                 (*i)->remove_property_recursively (n);
625         }
626 }
627
628 void
629 XMLNode::remove_nodes(const string& n)
630 {
631         XMLNodeIterator i = _children.begin();
632         while (i != _children.end()) {
633                 if ((*i)->name() == n) {
634                         i = _children.erase (i);
635                 } else {
636                         ++i;
637                 }
638         }
639 }
640
641 void
642 XMLNode::remove_nodes_and_delete(const string& n)
643 {
644         XMLNodeIterator i = _children.begin();
645
646         while (i != _children.end()) {
647                 if ((*i)->name() == n) {
648                         delete *i;
649                         i = _children.erase (i);
650                 } else {
651                         ++i;
652                 }
653         }
654 }
655
656 void
657 XMLNode::remove_nodes_and_delete(const string& propname, const string& val)
658 {
659         XMLNodeIterator i = _children.begin();
660         XMLProperty const * prop;
661
662         while (i != _children.end()) {
663                 prop = (*i)->property(propname);
664                 if (prop && prop->value() == val) {
665                         delete *i;
666                         i = _children.erase(i);
667                 } else {
668                         ++i;
669                 }
670         }
671 }
672
673 void
674 XMLNode::remove_node_and_delete(const string& n, const string& propname, const string& val)
675 {
676         for (XMLNodeIterator i = _children.begin(); i != _children.end(); ++i) {
677                 if ((*i)->name() == n) {
678                         XMLProperty const * prop = (*i)->property (propname);
679                         if (prop && prop->value() == val) {
680                                 delete *i;
681                                 _children.erase (i);
682                                 break;
683                         }
684                 }
685         }
686 }
687
688 XMLProperty::XMLProperty(const string& n, const string& v)
689         : _name(n)
690         , _value(v)
691 {
692 }
693
694 XMLProperty::~XMLProperty()
695 {
696 }
697
698 static XMLNode*
699 readnode(xmlNodePtr node)
700 {
701         string name, content;
702         xmlNodePtr child;
703         XMLNode* tmp;
704         xmlAttrPtr attr;
705
706         if (node->name) {
707                 name = (const char*)node->name;
708         }
709
710         tmp = new XMLNode(name);
711
712         for (attr = node->properties; attr; attr = attr->next) {
713                 content = "";
714                 if (attr->children) {
715                         content = (char*)attr->children->content;
716                 }
717                 tmp->set_property((const char*)attr->name, content);
718         }
719
720         if (node->content) {
721                 tmp->set_content((char*)node->content);
722         } else {
723                 tmp->set_content(string());
724         }
725
726         for (child = node->children; child; child = child->next) {
727                 tmp->add_child_nocopy (*readnode(child));
728         }
729
730         return tmp;
731 }
732
733 static void
734 writenode(xmlDocPtr doc, XMLNode* n, xmlNodePtr p, int root = 0)
735 {
736         xmlNodePtr node;
737
738         if (root) {
739                 node = doc->children = xmlNewDocNode(doc, 0, (const xmlChar*) n->name().c_str(), 0);
740         } else {
741                 node = xmlNewChild(p, 0, (const xmlChar*) n->name().c_str(), 0);
742         }
743
744         if (n->is_content()) {
745                 node->type = XML_TEXT_NODE;
746                 xmlNodeSetContentLen(node, (const xmlChar*)n->content().c_str(), n->content().length());
747         }
748
749         const XMLPropertyList& props = n->properties();
750
751         for (XMLPropertyConstIterator prop_iter = props.begin (); prop_iter != props.end ();
752              ++prop_iter) {
753                 xmlSetProp (node, (const xmlChar*)(*prop_iter)->name ().c_str (),
754                             (const xmlChar*)(*prop_iter)->value ().c_str ());
755         }
756
757         const XMLNodeList& children = n->children ();
758         for (XMLNodeConstIterator child_iter = children.begin (); child_iter != children.end ();
759              ++child_iter) {
760                 writenode (doc, *child_iter, node);
761         }
762 }
763
764 static XMLSharedNodeList* find_impl(xmlXPathContext* ctxt, const string& xpath)
765 {
766         xmlXPathObject* result = xmlXPathEval((const xmlChar*)xpath.c_str(), ctxt);
767
768         if (!result) {
769                 xmlXPathFreeContext(ctxt);
770                 xmlFreeDoc(ctxt->doc);
771
772                 throw XMLException("Invalid XPath: " + xpath);
773         }
774
775         if (result->type != XPATH_NODESET) {
776                 xmlXPathFreeObject(result);
777                 xmlXPathFreeContext(ctxt);
778                 xmlFreeDoc(ctxt->doc);
779
780                 throw XMLException("Only nodeset result types are supported.");
781         }
782
783         xmlNodeSet* nodeset = result->nodesetval;
784         XMLSharedNodeList* nodes = new XMLSharedNodeList();
785         if (nodeset) {
786                 for (int i = 0; i < nodeset->nodeNr; ++i) {
787                         XMLNode* node = readnode(nodeset->nodeTab[i]);
788                         nodes->push_back(boost::shared_ptr<XMLNode>(node));
789                 }
790         } else {
791                 // return empty set
792         }
793
794         xmlXPathFreeObject(result);
795
796         return nodes;
797 }
798
799 /** Dump a node, its properties and children to a stream */
800 void
801 XMLNode::dump (ostream& s, string p) const
802 {
803         if (_is_content) {
804                 s << p << "  " << content() << "\n";
805         } else {
806                 s << p << "<" << _name;
807                 for (XMLPropertyList::const_iterator i = _proplist.begin(); i != _proplist.end(); ++i) {
808                         s << " " << (*i)->name() << "=\"" << (*i)->value() << "\"";
809                 }
810                 s << ">\n";
811
812                 for (XMLNodeList::const_iterator i = _children.begin(); i != _children.end(); ++i) {
813                         (*i)->dump (s, p + "  ");
814                 }
815
816                 s << p << "</" << _name << ">\n";
817         }
818 }