f6305d0392161102876ac9c943b65b0367c4f467
[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 boost::shared_ptr;
35 using boost::optional;
36
37 cxml::Node::Node ()
38 {
39
40 }
41
42 cxml::Node::Node (xmlpp::Node const * node)
43 {
44         read (node);
45 }
46
47 void
48 cxml::Node::read (xmlpp::Node const * node)
49 {
50         _name = node->get_name ();
51         _namespace_uri = node->get_namespace_uri ();
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);
77
78         if (!_content.empty ()) {
79                 node->add_child_text (_content);
80         }
81
82         for (list<pair<string, string> >::const_iterator i = _attributes.begin(); i != _attributes.end(); ++i) {
83                 node->set_attribute (i->first, i->second);
84         }
85
86         for (NodeList::const_iterator i = _children.begin(); i != _children.end(); ++i) {
87                 (*i)->write (node);
88         }
89 }
90
91 cxml::NodePtr
92 cxml::Node::node_child (string name) const
93 {
94         NodeList n = node_children (name);
95         if (n.size() > 1) {
96                 throw cxml::Error ("duplicate XML tag " + name);
97         } else if (n.empty ()) {
98                 throw cxml::Error ("missing XML tag " + name + " in " + _name);
99         }
100         
101         return n.front ();
102 }
103
104 cxml::NodePtr
105 cxml::Node::optional_node_child (string name) const
106 {
107         NodeList n = node_children (name);
108         if (n.size() > 1) {
109                 throw cxml::Error ("duplicate XML tag " + name);
110         } else if (n.empty ()) {
111                 return NodePtr ();
112         }
113         
114         return n.front ();
115 }
116
117 cxml::NodeList
118 cxml::Node::node_children (string name) const
119 {
120         NodeList n;
121         
122         for (NodeList::const_iterator i = _children.begin(); i != _children.end(); ++i) {
123                 if ((*i)->name() == name) {
124                         n.push_back (*i);
125                 }
126         }
127
128         _taken.push_back (name);
129         return n;
130 }
131
132 string
133 cxml::Node::string_child (string c) const
134 {
135         return node_child(c)->content ();
136 }
137
138 optional<string>
139 cxml::Node::optional_string_child (string c) const
140 {
141         NodeList nodes = node_children (c);
142         if (nodes.size() > 1) {
143                 throw cxml::Error ("duplicate XML tag " + c);
144         }
145
146         if (nodes.empty ()) {
147                 return optional<string> ();
148         }
149
150         return nodes.front()->content();
151 }
152
153 bool
154 cxml::Node::bool_child (string c) const
155 {
156         string const s = string_child (c);
157         return (s == "1" || s == "yes");
158 }
159
160 optional<bool>
161 cxml::Node::optional_bool_child (string c) const
162 {
163         optional<string> s = optional_string_child (c);
164         if (!s) {
165                 return optional<bool> ();
166         }
167         
168         return (s.get() == "1" || s.get() == "yes");
169 }
170
171 void
172 cxml::Node::ignore_child (string name) const
173 {
174         _taken.push_back (name);
175 }
176
177 string
178 cxml::Node::string_attribute (string name) const
179 {
180         list<pair<string, string> >::const_iterator i;
181         while (i != _attributes.end() && i->first != name) {
182                 ++i;
183         }
184         
185         if (i == _attributes.end ()) {
186                 throw cxml::Error ("missing attribute");
187         }
188
189         return i->second;
190 }
191
192 optional<string>
193 cxml::Node::optional_string_attribute (string name) const
194 {
195         list<pair<string, string> >::const_iterator i;
196         while (i != _attributes.end() && i->first != name) {
197                 ++i;
198         }
199         
200         if (i == _attributes.end ()) {
201                 return optional<string> ();
202         }
203
204         return i->second;
205 }
206
207 bool
208 cxml::Node::bool_attribute (string name) const
209 {
210         string const s = string_attribute (name);
211         return (s == "1" || s == "yes");
212 }
213
214 optional<bool>
215 cxml::Node::optional_bool_attribute (string name) const
216 {
217         optional<string> s = optional_string_attribute (name);
218         if (!s) {
219                 return optional<bool> ();
220         }
221
222         return (s.get() == "1" || s.get() == "yes");
223 }
224
225 void
226 cxml::Node::done () const
227 {
228         for (NodeList::const_iterator i = _children.begin(); i != _children.end(); ++i) {
229                 if (find (_taken.begin(), _taken.end(), (*i)->name()) == _taken.end ()) {
230                         throw cxml::Error ("unexpected XML node " + (*i)->name());
231                 }
232         }
233 }
234
235 string
236 cxml::Node::content () const
237 {
238         return _content;
239 }
240
241 string
242 cxml::Node::namespace_uri () const
243 {
244         return _namespace_uri;
245 }
246
247 string
248 cxml::Node::namespace_prefix () const
249 {
250         return _namespace_prefix;
251 }
252
253 void
254 cxml::Node::set_string_content (string c)
255 {
256         _content = c;
257 }
258
259 void
260 cxml::Node::set_bool_content (bool c)
261 {
262         _content = c ? "1" : "0";
263 }
264
265 cxml::NodePtr
266 cxml::Node::add_string (string name, string content)
267 {
268         NodePtr n = add (name);
269         n->set_string_content (content);
270         return n;
271 }
272         
273 cxml::NodePtr
274 cxml::Node::add_bool (std::string name, bool content)
275 {
276         NodePtr n = add (name);
277         n->set_bool_content (content);
278         return n;
279 }
280
281 void
282 cxml::Document::read_file (boost::filesystem::path file)
283 {
284         if (!boost::filesystem::exists (file)) {
285                 throw cxml::Error ("XML file does not exist");
286         }
287
288         xmlpp::DomParser parser;
289         parser.parse_file (file.string ());
290         read (parser.get_document()->get_root_node ());
291 }
292
293 void
294 cxml::Document::read_stream (istream& stream)
295 {
296         xmlpp::DomParser parser;
297         parser.parse_stream (stream);
298         read (parser.get_document()->get_root_node ());
299 }
300
301 void
302 cxml::Document::read_string (string s)
303 {
304         xmlpp::DomParser parser;
305         stringstream t (s);
306         parser.parse_stream (t);
307         read (parser.get_document()->get_root_node ());
308 }
309
310 void
311 cxml::Document::check_root_name (string r)
312 {
313         if (name() != r) {
314                 throw cxml::Error ("unrecognised root node");
315         }
316 }
317
318 void
319 cxml::Document::write_to_file_formatted (boost::filesystem::path path) const
320 {
321         xmlpp::Document doc;
322         write_to_xmlpp_document (doc);
323         doc.write_to_file_formatted (path.string ());
324 }
325
326 void
327 cxml::Document::write_to_string (std::string coding) const
328 {
329         xmlpp::Document doc;
330         write_to_xmlpp_document (doc);
331         doc.write_to_string (coding);
332 }
333
334 void
335 cxml::Document::write_to_xmlpp_document (xmlpp::Document& doc) const
336 {
337         xmlpp::Element* root = doc.create_root_node (name ());
338         for (NodeList::const_iterator i = _children.begin(); i != _children.end(); ++i) {
339                 (*i)->write (root);
340         }
341 }