Allow True in bool_child.
[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::string;
28 using std::stringstream;
29 using std::istream;
30 using std::list;
31 using boost::shared_ptr;
32 using boost::optional;
33
34 cxml::Node::Node ()
35         : _node (0)
36 {
37
38 }
39
40 cxml::Node::Node (xmlpp::Node* node)
41         : _node (node)
42 {
43
44 }
45
46 string
47 cxml::Node::name () const
48 {
49         assert (_node);
50         return _node->get_name ();
51 }
52
53 shared_ptr<cxml::Node>
54 cxml::Node::node_child (string name) const
55 {
56         list<shared_ptr<cxml::Node> > n = node_children (name);
57         if (n.size() > 1) {
58                 throw cxml::Error ("duplicate XML tag " + name);
59         } else if (n.empty ()) {
60                 throw cxml::Error ("missing XML tag " + name + " in " + _node->get_name());
61         }
62
63         return n.front ();
64 }
65
66 shared_ptr<cxml::Node>
67 cxml::Node::optional_node_child (string name) const
68 {
69         list<shared_ptr<cxml::Node> > n = node_children (name);
70         if (n.size() > 1) {
71                 throw cxml::Error ("duplicate XML tag " + name);
72         } else if (n.empty ()) {
73                 return shared_ptr<cxml::Node> ();
74         }
75
76         return n.front ();
77 }
78
79 list<shared_ptr<cxml::Node> >
80 cxml::Node::node_children (string name) const
81 {
82         /* XXX: using find / get_path should work here, but I can't follow
83            how get_path works.
84         */
85
86         xmlpp::Node::NodeList c = _node->get_children ();
87
88         list<shared_ptr<cxml::Node> > n;
89         for (xmlpp::Node::NodeList::iterator i = c.begin (); i != c.end(); ++i) {
90                 if ((*i)->get_name() == name) {
91                         n.push_back (shared_ptr<Node> (new Node (*i)));
92                 }
93         }
94
95         _taken.push_back (name);
96         return n;
97 }
98
99 string
100 cxml::Node::string_child (string c) const
101 {
102         return node_child(c)->content ();
103 }
104
105 optional<string>
106 cxml::Node::optional_string_child (string c) const
107 {
108         list<shared_ptr<Node> > nodes = node_children (c);
109         if (nodes.size() > 1) {
110                 throw cxml::Error ("duplicate XML tag " + c);
111         }
112
113         if (nodes.empty ()) {
114                 return optional<string> ();
115         }
116
117         return nodes.front()->content();
118 }
119
120 bool
121 cxml::Node::bool_child (string c) const
122 {
123         string const s = string_child (c);
124         return (s == "1" || s == "yes" || s == "True");
125 }
126
127 optional<bool>
128 cxml::Node::optional_bool_child (string c) const
129 {
130         optional<string> s = optional_string_child (c);
131         if (!s) {
132                 return optional<bool> ();
133         }
134
135         return (s.get() == "1" || s.get() == "yes" || s.get() == "True");
136 }
137
138 void
139 cxml::Node::ignore_child (string name) const
140 {
141         _taken.push_back (name);
142 }
143
144 string
145 cxml::Node::string_attribute (string name) const
146 {
147         xmlpp::Element const * e = dynamic_cast<const xmlpp::Element *> (_node);
148         if (!e) {
149                 throw cxml::Error ("missing attribute " + name);
150         }
151
152         xmlpp::Attribute* a = e->get_attribute (name);
153         if (!a) {
154                 throw cxml::Error ("missing attribute " + name);
155         }
156
157         return a->get_value ();
158 }
159
160 optional<string>
161 cxml::Node::optional_string_attribute (string name) const
162 {
163         xmlpp::Element const * e = dynamic_cast<const xmlpp::Element *> (_node);
164         if (!e) {
165                 return optional<string> ();
166         }
167
168         xmlpp::Attribute* a = e->get_attribute (name);
169         if (!a) {
170                 return optional<string> ();
171         }
172
173         return string (a->get_value ());
174 }
175
176 bool
177 cxml::Node::bool_attribute (string name) const
178 {
179         string const s = string_attribute (name);
180         return (s == "1" || s == "yes");
181 }
182
183 optional<bool>
184 cxml::Node::optional_bool_attribute (string name) const
185 {
186         optional<string> s = optional_string_attribute (name);
187         if (!s) {
188                 return optional<bool> ();
189         }
190
191         return (s.get() == "1" || s.get() == "yes");
192 }
193
194 void
195 cxml::Node::done () const
196 {
197         xmlpp::Node::NodeList c = _node->get_children ();
198         for (xmlpp::Node::NodeList::iterator i = c.begin(); i != c.end(); ++i) {
199                 if (dynamic_cast<xmlpp::Element *> (*i) && find (_taken.begin(), _taken.end(), (*i)->get_name()) == _taken.end ()) {
200                         throw cxml::Error ("unexpected XML node " + (*i)->get_name());
201                 }
202         }
203 }
204
205 string
206 cxml::Node::content () const
207 {
208         string content;
209
210         xmlpp::Node::NodeList c = _node->get_children ();
211         for (xmlpp::Node::NodeList::const_iterator i = c.begin(); i != c.end(); ++i) {
212                 xmlpp::ContentNode const * v = dynamic_cast<xmlpp::ContentNode const *> (*i);
213                 if (v) {
214                         content += v->get_content ();
215                 }
216         }
217
218         return content;
219 }
220
221 string
222 cxml::Node::namespace_uri () const
223 {
224         return _node->get_namespace_uri ();
225 }
226
227 string
228 cxml::Node::namespace_prefix () const
229 {
230         return _node->get_namespace_prefix ();
231 }
232
233 cxml::Document::Document (string root_name)
234         : _root_name (root_name)
235 {
236         _parser = new xmlpp::DomParser;
237 }
238
239 cxml::Document::Document (string root_name, boost::filesystem::path file)
240         : _root_name (root_name)
241 {
242         _parser = new xmlpp::DomParser ();
243         read_file (file);
244 }
245
246 cxml::Document::Document ()
247 {
248         _parser = new xmlpp::DomParser ();
249 }
250
251 cxml::Document::~Document ()
252 {
253         delete _parser;
254 }
255
256 void
257 cxml::Document::read_file (boost::filesystem::path file)
258 {
259         if (!boost::filesystem::exists (file)) {
260                 throw cxml::Error ("XML file " + file.string() + " does not exist");
261         }
262
263         _parser->parse_file (file.string ());
264         take_root_node ();
265 }
266
267 void
268 cxml::Document::read_stream (istream& stream)
269 {
270         _parser->parse_stream (stream);
271         take_root_node ();
272 }
273
274 void
275 cxml::Document::read_string (string s)
276 {
277         stringstream t (s);
278         _parser->parse_stream (t);
279         take_root_node ();
280 }
281
282 void
283 cxml::Document::take_root_node ()
284 {
285         if (!_parser) {
286                 throw cxml::Error ("could not parse XML");
287         }
288
289         _node = _parser->get_document()->get_root_node ();
290         if (!_root_name.empty() && _node->get_name() != _root_name) {
291                 throw cxml::Error ("unrecognised root node " + _node->get_name() + " (expecting " + _root_name + ")");
292         } else if (_root_name.empty ()) {
293                 _root_name = _node->get_name ();
294         }
295 }