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