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.
10 #include <libxml/debugXML.h>
11 #include <libxml/xpath.h>
12 #include <libxml/xpathInternals.h>
14 #define XML_VERSION "1.0"
18 static XMLNode
* readnode(xmlNodePtr
);
19 static void writenode(xmlDocPtr
, XMLNode
*, xmlNodePtr
, int);
20 static XMLSharedNodeList
* find_impl(xmlXPathContext
* ctxt
, const string
& xpath
);
29 XMLTree::XMLTree(const string
& fn
, bool validate
)
34 read_internal(validate
);
37 XMLTree::XMLTree(const XMLTree
* from
)
38 : _filename(from
->filename())
39 , _root(new XMLNode(*from
->root()))
40 , _compression(from
->compression())
50 XMLTree::set_compression(int c
)
64 XMLTree::read_internal(bool validate
)
66 //shouldnt be used anywhere ATM, remove if so!
72 xmlParserCtxtPtr ctxt
= NULL
; /* the parser context */
73 xmlDocPtr doc
; /* the resulting document tree */
75 xmlKeepBlanksDefault(0);
76 /* parse the file, activating the DTD validation option */
78 /* create a parser context */
79 ctxt
= xmlNewParserCtxt();
83 doc
= xmlCtxtReadFile(ctxt
, _filename
.c_str(), NULL
, XML_PARSE_DTDVALID
);
85 doc
= xmlParseFile(_filename
.c_str());
88 /* check if parsing suceeded */
91 xmlFreeParserCtxt(ctxt
);
95 /* check if validation suceeded */
96 if (validate
&& ctxt
->valid
== 0) {
97 xmlFreeParserCtxt(ctxt
);
99 throw XMLException("Failed to validate document " + _filename
);
103 _root
= readnode(xmlDocGetRootElement(doc
));
105 /* free up the parser context */
107 xmlFreeParserCtxt(ctxt
);
115 XMLTree::read_buffer(const string
& buffer
)
124 doc
= xmlParseMemory((char*)buffer
.c_str(), buffer
.length());
129 _root
= readnode(xmlDocGetRootElement(doc
));
137 XMLTree::write() const
140 XMLNodeList children
;
143 xmlKeepBlanksDefault(0);
144 doc
= xmlNewDoc((xmlChar
*) XML_VERSION
);
145 xmlSetDocCompressMode(doc
, _compression
);
146 writenode(doc
, _root
, doc
->children
, 1);
147 result
= xmlSaveFormatFileEnc(_filename
.c_str(), doc
, "UTF-8", 1);
158 XMLTree::debug(FILE* out
) const
161 XMLNodeList children
;
163 xmlKeepBlanksDefault(0);
164 doc
= xmlNewDoc((xmlChar
*) XML_VERSION
);
165 xmlSetDocCompressMode(doc
, _compression
);
166 writenode(doc
, _root
, doc
->children
, 1);
167 xmlDebugDumpDocument (out
, doc
);
172 XMLTree::write_buffer() const
174 static string retval
;
178 XMLNodeList children
;
180 xmlKeepBlanksDefault(0);
181 doc
= xmlNewDoc((xmlChar
*) XML_VERSION
);
182 xmlSetDocCompressMode(doc
, _compression
);
183 writenode(doc
, _root
, doc
->children
, 1);
184 xmlDocDumpMemory(doc
, (xmlChar
**) & ptr
, &len
);
194 XMLNode::XMLNode(const string
& n
)
200 XMLNode::XMLNode(const string
& n
, const string
& c
)
207 XMLNode::XMLNode(const XMLNode
& from
)
209 XMLPropertyList props
;
210 XMLPropertyIterator curprop
;
212 XMLNodeIterator curnode
;
215 set_content(from
.content());
217 props
= from
.properties();
218 for (curprop
= props
.begin(); curprop
!= props
.end(); ++curprop
) {
219 add_property((*curprop
)->name().c_str(), (*curprop
)->value());
222 nodes
= from
.children();
223 for (curnode
= nodes
.begin(); curnode
!= nodes
.end(); ++curnode
) {
224 add_child_copy(**curnode
);
230 XMLNodeIterator curchild
;
231 XMLPropertyIterator curprop
;
233 for (curchild
= _children
.begin(); curchild
!= _children
.end(); ++curchild
) {
237 for (curprop
= _proplist
.begin(); curprop
!= _proplist
.end(); ++curprop
) {
243 XMLNode::set_content(const string
& c
)
257 XMLNode::child (const char* name
) const
259 /* returns first child matching name */
261 XMLNodeConstIterator cur
;
267 for (cur
= _children
.begin(); cur
!= _children
.end(); ++cur
) {
268 if ((*cur
)->name() == name
) {
277 XMLNode::children(const string
& n
) const
279 /* returns all children matching name */
281 XMLNodeConstIterator cur
;
287 _selected_children
.clear();
289 for (cur
= _children
.begin(); cur
!= _children
.end(); ++cur
) {
290 if ((*cur
)->name() == n
) {
291 _selected_children
.insert(_selected_children
.end(), *cur
);
295 return _selected_children
;
299 XMLNode::add_child(const char* n
)
301 return add_child_copy(XMLNode (n
));
305 XMLNode::add_child_nocopy(XMLNode
& n
)
307 _children
.insert(_children
.end(), &n
);
311 XMLNode::add_child_copy(const XMLNode
& n
)
313 XMLNode
*copy
= new XMLNode(n
);
314 _children
.insert(_children
.end(), copy
);
318 boost::shared_ptr
<XMLSharedNodeList
>
319 XMLNode::find(const string xpath
) const
321 xmlDocPtr doc
= xmlNewDoc((xmlChar
*) XML_VERSION
);
322 writenode(doc
, (XMLNode
*)this, doc
->children
, 1);
323 xmlXPathContext
* ctxt
= xmlXPathNewContext(doc
);
325 boost::shared_ptr
<XMLSharedNodeList
> result
=
326 boost::shared_ptr
<XMLSharedNodeList
>(find_impl(ctxt
, xpath
));
328 xmlXPathFreeContext(ctxt
);
335 XMLNode::attribute_value()
337 XMLNodeList children
= this->children();
338 assert(!_is_content
);
339 assert(children
.size() == 1);
340 XMLNode
* child
= *(children
.begin());
341 assert(child
->is_content());
342 return child
->content();
346 XMLNode::add_content(const string
& c
)
348 return add_child_copy(XMLNode (string(), c
));
352 XMLNode::property(const char* n
)
355 map
<string
,XMLProperty
*>::iterator iter
;
357 if ((iter
= _propmap
.find(ns
)) != _propmap
.end()) {
365 XMLNode::property(const string
& ns
)
367 map
<string
,XMLProperty
*>::iterator iter
;
369 if ((iter
= _propmap
.find(ns
)) != _propmap
.end()) {
377 XMLNode::add_property(const char* n
, const string
& v
)
380 map
<string
,XMLProperty
*>::iterator iter
;
382 if ((iter
= _propmap
.find(ns
)) != _propmap
.end()) {
383 iter
->second
->set_value (v
);
387 XMLProperty
* tmp
= new XMLProperty(ns
, v
);
393 _propmap
[tmp
->name()] = tmp
;
394 _proplist
.insert(_proplist
.end(), tmp
);
400 XMLNode::add_property(const char* n
, const char* v
)
403 return add_property(n
, vs
);
407 XMLNode::add_property(const char* name
, const long value
)
410 snprintf(str
, sizeof(str
), "%ld", value
);
411 return add_property(name
, str
);
415 XMLNode::remove_property(const string
& n
)
417 if (_propmap
.find(n
) != _propmap
.end()) {
418 XMLProperty
* p
= _propmap
[n
];
419 _proplist
.remove (p
);
426 XMLNode::remove_nodes(const string
& n
)
428 XMLNodeIterator i
= _children
.begin();
431 while (i
!= _children
.end()) {
434 if ((*i
)->name() == n
) {
442 XMLNode::remove_nodes_and_delete(const string
& n
)
444 XMLNodeIterator i
= _children
.begin();
447 while (i
!= _children
.end()) {
450 if ((*i
)->name() == n
) {
459 XMLNode::remove_nodes_and_delete(const string
& propname
, const string
& val
)
461 XMLNodeIterator i
= _children
.begin();
465 while (i
!= _children
.end()) {
469 prop
= (*i
)->property(propname
);
470 if (prop
&& prop
->value() == val
) {
479 XMLProperty::XMLProperty(const string
& n
, const string
& v
)
483 // Normalize property name (replace '_' with '-' as old session are inconsistent)
484 for (size_t i
= 0; i
< _name
.length(); ++i
) {
485 if (_name
[i
] == '_') {
491 XMLProperty::~XMLProperty()
496 readnode(xmlNodePtr node
)
498 string name
, content
;
504 name
= (char*)node
->name
;
507 tmp
= new XMLNode(name
);
509 for (attr
= node
->properties
; attr
; attr
= attr
->next
) {
511 if (attr
->children
) {
512 content
= (char*)attr
->children
->content
;
514 tmp
->add_property((char*)attr
->name
, content
);
518 tmp
->set_content((char*)node
->content
);
520 tmp
->set_content(string());
523 for (child
= node
->children
; child
; child
= child
->next
) {
524 tmp
->add_child_nocopy (*readnode(child
));
531 writenode(xmlDocPtr doc
, XMLNode
* n
, xmlNodePtr p
, int root
= 0)
533 XMLPropertyList props
;
534 XMLPropertyIterator curprop
;
535 XMLNodeList children
;
536 XMLNodeIterator curchild
;
540 node
= doc
->children
= xmlNewDocNode(doc
, 0, (xmlChar
*) n
->name().c_str(), 0);
542 node
= xmlNewChild(p
, 0, (xmlChar
*) n
->name().c_str(), 0);
545 if (n
->is_content()) {
546 node
->type
= XML_TEXT_NODE
;
547 xmlNodeSetContentLen(node
, (const xmlChar
*)n
->content().c_str(), n
->content().length());
550 props
= n
->properties();
551 for (curprop
= props
.begin(); curprop
!= props
.end(); ++curprop
) {
552 xmlSetProp(node
, (xmlChar
*) (*curprop
)->name().c_str(), (xmlChar
*) (*curprop
)->value().c_str());
555 children
= n
->children();
556 for (curchild
= children
.begin(); curchild
!= children
.end(); ++curchild
) {
557 writenode(doc
, *curchild
, node
);
561 static XMLSharedNodeList
* find_impl(xmlXPathContext
* ctxt
, const string
& xpath
)
563 xmlXPathObject
* result
= xmlXPathEval((const xmlChar
*)xpath
.c_str(), ctxt
);
566 xmlXPathFreeContext(ctxt
);
567 xmlFreeDoc(ctxt
->doc
);
569 throw XMLException("Invalid XPath: " + xpath
);
572 if (result
->type
!= XPATH_NODESET
) {
573 xmlXPathFreeObject(result
);
574 xmlXPathFreeContext(ctxt
);
575 xmlFreeDoc(ctxt
->doc
);
577 throw XMLException("Only nodeset result types are supported.");
580 xmlNodeSet
* nodeset
= result
->nodesetval
;
581 XMLSharedNodeList
* nodes
= new XMLSharedNodeList();
583 for (int i
= 0; i
< nodeset
->nodeNr
; ++i
) {
584 XMLNode
* node
= readnode(nodeset
->nodeTab
[i
]);
585 nodes
->push_back(boost::shared_ptr
<XMLNode
>(node
));
591 xmlXPathFreeObject(result
);
596 /** Dump a node, its properties and children to a stream */
598 XMLNode::dump (ostream
& s
, string p
) const
600 s
<< p
<< _name
<< " ";
601 for (XMLPropertyList::const_iterator i
= _proplist
.begin(); i
!= _proplist
.end(); ++i
) {
602 s
<< (*i
)->name() << "=" << (*i
)->value() << " ";
606 for (XMLNodeList::const_iterator i
= _children
.begin(); i
!= _children
.end(); ++i
) {
607 (*i
)->dump (s
, p
+ " ");