8d783d59f2da5616ede495ef4908b31326d5c2f6
[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  */
6
7 #include <pbd/xml++.h>
8 #include <libxml/debugXML.h>
9
10 static XMLNode *readnode(xmlNodePtr);
11 static void writenode(xmlDocPtr, XMLNode *, xmlNodePtr, int);
12
13 XMLTree::XMLTree() 
14         : _filename(), 
15         _root(0), 
16         _compression(0)
17
18 }
19
20 XMLTree::XMLTree(const string &fn)
21         : _filename(fn), 
22         _root(0), 
23         _compression(0)
24
25         read(); 
26 }
27
28 XMLTree::XMLTree(const XMLTree * from)
29 {
30         _filename = from->filename();
31         _root = new XMLNode(*from->root());
32         _compression = from->compression();
33 }
34
35 XMLTree::~XMLTree()
36 {
37         if (_root) {
38                 delete _root;
39         }
40 }
41
42 int 
43 XMLTree::set_compression(int c)
44 {
45         if (c > 9) {
46                 c = 9;
47         } else if (c < 0) {
48                 c = 0;
49         }
50         
51         _compression = c;
52         
53         return _compression;
54 }
55
56 bool 
57 XMLTree::read(void)
58 {
59         xmlDocPtr doc;
60         
61         if (_root) {
62                 delete _root;
63                 _root = 0;
64         }
65         
66         xmlKeepBlanksDefault(0);
67         
68         doc = xmlParseFile(_filename.c_str());
69         if (!doc) {
70                 return false;
71         }
72         
73         _root = readnode(xmlDocGetRootElement(doc));
74         xmlFreeDoc(doc);
75         
76         return true;
77 }
78
79 bool 
80 XMLTree::read_buffer(const string & buffer)
81 {
82         xmlDocPtr doc;
83         
84         _filename = "";
85         
86         if (_root) {
87                 delete _root;
88                 _root = 0;
89         }
90         
91         doc = xmlParseMemory((char *) buffer.c_str(), buffer.length());
92         if (!doc) {
93                 return false;
94         }
95         
96         _root = readnode(xmlDocGetRootElement(doc));
97         xmlFreeDoc(doc);
98         
99         return true;
100 }
101
102 bool 
103 XMLTree::write(void) const
104 {
105         xmlDocPtr doc;
106         XMLNodeList children;
107         int result;
108         
109         xmlKeepBlanksDefault(0);
110         doc = xmlNewDoc((xmlChar *) "1.0");
111         xmlSetDocCompressMode(doc, _compression);
112         writenode(doc, _root, doc->children, 1);
113         result = xmlSaveFormatFileEnc(_filename.c_str(), doc, "UTF-8", 1);
114         xmlFreeDoc(doc);
115         
116         if (result == -1) {
117                 return false;
118         }
119         
120         return true;
121 }
122
123 void
124 XMLTree::debug(FILE* out) const
125 {
126     xmlDocPtr doc;
127     XMLNodeList children;
128
129     xmlKeepBlanksDefault(0);
130     doc = xmlNewDoc((xmlChar *) "1.0");
131     xmlSetDocCompressMode(doc, _compression);
132     writenode(doc, _root, doc->children, 1);
133     xmlDebugDumpDocument (out, doc);
134     xmlFreeDoc(doc);
135 }
136
137 const string & 
138 XMLTree::write_buffer(void) const
139 {
140         static string retval;
141         char *ptr;
142         int len;
143         xmlDocPtr doc;
144         XMLNodeList children;
145         
146         xmlKeepBlanksDefault(0);
147         doc = xmlNewDoc((xmlChar *) "1.0");
148         xmlSetDocCompressMode(doc, _compression);
149         writenode(doc, _root, doc->children, 1);
150         xmlDocDumpMemory(doc, (xmlChar **) & ptr, &len);
151         xmlFreeDoc(doc);
152         
153         retval = ptr;
154         
155         free(ptr);
156         
157         return retval;
158 }
159
160 XMLNode::XMLNode(const string & n)
161         :  _name(n), _is_content(false), _content(string())
162 {
163 }
164
165 XMLNode::XMLNode(const string & n, const string & c)
166         :_name(n), _is_content(true), _content(c)
167 {
168 }
169
170 XMLNode::XMLNode(const XMLNode& from)
171 {
172         XMLPropertyList props;
173         XMLPropertyIterator curprop;
174         XMLNodeList nodes;
175         XMLNodeIterator curnode;
176         
177         _name = from.name();
178         set_content(from.content());
179         
180         props = from.properties();
181         for (curprop = props.begin(); curprop != props.end(); ++curprop) {
182                 add_property((*curprop)->name().c_str(), (*curprop)->value());
183         }
184         
185         nodes = from.children();
186         for (curnode = nodes.begin(); curnode != nodes.end(); ++curnode) {
187                 add_child_copy(**curnode);
188         }
189 }
190
191 XMLNode::~XMLNode()
192 {
193         XMLNodeIterator curchild;
194         XMLPropertyIterator curprop;
195         
196         for (curchild = _children.begin(); curchild != _children.end(); ++curchild) {
197                 delete *curchild;
198         }
199             
200         for (curprop = _proplist.begin(); curprop != _proplist.end(); ++curprop) {
201                 delete *curprop;
202         }
203 }
204
205 const string & 
206 XMLNode::set_content(const string & c)
207 {
208         if (c.empty()) {
209                 _is_content = false;
210         } else {
211                 _is_content = true;
212         }
213             
214         _content = c;
215         
216         return _content;
217 }
218
219 XMLNode*
220 XMLNode::child (const char *name) const
221 {
222         /* returns first child matching name */
223
224         XMLNodeConstIterator cur;
225         
226         if (name == 0) {
227                 return 0;
228         }
229             
230         for (cur = _children.begin(); cur != _children.end(); ++cur) {
231                 if ((*cur)->name() == name) {
232                         return *cur;
233                 }
234         }
235             
236         return 0;
237 }
238
239 const XMLNodeList & 
240 XMLNode::children(const string& n) const
241 {
242         /* returns all children matching name */
243
244         static XMLNodeList retval;
245         XMLNodeConstIterator cur;
246         
247         if (n.empty()) {
248                 return _children;
249         }
250             
251         retval.erase(retval.begin(), retval.end());
252         
253         for (cur = _children.begin(); cur != _children.end(); ++cur) {
254                 if ((*cur)->name() == n) {
255                         retval.insert(retval.end(), *cur);
256                 }
257         }
258             
259         return retval;
260 }
261
262 XMLNode *
263 XMLNode::add_child(const char * n)
264 {
265         return add_child_copy(XMLNode (n));
266 }
267
268 void
269 XMLNode::add_child_nocopy (XMLNode& n)
270 {
271         _children.insert(_children.end(), &n);
272 }
273
274 XMLNode *
275 XMLNode::add_child_copy(const XMLNode& n)
276 {
277         XMLNode *copy = new XMLNode (n);
278         _children.insert(_children.end(), copy);
279         return copy;
280 }
281
282 XMLNode *
283 XMLNode::add_content(const string & c)
284 {
285         return add_child_copy(XMLNode (string(), c));
286 }
287
288 XMLProperty *
289 XMLNode::property(const char * n)
290 {
291         string ns(n);
292         if (_propmap.find(ns) == _propmap.end()) {
293                 return 0;
294         }
295         
296         return _propmap[ns];
297 }
298
299 XMLProperty *
300 XMLNode::add_property(const char * n, const string & v)
301 {
302         string ns(n);
303         if(_propmap.find(ns) != _propmap.end()){
304                 remove_property(ns);
305         }
306
307         XMLProperty *tmp = new XMLProperty(ns, v);
308
309         if (!tmp) {
310                 return 0;
311         }
312
313         _propmap[tmp->name()] = tmp;
314         _proplist.insert(_proplist.end(), tmp);
315
316         return tmp;
317 }
318
319 XMLProperty *
320 XMLNode::add_property(const char * n, const char * v)
321 {
322         string vs(v);
323         return add_property(n, vs);
324 }
325
326 void 
327 XMLNode::remove_property(const string & n)
328 {
329         if (_propmap.find(n) != _propmap.end()) {
330                 _proplist.remove(_propmap[n]);
331                 _propmap.erase(n);
332         }
333 }
334
335 void 
336 XMLNode::remove_nodes(const string & n)
337 {
338         XMLNodeIterator i = _children.begin();
339         XMLNodeIterator tmp;
340         
341         while (i != _children.end()) {
342                 tmp = i;
343                 ++tmp;
344                 if ((*i)->name() == n) {
345                         _children.erase (i);
346                 }
347                 i = tmp;
348         }
349 }
350
351 void 
352 XMLNode::remove_nodes_and_delete(const string & n)
353 {
354         XMLNodeIterator i = _children.begin();
355         XMLNodeIterator tmp;
356         
357         while (i != _children.end()) {
358                 tmp = i;
359                 ++tmp;
360                 if ((*i)->name() == n) {
361                         delete *i;
362                         _children.erase (i);
363                 }
364                 i = tmp;
365         }
366 }
367
368 XMLProperty::XMLProperty(const string &n, const string &v)
369         : _name(n), 
370         _value(v) 
371
372 }
373
374 XMLProperty::~XMLProperty()
375 {
376 }
377
378 static XMLNode *
379 readnode(xmlNodePtr node)
380 {
381         string name, content;
382         xmlNodePtr child;
383         XMLNode *tmp;
384         xmlAttrPtr attr;
385         
386         if (node->name) {
387                 name = (char *) node->name;
388         }
389         
390         tmp = new XMLNode(name);
391         
392         for (attr = node->properties; attr; attr = attr->next) {
393                 content = "";
394                 if (attr->children) {
395                         content = (char *) attr->children->content;
396                 }
397                 tmp->add_property((char *) attr->name, content);
398         }
399         
400         if (node->content) {
401                 tmp->set_content((char *) node->content);
402         } else {
403                 tmp->set_content(string());
404         }
405         
406         for (child = node->children; child; child = child->next) {
407                 tmp->add_child_nocopy (*readnode(child));
408         }
409         
410         return tmp;
411 }
412
413 static void 
414 writenode(xmlDocPtr doc, XMLNode * n, xmlNodePtr p, int root = 0)
415 {
416         XMLPropertyList props;
417         XMLPropertyIterator curprop;
418         XMLNodeList children;
419         XMLNodeIterator curchild;
420         xmlNodePtr node;
421         
422         if (root) {
423                 node = doc->children = xmlNewDocNode(doc, 0, (xmlChar *) n->name().c_str(), 0);
424         } else {
425                 node = xmlNewChild(p, 0, (xmlChar *) n->name().c_str(), 0);
426         }
427             
428         if (n->is_content()) {
429                 node->type = XML_TEXT_NODE;
430                 xmlNodeSetContentLen(node, (const xmlChar *) n->content().c_str(), n->content().length());
431         }
432         
433         props = n->properties();
434         for (curprop = props.begin(); curprop != props.end(); ++curprop) {
435                 xmlSetProp(node, (xmlChar *) (*curprop)->name().c_str(), (xmlChar *) (*curprop)->value().c_str());
436         }
437             
438         children = n->children();
439         for (curchild = children.begin(); curchild != children.end(); ++curchild) {
440                 writenode(doc, *curchild, node);
441         }
442 }