110a224f3db1c9d6619552e60a0887ebdac353b9
[libcxml.git] / src / cxml.h
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 #ifndef LIBCXML_CXML_H
21 #define LIBCXML_CXML_H
22
23 #include <string>
24 #include <list>
25 #include <stdint.h>
26 #include <boost/shared_ptr.hpp>
27 #include <boost/optional.hpp>
28 #include <boost/filesystem.hpp>
29 #include <boost/algorithm/string/erase.hpp>
30
31 /* Hack for OS X compile failure; see https://bugs.launchpad.net/hugin/+bug/910160 */
32 #ifdef check
33 #undef check
34 #endif
35
36 namespace xmlpp {
37         class Node;
38         class DomParser;
39 }
40
41 namespace cxml {
42
43 /** @brief An error */
44 class Error : public std::exception
45 {
46 public:
47         /** Construct an Error exception.
48          *  @param message Error message.
49          */
50         Error (std::string const & message) : _message (message) {}
51
52         /** Error destructor */
53         ~Error () throw () {}
54
55         /** @return error message.  Caller must not free the returned
56          *  value.
57          */
58         char const * what () const throw () {
59                 return _message.c_str ();
60         }
61
62 private:
63         /** error message */
64         std::string _message;
65 };
66
67 /** @brief A wrapper for a xmlpp::Node which simplifies parsing */
68 class Node
69 {
70 public:
71         Node ();
72
73         /** Construct a Node from an xmlpp::Node.  This class will
74          *  not destroy the xmlpp::Node.
75          *  @param node xmlpp::Node.
76          */
77         Node (xmlpp::Node* node);
78
79         std::string name () const;
80
81         /* A set of methods which look up a child of this node by
82          * its name, and return its contents as some type or other.
83          *
84          * If, for example, this object has been created with
85          * a node named "Fred", we might have the following XML:
86          *
87          * <Fred>
88          *   <Jim>42</Jim>
89          * </Fred>
90          *
91          * string_child ("Jim") would return "42"
92          * number_child<int64_t> ("Jim") would return 42.
93          * ...and so on.
94          *
95          * The methods not marked "optional" will throw an exception
96          * if the child node is not present.  The "optional" methods
97          * will return an empty boost::optional<> in that case.
98          *
99          * All methods will also throw an exception if there is more
100          * than one of the specified child node.
101          */
102
103         std::string string_child (std::string c) const;
104         boost::optional<std::string> optional_string_child (std::string) const;
105
106         bool bool_child (std::string) const;
107         boost::optional<bool> optional_bool_child (std::string) const;
108
109         template <class T>
110         T number_child (std::string c) const
111         {
112                 std::string s = string_child (c);
113                 boost::erase_all (s, " ");
114                 std::stringstream t;
115                 t.imbue (std::locale::classic ());
116                 t << s;
117                 T n;
118                 t >> n;
119                 return n;
120         }
121
122         template <class T>
123         boost::optional<T> optional_number_child (std::string c) const
124         {
125                 boost::optional<std::string> s = optional_string_child (c);
126                 if (!s) {
127                         return boost::optional<T> ();
128                 }
129
130                 std::string t = s.get ();
131                 boost::erase_all (t, " ");
132                 std::stringstream u;
133                 u.imbue (std::locale::classic ());
134                 u << t;
135                 T n;
136                 u >> n;
137                 return n;
138         }
139
140         /** This will mark a child as to be ignored when calling done() */
141         void ignore_child (std::string) const;
142
143         /** Check whether all children of this Node have been looked up
144          *  or passed to ignore_child().  If not, an exception is thrown.
145          */
146         void done () const;
147
148         /* These methods look for an attribute of this node, in the
149          * same way as the child methods do.
150          */
151
152         std::string string_attribute (std::string) const;
153         boost::optional<std::string> optional_string_attribute (std::string) const;
154
155         bool bool_attribute (std::string) const;
156         boost::optional<bool> optional_bool_attribute (std::string) const;
157
158         template <class T>
159         T number_attribute (std::string c) const
160         {
161                 std::string s = string_attribute (c);
162                 boost::erase_all (s, " ");
163                 std::stringstream t;
164                 t.imbue (std::locale::classic ());
165                 t << s;
166                 T n;
167                 t >> n;
168                 return n;
169         }
170
171         template <class T>
172         boost::optional<T> optional_number_attribute (std::string c) const
173         {
174                 boost::optional<std::string> s = optional_string_attribute (c);
175                 if (!s) {
176                         return boost::optional<T> ();
177                 }
178
179                 std::string t = s.get ();
180                 boost::erase_all (t, " ");
181                 std::stringstream u;
182                 u.imbue (std::locale::classic ());
183                 u << t;
184                 T n;
185                 u >> n;
186                 return n;
187         }
188
189         /** @return The content of this node */
190         std::string content () const;
191
192         /** @return namespace URI of this node */
193         std::string namespace_uri () const;
194
195         /** @return namespace prefix of this node */
196         std::string namespace_prefix () const;
197
198         boost::shared_ptr<Node> node_child (std::string) const;
199         boost::shared_ptr<Node> optional_node_child (std::string) const;
200
201         std::list<boost::shared_ptr<Node> > node_children (std::string) const;
202
203         xmlpp::Node* node () const {
204                 return _node;
205         }
206
207 protected:
208         xmlpp::Node* _node;
209
210 private:
211         mutable std::list<std::string> _taken;
212 };
213
214 typedef boost::shared_ptr<cxml::Node> NodePtr;
215 typedef boost::shared_ptr<const cxml::Node> ConstNodePtr;
216
217 class Document : public Node
218 {
219 public:
220         Document ();
221         Document (std::string root_name);
222         Document (std::string root_name, boost::filesystem::path);
223
224         virtual ~Document ();
225
226         void read_file (boost::filesystem::path);
227         void read_stream (std::istream &);
228         void read_string (std::string);
229
230         std::string root_name () const {
231                 return _root_name;
232         }
233
234 private:
235         void take_root_node ();
236
237         xmlpp::DomParser* _parser;
238         std::string _root_name;
239 };
240
241 }
242
243 #endif