Sort out xmlns stuff.
[libcxml.git] / src / cxml.cc
1 /*
2     Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <sstream>
21 #include <iostream>
22 #include <boost/filesystem.hpp>
23 #include <boost/algorithm/string.hpp>
24 #include <libxml++/libxml++.h>
25 #include "cxml.h"
26
27 using std::pair;
28 using std::list;
29 using std::istream;
30 using std::string;
31 using std::make_pair;
32 using std::map;
33 using std::stringstream;
34 using std::ostream;
35 using boost::shared_ptr;
36 using boost::optional;
37
38 cxml::Node::Node ()
39 {
40
41 }
42
43 cxml::Node::Node (xmlpp::Node const * node)
44 {
45         read (node);
46 }
47
48 void
49 cxml::Node::read (xmlpp::Node const * node)
50 {
51         _name = node->get_name ();
52         _namespace_prefix = node->get_namespace_prefix ();
53         
54         xmlpp::Node::NodeList content = node->get_children ();
55         for (xmlpp::Node::NodeList::const_iterator i = content.begin(); i != content.end(); ++i) {
56                 xmlpp::ContentNode const * v = dynamic_cast<xmlpp::ContentNode const *> (*i);
57                 if (v) {
58                         _content += v->get_content ();
59                 } else {
60                         _children.push_back (cxml::NodePtr (new cxml::Node (*i)));
61                 }
62         }
63
64         xmlpp::Element const * element = dynamic_cast<xmlpp::Element const *> (node);
65         if (element) {
66                 xmlpp::Element::AttributeList attributes = element->get_attributes ();
67                 for (list<xmlpp::Attribute*>::const_iterator i = attributes.begin(); i != attributes.end(); ++i) {
68                         _attributes.push_back (make_pair ((*i)->get_name(), (*i)->get_value ()));
69                 }
70         }
71 }
72
73 void
74 cxml::Node::write (xmlpp::Node* parent) const
75 {
76         xmlpp::Element* node = parent->add_child (_name, _namespace_prefix);
77
78         if (!_content.empty ()) {
79                 node->add_child_text (_content);
80         }
81
82         for (KeyValueList::const_iterator i = _namespace_declarations.begin(); i != _namespace_declarations.end(); ++i) {
83                 node->set_namespace_declaration (i->first, i->second);
84         }
85         
86         for (KeyValueList::const_iterator i = _attributes.begin(); i != _attributes.end(); ++i) {
87                 node->set_attribute (i->first, i->second);
88         }
89
90         for (NodeList::const_iterator i = _children.begin(); i != _children.end(); ++i) {
91                 (*i)->write (node);
92         }
93 }
94
95 cxml::NodePtr
96 cxml::Node::child (string name) const
97 {
98         NodeList n = children (name);
99         if (n.size() > 1) {
100                 throw cxml::Error ("duplicate XML tag " + name);
101         } else if (n.empty ()) {
102                 throw cxml::Error ("missing XML tag " + name + " in " + _name);
103         }
104         
105         return n.front ();
106 }
107
108 cxml::NodePtr
109 cxml::Node::optional_child (string name) const
110 {
111         NodeList n = children (name);
112         if (n.size() > 1) {
113                 throw cxml::Error ("duplicate XML tag " + name);
114         } else if (n.empty ()) {
115                 return NodePtr ();
116         }
117         
118         return n.front ();
119 }
120
121 cxml::NodeList
122 cxml::Node::children () const
123 {
124         return _children;
125 }
126
127 cxml::NodeList
128 cxml::Node::children (string name) const
129 {
130         NodeList n;
131         
132         for (NodeList::const_iterator i = _children.begin(); i != _children.end(); ++i) {
133                 if ((*i)->name() == name) {
134                         n.push_back (*i);
135                 }
136         }
137
138         _taken.push_back (name);
139         return n;
140 }
141
142 string
143 cxml::Node::string_child (string c) const
144 {
145         return child(c)->content ();
146 }
147
148 optional<string>
149 cxml::Node::optional_string_child (string c) const
150 {
151         NodeList nodes = children (c);
152         if (nodes.size() > 1) {
153                 throw cxml::Error ("duplicate XML tag " + c);
154         }
155
156         if (nodes.empty ()) {
157                 return optional<string> ();
158         }
159
160         return nodes.front()->content();
161 }
162
163 bool
164 cxml::Node::bool_child (string c) const
165 {
166         string const s = string_child (c);
167         return (s == "1" || s == "yes");
168 }
169
170 optional<bool>
171 cxml::Node::optional_bool_child (string c) const
172 {
173         optional<string> s = optional_string_child (c);
174         if (!s) {
175                 return optional<bool> ();
176         }
177         
178         return (s.get() == "1" || s.get() == "yes");
179 }
180
181 void
182 cxml::Node::ignore_child (string name) const
183 {
184         _taken.push_back (name);
185 }
186
187 string
188 cxml::Node::string_attribute (string name) const
189 {
190         KeyValueList::const_iterator i = _attributes.begin ();
191         while (i != _attributes.end() && i->first != name) {
192                 ++i;
193         }
194         
195         if (i == _attributes.end ()) {
196                 throw cxml::Error ("missing attribute");
197         }
198
199         return i->second;
200 }
201
202 optional<string>
203 cxml::Node::optional_string_attribute (string name) const
204 {
205         KeyValueList::const_iterator i;
206         while (i != _attributes.end() && i->first != name) {
207                 ++i;
208         }
209         
210         if (i == _attributes.end ()) {
211                 return optional<string> ();
212         }
213
214         return i->second;
215 }
216
217 bool
218 cxml::Node::bool_attribute (string name) const
219 {
220         string const s = string_attribute (name);
221         return (s == "1" || s == "yes");
222 }
223
224 optional<bool>
225 cxml::Node::optional_bool_attribute (string name) const
226 {
227         optional<string> s = optional_string_attribute (name);
228         if (!s) {
229                 return optional<bool> ();
230         }
231
232         return (s.get() == "1" || s.get() == "yes");
233 }
234
235 void
236 cxml::Node::done () const
237 {
238         for (NodeList::const_iterator i = _children.begin(); i != _children.end(); ++i) {
239                 if (find (_taken.begin(), _taken.end(), (*i)->name()) == _taken.end ()) {
240                         throw cxml::Error ("unexpected XML node " + (*i)->name());
241                 }
242         }
243 }
244
245 string
246 cxml::Node::content () const
247 {
248         return _content;
249 }
250
251 string
252 cxml::Node::namespace_prefix () const
253 {
254         return _namespace_prefix;
255 }
256
257 void
258 cxml::Node::set_attribute (string name, string value)
259 {
260         KeyValueList::iterator i = _attributes.begin ();
261         while (i != _attributes.end() && i->first != name) {
262                 ++i;
263         }
264
265         if (i != _attributes.end ()) {
266                 _attributes.erase (i);
267         }
268                 
269         _attributes.push_back (make_pair (name, value));
270 }
271
272 void
273 cxml::Node::set_namespace_declaration (string uri, string ns)
274 {
275         KeyValueList::iterator i = _namespace_declarations.begin ();
276         while (i != _namespace_declarations.end() && i->second != ns) {
277                 ++i;
278         }
279
280         if (i != _namespace_declarations.end ()) {
281                 _namespace_declarations.erase (i);
282         }
283
284         _namespace_declarations.push_back (make_pair (uri, ns));
285 }
286
287 cxml::NodePtr
288 cxml::read_file (boost::filesystem::path file)
289 {
290         if (!boost::filesystem::exists (file)) {
291                 throw cxml::Error ("XML file does not exist");
292         }
293
294         xmlpp::DomParser parser;
295         parser.parse_file (file.string ());
296         cxml::NodePtr node (new cxml::Node);
297         node->read (parser.get_document()->get_root_node ());
298         return node;
299 }
300
301 cxml::NodePtr
302 cxml::read_stream (istream& stream)
303 {
304         xmlpp::DomParser parser;
305         parser.parse_stream (stream);
306         cxml::NodePtr node (new cxml::Node);
307         node->read (parser.get_document()->get_root_node ());
308         return node;
309 }
310
311 cxml::NodePtr
312 cxml::read_string (string s)
313 {
314         xmlpp::DomParser parser;
315         stringstream t (s);
316         parser.parse_stream (t);
317         cxml::NodePtr node (new cxml::Node);
318         node->read (parser.get_document()->get_root_node ());
319         return node;
320 }
321
322 static void
323 write_to_xmlpp_document (cxml::ConstNodePtr node, xmlpp::Document& doc)
324 {
325         xmlpp::Element* root = doc.create_root_node (node->name ());
326
327         cxml::KeyValueList namespace_declarations = node->namespace_declarations ();
328         for (cxml::KeyValueList::const_iterator i = namespace_declarations.begin(); i != namespace_declarations.end(); ++i) {
329                 root->set_namespace_declaration (i->first, i->second);
330         }
331
332         cxml::NodeList children = node->children ();
333         for (cxml::NodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
334                 (*i)->write (root);
335         }
336 }
337
338 void
339 cxml::write_to_file (cxml::ConstNodePtr node, boost::filesystem::path path)
340 {
341         xmlpp::Document doc;
342         write_to_xmlpp_document (node, doc);
343         doc.write_to_file (path.string ());
344 }
345
346 void
347 cxml::write_to_file_formatted (cxml::ConstNodePtr node, boost::filesystem::path path)
348 {
349         xmlpp::Document doc;
350         write_to_xmlpp_document (node, doc);
351         doc.write_to_file_formatted (path.string ());
352 }
353
354 void
355 cxml::write_to_stream_formatted (cxml::ConstNodePtr node, ostream& stream)
356 {
357         xmlpp::Document doc;
358         write_to_xmlpp_document (node, doc);
359         doc.write_to_stream_formatted (stream);
360 }
361
362 string
363 cxml::write_to_string (cxml::ConstNodePtr node)
364 {
365         xmlpp::Document doc;
366         write_to_xmlpp_document (node, doc);
367         return doc.write_to_string ();
368 }
369
370 string
371 cxml::write_to_string_formatted (cxml::ConstNodePtr node)
372 {
373         xmlpp::Document doc;
374         write_to_xmlpp_document (node, doc);
375         return doc.write_to_string_formatted ();
376 }
377