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