]>
Commit | Line | Data |
---|---|---|
7c673cae | 1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
9f95a23c | 2 | // vim: ts=8 sw=2 smarttab ft=cpp |
7c673cae | 3 | |
1e59de90 | 4 | #pragma once |
7c673cae FG |
5 | |
6 | #include <map> | |
9f95a23c | 7 | #include <stdexcept> |
7c673cae FG |
8 | #include <string> |
9 | #include <iosfwd> | |
10 | #include <include/types.h> | |
11 | #include <common/Formatter.h> | |
12 | ||
7c673cae | 13 | class XMLObj; |
11fdf7f2 | 14 | class RGWXMLParser; |
7c673cae FG |
15 | |
16 | class XMLObjIter { | |
7c673cae | 17 | public: |
20effc67 TL |
18 | typedef std::map<std::string, XMLObj *>::iterator map_iter_t; |
19 | typedef std::map<std::string, XMLObj *>::iterator const_map_iter_t; | |
11fdf7f2 | 20 | |
7c673cae | 21 | XMLObjIter(); |
1e59de90 | 22 | virtual ~XMLObjIter(); |
11fdf7f2 | 23 | void set(const XMLObjIter::const_map_iter_t &_cur, const XMLObjIter::const_map_iter_t &_end); |
7c673cae | 24 | XMLObj *get_next(); |
11fdf7f2 TL |
25 | bool get_name(std::string& name) const; |
26 | ||
27 | private: | |
28 | map_iter_t cur; | |
29 | map_iter_t end; | |
7c673cae FG |
30 | }; |
31 | ||
32 | /** | |
33 | * Represents a block of XML. | |
34 | * Give the class an XML blob, and it will parse the blob into | |
35 | * an attr_name->value map. | |
11fdf7f2 | 36 | * It shouldn't be the start point for any parsing. Look at RGWXMLParser for that. |
7c673cae FG |
37 | */ |
38 | class XMLObj | |
39 | { | |
11fdf7f2 | 40 | private: |
7c673cae | 41 | XMLObj *parent; |
11fdf7f2 | 42 | std::string obj_type; |
7c673cae | 43 | |
11fdf7f2 TL |
44 | protected: |
45 | std::string data; | |
46 | std::multimap<std::string, XMLObj *> children; | |
47 | std::map<std::string, std::string> attr_map; | |
7c673cae | 48 | |
11fdf7f2 | 49 | // invoked at the beginning of the XML tag, and populate any attributes |
7c673cae | 50 | bool xml_start(XMLObj *parent, const char *el, const char **attr); |
11fdf7f2 TL |
51 | // callback invoked at the end of the XML tag |
52 | // if objects are created while parsing, this should be overwritten in the drived class | |
7c673cae | 53 | virtual bool xml_end(const char *el); |
11fdf7f2 TL |
54 | // callback invoked for storing the data of the XML tag |
55 | // if data manipulation is needed this could be overwritten in the drived class | |
7c673cae | 56 | virtual void xml_handle_data(const char *s, int len); |
11fdf7f2 | 57 | // get the parent object |
7c673cae | 58 | XMLObj *get_parent(); |
11fdf7f2 TL |
59 | // add a child XML object |
60 | void add_child(const std::string& el, XMLObj *obj); | |
61 | ||
62 | public: | |
63 | XMLObj() : parent(nullptr) {} | |
64 | virtual ~XMLObj(); | |
65 | ||
66 | // get the data (as string) | |
67 | const std::string& get_data() const; | |
68 | // get the type of the object (as string) | |
69 | const std::string& get_obj_type() const; | |
70 | bool get_attr(const std::string& name, std::string& attr) const; | |
71 | // return a list of sub-tags matching the name | |
72 | XMLObjIter find(const std::string& name); | |
73 | // return the first sub-tag | |
74 | XMLObjIter find_first(); | |
75 | // return the first sub-tags matching the name | |
76 | XMLObj *find_first(const std::string& name); | |
7c673cae | 77 | |
20effc67 | 78 | friend std::ostream& operator<<(std::ostream &out, const XMLObj &obj); |
11fdf7f2 | 79 | friend RGWXMLParser; |
7c673cae FG |
80 | }; |
81 | ||
82 | struct XML_ParserStruct; | |
11fdf7f2 TL |
83 | |
84 | // an XML parser is an XML object without a parent (root of the tree) | |
85 | // the parser could be used in 2 ways: | |
86 | // | |
87 | // (1) lazy object creation/intrusive API: usually used within the RGWXMLDecode namespace (as RGWXMLDecode::XMLParser) | |
88 | // the parser will parse the input and store info, but will not generate the target object. The object can be allocated outside | |
89 | // of the parser (stack or heap), and require to implement the decode_xml() API for the values to be populated. | |
90 | // note that the decode_xml() calls may throw exceptions if parsing fails | |
91 | // | |
92 | // (2) object creation while parsing: a new class needs to be derived from RGWXMLParser and implement alloc_obj() | |
93 | // API that should create a set of classes derived from XMLObj implementing xml_end() to create the actual target objects | |
94 | // | |
95 | // There could be a mix-and-match of the 2 types, control over that is in the alloc_obj() call | |
96 | // deciding for which tags objects are allocate during parsing and for which tags object allocation is external | |
97 | ||
7c673cae FG |
98 | class RGWXMLParser : public XMLObj |
99 | { | |
11fdf7f2 | 100 | private: |
7c673cae FG |
101 | XML_ParserStruct *p; |
102 | char *buf; | |
103 | int buf_len; | |
104 | XMLObj *cur_obj; | |
11fdf7f2 TL |
105 | std::vector<XMLObj *> objs; |
106 | std::list<XMLObj *> allocated_objs; | |
107 | std::list<XMLObj> unallocated_objs; | |
108 | bool success; | |
109 | bool init_called; | |
110 | ||
111 | // calls xml_start() on each parsed object | |
112 | // passed as static callback to actual parser, passes itself as user_data | |
113 | static void call_xml_start(void* user_data, const char *el, const char **attr); | |
114 | // calls xml_end() on each parsed object | |
115 | // passed as static callback to actual parser, passes itself as user_data | |
116 | static void call_xml_end(void* user_data, const char *el); | |
117 | // calls xml_handle_data() on each parsed object | |
118 | // passed as static callback to actual parser, passes itself as user_data | |
119 | static void call_xml_handle_data(void* user_data, const char *s, int len); | |
120 | ||
7c673cae | 121 | protected: |
11fdf7f2 TL |
122 | // if objects are created while parsing, this should be implemented in the derived class |
123 | // and be a factory for creating the classes derived from XMLObj | |
1e59de90 | 124 | // note that not all sub-tags has to be constructed here, any such tag which is not |
11fdf7f2 TL |
125 | // constructed will be lazily created when decode_xml() is invoked on it |
126 | // | |
127 | // note that in case of different tags sharing the same name at different levels | |
128 | // this method should not be used | |
7c673cae | 129 | virtual XMLObj *alloc_obj(const char *el) { |
11fdf7f2 | 130 | return nullptr; |
7c673cae | 131 | } |
11fdf7f2 | 132 | |
7c673cae FG |
133 | public: |
134 | RGWXMLParser(); | |
1e59de90 | 135 | virtual ~RGWXMLParser() override; |
7c673cae | 136 | |
11fdf7f2 TL |
137 | // initialize the parser, must be called before parsing |
138 | bool init(); | |
139 | // parse the XML buffer (can be invoked multiple times for incremental parsing) | |
140 | // receives the buffer to parse, its length, and boolean indication (0,1) | |
141 | // whether this is the final chunk of the buffer | |
7c673cae | 142 | bool parse(const char *buf, int len, int done); |
11fdf7f2 TL |
143 | // get the XML blob being parsed |
144 | const char *get_xml() const { return buf; } | |
7c673cae FG |
145 | }; |
146 | ||
11fdf7f2 | 147 | namespace RGWXMLDecoder { |
9f95a23c TL |
148 | struct err : std::runtime_error { |
149 | using runtime_error::runtime_error; | |
7c673cae FG |
150 | }; |
151 | ||
11fdf7f2 | 152 | typedef RGWXMLParser XMLParser; |
7c673cae | 153 | |
11fdf7f2 TL |
154 | template<class T> |
155 | bool decode_xml(const char *name, T& val, XMLObj* obj, bool mandatory = false); | |
7c673cae FG |
156 | |
157 | template<class T> | |
11fdf7f2 | 158 | bool decode_xml(const char *name, std::vector<T>& v, XMLObj* obj, bool mandatory = false); |
7c673cae FG |
159 | |
160 | template<class C> | |
11fdf7f2 | 161 | bool decode_xml(const char *name, C& container, void (*cb)(C&, XMLObj *obj), XMLObj *obj, bool mandatory = false); |
7c673cae FG |
162 | |
163 | template<class T> | |
11fdf7f2 TL |
164 | void decode_xml(const char *name, T& val, T& default_val, XMLObj* obj); |
165 | } | |
166 | ||
20effc67 | 167 | static inline std::ostream& operator<<(std::ostream &out, RGWXMLDecoder::err& err) |
11fdf7f2 | 168 | { |
9f95a23c | 169 | return out << err.what(); |
11fdf7f2 | 170 | } |
7c673cae FG |
171 | |
172 | template<class T> | |
173 | void decode_xml_obj(T& val, XMLObj *obj) | |
174 | { | |
175 | val.decode_xml(obj); | |
176 | } | |
177 | ||
20effc67 | 178 | static inline void decode_xml_obj(std::string& val, XMLObj *obj) |
7c673cae FG |
179 | { |
180 | val = obj->get_data(); | |
181 | } | |
182 | ||
183 | void decode_xml_obj(unsigned long long& val, XMLObj *obj); | |
184 | void decode_xml_obj(long long& val, XMLObj *obj); | |
185 | void decode_xml_obj(unsigned long& val, XMLObj *obj); | |
186 | void decode_xml_obj(long& val, XMLObj *obj); | |
187 | void decode_xml_obj(unsigned& val, XMLObj *obj); | |
188 | void decode_xml_obj(int& val, XMLObj *obj); | |
189 | void decode_xml_obj(bool& val, XMLObj *obj); | |
190 | void decode_xml_obj(bufferlist& val, XMLObj *obj); | |
191 | class utime_t; | |
192 | void decode_xml_obj(utime_t& val, XMLObj *obj); | |
193 | ||
9f95a23c TL |
194 | template<class T> |
195 | void decode_xml_obj(std::optional<T>& val, XMLObj *obj) | |
196 | { | |
197 | val.emplace(); | |
198 | decode_xml_obj(*val, obj); | |
199 | } | |
200 | ||
7c673cae | 201 | template<class T> |
20effc67 | 202 | void do_decode_xml_obj(std::list<T>& l, const std::string& name, XMLObj *obj) |
7c673cae FG |
203 | { |
204 | l.clear(); | |
205 | ||
206 | XMLObjIter iter = obj->find(name); | |
207 | XMLObj *o; | |
208 | ||
209 | while ((o = iter.get_next())) { | |
210 | T val; | |
211 | decode_xml_obj(val, o); | |
212 | l.push_back(val); | |
213 | } | |
214 | } | |
215 | ||
7c673cae FG |
216 | template<class T> |
217 | bool RGWXMLDecoder::decode_xml(const char *name, T& val, XMLObj *obj, bool mandatory) | |
218 | { | |
219 | XMLObjIter iter = obj->find(name); | |
220 | XMLObj *o = iter.get_next(); | |
221 | if (!o) { | |
222 | if (mandatory) { | |
20effc67 | 223 | std::string s = "missing mandatory field " + std::string(name); |
7c673cae FG |
224 | throw err(s); |
225 | } | |
226 | val = T(); | |
227 | return false; | |
228 | } | |
229 | ||
230 | try { | |
231 | decode_xml_obj(val, o); | |
9f95a23c | 232 | } catch (const err& e) { |
20effc67 | 233 | std::string s = std::string(name) + ": "; |
9f95a23c | 234 | s.append(e.what()); |
7c673cae FG |
235 | throw err(s); |
236 | } | |
237 | ||
238 | return true; | |
239 | } | |
240 | ||
11fdf7f2 TL |
241 | template<class T> |
242 | bool RGWXMLDecoder::decode_xml(const char *name, std::vector<T>& v, XMLObj *obj, bool mandatory) | |
243 | { | |
244 | XMLObjIter iter = obj->find(name); | |
245 | XMLObj *o = iter.get_next(); | |
246 | ||
247 | v.clear(); | |
248 | ||
249 | if (!o) { | |
250 | if (mandatory) { | |
20effc67 | 251 | std::string s = "missing mandatory field " + std::string(name); |
11fdf7f2 TL |
252 | throw err(s); |
253 | } | |
254 | return false; | |
255 | } | |
256 | ||
257 | do { | |
258 | T val; | |
259 | try { | |
260 | decode_xml_obj(val, o); | |
9f95a23c | 261 | } catch (const err& e) { |
20effc67 | 262 | std::string s = std::string(name) + ": "; |
9f95a23c | 263 | s.append(e.what()); |
11fdf7f2 TL |
264 | throw err(s); |
265 | } | |
266 | v.push_back(val); | |
267 | } while ((o = iter.get_next())); | |
268 | return true; | |
269 | } | |
270 | ||
7c673cae FG |
271 | template<class C> |
272 | bool RGWXMLDecoder::decode_xml(const char *name, C& container, void (*cb)(C&, XMLObj *), XMLObj *obj, bool mandatory) | |
273 | { | |
274 | container.clear(); | |
275 | ||
276 | XMLObjIter iter = obj->find(name); | |
277 | XMLObj *o = iter.get_next(); | |
278 | if (!o) { | |
279 | if (mandatory) { | |
20effc67 | 280 | std::string s = "missing mandatory field " + std::string(name); |
7c673cae FG |
281 | throw err(s); |
282 | } | |
283 | return false; | |
284 | } | |
285 | ||
286 | try { | |
287 | decode_xml_obj(container, cb, o); | |
9f95a23c | 288 | } catch (const err& e) { |
20effc67 | 289 | std::string s = std::string(name) + ": "; |
9f95a23c | 290 | s.append(e.what()); |
7c673cae FG |
291 | throw err(s); |
292 | } | |
293 | ||
294 | return true; | |
295 | } | |
296 | ||
297 | template<class T> | |
298 | void RGWXMLDecoder::decode_xml(const char *name, T& val, T& default_val, XMLObj *obj) | |
299 | { | |
300 | XMLObjIter iter = obj->find(name); | |
301 | XMLObj *o = iter.get_next(); | |
302 | if (!o) { | |
303 | val = default_val; | |
304 | return; | |
305 | } | |
306 | ||
307 | try { | |
308 | decode_xml_obj(val, o); | |
9f95a23c | 309 | } catch (const err& e) { |
7c673cae | 310 | val = default_val; |
20effc67 | 311 | std::string s = std::string(name) + ": "; |
9f95a23c | 312 | s.append(e.what()); |
7c673cae FG |
313 | throw err(s); |
314 | } | |
315 | } | |
316 | ||
317 | template<class T> | |
318 | static void encode_xml(const char *name, const T& val, ceph::Formatter *f) | |
319 | { | |
320 | f->open_object_section(name); | |
321 | val.dump_xml(f); | |
322 | f->close_section(); | |
323 | } | |
324 | ||
11fdf7f2 TL |
325 | template<class T> |
326 | static void encode_xml(const char *name, const char *ns, const T& val, ceph::Formatter *f) | |
327 | { | |
328 | f->open_object_section_in_ns(name, ns); | |
329 | val.dump_xml(f); | |
330 | f->close_section(); | |
331 | } | |
332 | ||
20effc67 | 333 | void encode_xml(const char *name, const std::string& val, ceph::Formatter *f); |
7c673cae FG |
334 | void encode_xml(const char *name, const char *val, ceph::Formatter *f); |
335 | void encode_xml(const char *name, bool val, ceph::Formatter *f); | |
336 | void encode_xml(const char *name, int val, ceph::Formatter *f); | |
337 | void encode_xml(const char *name, unsigned val, ceph::Formatter *f); | |
338 | void encode_xml(const char *name, long val, ceph::Formatter *f); | |
339 | void encode_xml(const char *name, unsigned long val, ceph::Formatter *f); | |
340 | void encode_xml(const char *name, long long val, ceph::Formatter *f); | |
341 | void encode_xml(const char *name, const utime_t& val, ceph::Formatter *f); | |
342 | void encode_xml(const char *name, const bufferlist& bl, ceph::Formatter *f); | |
7c673cae FG |
343 | void encode_xml(const char *name, long long unsigned val, ceph::Formatter *f); |
344 | ||
345 | template<class T> | |
346 | static void do_encode_xml(const char *name, const std::list<T>& l, const char *entry_name, ceph::Formatter *f) | |
347 | { | |
348 | f->open_array_section(name); | |
349 | for (typename std::list<T>::const_iterator iter = l.begin(); iter != l.end(); ++iter) { | |
350 | encode_xml(entry_name, *iter, f); | |
351 | } | |
352 | f->close_section(); | |
353 | } | |
354 | ||
9f95a23c TL |
355 | template<class T> |
356 | static void encode_xml(const char *name, const std::vector<T>& l, ceph::Formatter *f) | |
357 | { | |
358 | for (typename std::vector<T>::const_iterator iter = l.begin(); iter != l.end(); ++iter) { | |
359 | encode_xml(name, *iter, f); | |
360 | } | |
361 | } | |
362 | ||
363 | template<class T> | |
364 | static void encode_xml(const char *name, const std::optional<T>& o, ceph::Formatter *f) | |
365 | { | |
366 | if (!o) { | |
367 | return; | |
368 | } | |
369 | ||
370 | encode_xml(name, *o, f); | |
371 | } |