Use return {}.
[libcxml.git] / src / cxml.h
1 /*
2     Copyright (C) 2012-2021 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 <boost/optional.hpp>
25 #include <boost/filesystem.hpp>
26 #include <boost/algorithm/string/erase.hpp>
27 #include <stdint.h>
28 #include <string>
29 #include <vector>
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 /** A sort-of version of boost::lexical_cast that does uses the "C"
68  *  locale (i.e. no thousands separators and a . for the decimal separator).
69  */
70 template <typename P, typename Q>
71 P
72 raw_convert (Q)
73 {
74         /* We can't write a generic version of raw_convert; all required
75            versions must be specialised.
76         */
77         BOOST_STATIC_ASSERT (sizeof(Q) == 0);
78 }
79
80 template <>
81 int
82 raw_convert (std::string v);
83
84 template <>
85 unsigned int
86 raw_convert (std::string v);
87
88 template <>
89 long int
90 raw_convert (std::string v);
91
92 template <>
93 long unsigned int
94 raw_convert (std::string v);
95
96 template <>
97 long long
98 raw_convert (std::string v);
99
100 template <>
101 long long unsigned
102 raw_convert (std::string v);
103
104 template <>
105 float
106 raw_convert (std::string v);
107
108 template <>
109 double
110 raw_convert (std::string v);
111
112 /** @brief A wrapper for a xmlpp::Node which simplifies parsing */
113 class Node
114 {
115 public:
116         Node ();
117
118         /** Construct a Node from an xmlpp::Node.  This class will
119          *  not destroy the xmlpp::Node.
120          *  @param node xmlpp::Node.
121          */
122         Node (xmlpp::Node* node);
123
124         std::string name () const;
125
126         /* A set of methods which look up a child of this node by
127          * its name, and return its contents as some type or other.
128          *
129          * If, for example, this object has been created with
130          * a node named "Fred", we might have the following XML:
131          *
132          * <Fred>
133          *   <Jim>42</Jim>
134          * </Fred>
135          *
136          * string_child ("Jim") would return "42"
137          * number_child<int64_t> ("Jim") would return 42.
138          * ...and so on.
139          *
140          * The methods not marked "optional" will throw an exception
141          * if the child node is not present.  The "optional" methods
142          * will return an empty boost::optional<> in that case.
143          *
144          * All methods will also throw an exception if there is more
145          * than one of the specified child node.
146          */
147
148         std::string string_child (std::string c) const;
149         boost::optional<std::string> optional_string_child (std::string) const;
150
151         bool bool_child (std::string) const;
152         boost::optional<bool> optional_bool_child (std::string) const;
153
154         template <class T>
155         T number_child (std::string c) const
156         {
157                 auto s = string_child (c);
158                 boost::erase_all (s, " ");
159                 return raw_convert<T> (s);
160         }
161
162         template <class T>
163         boost::optional<T> optional_number_child (std::string c) const
164         {
165                 auto s = optional_string_child (c);
166                 if (!s) {
167                         return {};
168                 }
169
170                 auto t = s.get ();
171                 boost::erase_all (t, " ");
172                 return raw_convert<T> (t);
173         }
174
175         /** This will mark a child as to be ignored when calling done() */
176         void ignore_child (std::string) const;
177
178         /** Check whether all children of this Node have been looked up
179          *  or passed to ignore_child().  If not, an exception is thrown.
180          */
181         void done () const;
182
183         /* These methods look for an attribute of this node, in the
184          * same way as the child methods do.
185          */
186
187         std::string string_attribute (std::string) const;
188         boost::optional<std::string> optional_string_attribute (std::string) const;
189
190         bool bool_attribute (std::string) const;
191         boost::optional<bool> optional_bool_attribute (std::string) const;
192
193         template <class T>
194         T number_attribute (std::string c) const
195         {
196                 std::string s = string_attribute (c);
197                 boost::erase_all (s, " ");
198                 return raw_convert<T> (s);
199         }
200
201         template <class T>
202         boost::optional<T> optional_number_attribute (std::string c) const
203         {
204                 auto s = optional_string_attribute (c);
205                 if (!s) {
206                         return boost::optional<T> ();
207                 }
208
209                 auto t = s.get ();
210                 boost::erase_all (t, " ");
211                 return raw_convert<T> (t);
212         }
213
214         /** @return The text content of this node (excluding comments or CDATA) */
215         std::string content () const;
216
217         /** @return namespace URI of this node */
218         std::string namespace_uri () const;
219
220         /** @return namespace prefix of this node */
221         std::string namespace_prefix () const;
222
223         std::shared_ptr<Node> node_child (std::string) const;
224         std::shared_ptr<Node> optional_node_child (std::string) const;
225
226         std::vector<std::shared_ptr<Node>> node_children () const;
227         std::vector<std::shared_ptr<Node>> node_children (std::string) const;
228
229         xmlpp::Node* node () const {
230                 return _node;
231         }
232
233 protected:
234         xmlpp::Node* _node;
235
236 private:
237         mutable std::vector<std::string> _taken;
238 };
239
240 typedef std::shared_ptr<cxml::Node> NodePtr;
241 typedef std::shared_ptr<const cxml::Node> ConstNodePtr;
242
243 class Document : public Node
244 {
245 public:
246         Document ();
247         Document (std::string root_name);
248         Document (std::string root_name, boost::filesystem::path);
249
250         virtual ~Document ();
251
252         void read_file (boost::filesystem::path);
253         void read_string (std::string);
254
255         std::string root_name () const {
256                 return _root_name;
257         }
258
259 private:
260         void take_root_node ();
261
262         xmlpp::DomParser* _parser;
263         std::string _root_name;
264 };
265
266 }
267
268 #endif