5507f658e9329f5ea6e751937c96856f2b183568
[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         map<string,XMLProperty*>::iterator iter;
293
294         if ((iter = _propmap.find(ns)) != _propmap.end()) {
295                 return iter->second;
296         }
297
298         return 0;
299 }
300
301 XMLProperty *
302 XMLNode::property(const string & ns)
303 {
304         map<string,XMLProperty*>::iterator iter;
305
306         if ((iter = _propmap.find(ns)) != _propmap.end()) {
307                 return iter->second;
308         }
309         
310         return 0;
311 }
312
313 XMLProperty *
314 XMLNode::add_property(const char * n, const string & v)
315 {
316         string ns(n);
317         if(_propmap.find(ns) != _propmap.end()){
318                 remove_property(ns);
319         }
320
321         XMLProperty *tmp = new XMLProperty(ns, v);
322
323         if (!tmp) {
324                 return 0;
325         }
326
327         _propmap[tmp->name()] = tmp;
328         _proplist.insert(_proplist.end(), tmp);
329
330         return tmp;
331 }
332
333 XMLProperty *
334 XMLNode::add_property(const char * n, const char * v)
335 {
336         string vs(v);
337         return add_property(n, vs);
338 }
339
340 void 
341 XMLNode::remove_property(const string & n)
342 {
343         if (_propmap.find(n) != _propmap.end()) {
344                 _proplist.remove(_propmap[n]);
345                 _propmap.erase(n);
346         }
347 }
348
349 void 
350 XMLNode::remove_nodes(const string & n)
351 {
352         XMLNodeIterator i = _children.begin();
353         XMLNodeIterator tmp;
354         
355         while (i != _children.end()) {
356                 tmp = i;
357                 ++tmp;
358                 if ((*i)->name() == n) {
359                         _children.erase (i);
360                 }
361                 i = tmp;
362         }
363 }
364
365 void 
366 XMLNode::remove_nodes_and_delete(const string & n)
367 {
368         XMLNodeIterator i = _children.begin();
369         XMLNodeIterator tmp;
370         
371         while (i != _children.end()) {
372                 tmp = i;
373                 ++tmp;
374                 if ((*i)->name() == n) {
375                         delete *i;
376                         _children.erase (i);
377                 }
378                 i = tmp;
379         }
380 }
381
382 XMLProperty::XMLProperty(const string &n, const string &v)
383         : _name(n), 
384         _value(v) 
385
386 }
387
388 XMLProperty::~XMLProperty()
389 {
390 }
391
392 static XMLNode *
393 readnode(xmlNodePtr node)
394 {
395         string name, content;
396         xmlNodePtr child;
397         XMLNode *tmp;
398         xmlAttrPtr attr;
399         
400         if (node->name) {
401                 name = (char *) node->name;
402         }
403         
404         tmp = new XMLNode(name);
405         
406         for (attr = node->properties; attr; attr = attr->next) {
407                 content = "";
408                 if (attr->children) {
409                         content = (char *) attr->children->content;
410                 }
411                 tmp->add_property((char *) attr->name, content);
412         }
413         
414         if (node->content) {
415                 tmp->set_content((char *) node->content);
416         } else {
417                 tmp->set_content(string());
418         }
419         
420         for (child = node->children; child; child = child->next) {
421                 tmp->add_child_nocopy (*readnode(child));
422         }
423         
424         return tmp;
425 }
426
427 static void 
428 writenode(xmlDocPtr doc, XMLNode * n, xmlNodePtr p, int root = 0)
429 {
430         XMLPropertyList props;
431         XMLPropertyIterator curprop;
432         XMLNodeList children;
433         XMLNodeIterator curchild;
434         xmlNodePtr node;
435         
436         if (root) {
437                 node = doc->children = xmlNewDocNode(doc, 0, (xmlChar *) n->name().c_str(), 0);
438         } else {
439                 node = xmlNewChild(p, 0, (xmlChar *) n->name().c_str(), 0);
440         }
441             
442         if (n->is_content()) {
443                 node->type = XML_TEXT_NODE;
444                 xmlNodeSetContentLen(node, (const xmlChar *) n->content().c_str(), n->content().length());
445         }
446         
447         props = n->properties();
448         for (curprop = props.begin(); curprop != props.end(); ++curprop) {
449                 xmlSetProp(node, (xmlChar *) (*curprop)->name().c_str(), (xmlChar *) (*curprop)->value().c_str());
450         }
451             
452         children = n->children();
453         for (curchild = children.begin(); curchild != children.end(); ++curchild) {
454                 writenode(doc, *curchild, node);
455         }
456 }