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