diff options
Diffstat (limited to 'src/rgw/rgw_xml.h')
-rw-r--r-- | src/rgw/rgw_xml.h | 371 |
1 files changed, 371 insertions, 0 deletions
diff --git a/src/rgw/rgw_xml.h b/src/rgw/rgw_xml.h new file mode 100644 index 000000000..74a8c27a0 --- /dev/null +++ b/src/rgw/rgw_xml.h @@ -0,0 +1,371 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab ft=cpp + +#pragma once + +#include <map> +#include <stdexcept> +#include <string> +#include <iosfwd> +#include <include/types.h> +#include <common/Formatter.h> + +class XMLObj; +class RGWXMLParser; + +class XMLObjIter { +public: + typedef std::map<std::string, XMLObj *>::iterator map_iter_t; + typedef std::map<std::string, XMLObj *>::iterator const_map_iter_t; + + XMLObjIter(); + virtual ~XMLObjIter(); + void set(const XMLObjIter::const_map_iter_t &_cur, const XMLObjIter::const_map_iter_t &_end); + XMLObj *get_next(); + bool get_name(std::string& name) const; + +private: + map_iter_t cur; + map_iter_t end; +}; + +/** + * Represents a block of XML. + * Give the class an XML blob, and it will parse the blob into + * an attr_name->value map. + * It shouldn't be the start point for any parsing. Look at RGWXMLParser for that. + */ +class XMLObj +{ +private: + XMLObj *parent; + std::string obj_type; + +protected: + std::string data; + std::multimap<std::string, XMLObj *> children; + std::map<std::string, std::string> attr_map; + + // invoked at the beginning of the XML tag, and populate any attributes + bool xml_start(XMLObj *parent, const char *el, const char **attr); + // callback invoked at the end of the XML tag + // if objects are created while parsing, this should be overwritten in the drived class + virtual bool xml_end(const char *el); + // callback invoked for storing the data of the XML tag + // if data manipulation is needed this could be overwritten in the drived class + virtual void xml_handle_data(const char *s, int len); + // get the parent object + XMLObj *get_parent(); + // add a child XML object + void add_child(const std::string& el, XMLObj *obj); + +public: + XMLObj() : parent(nullptr) {} + virtual ~XMLObj(); + + // get the data (as string) + const std::string& get_data() const; + // get the type of the object (as string) + const std::string& get_obj_type() const; + bool get_attr(const std::string& name, std::string& attr) const; + // return a list of sub-tags matching the name + XMLObjIter find(const std::string& name); + // return the first sub-tag + XMLObjIter find_first(); + // return the first sub-tags matching the name + XMLObj *find_first(const std::string& name); + + friend std::ostream& operator<<(std::ostream &out, const XMLObj &obj); + friend RGWXMLParser; +}; + +struct XML_ParserStruct; + +// an XML parser is an XML object without a parent (root of the tree) +// the parser could be used in 2 ways: +// +// (1) lazy object creation/intrusive API: usually used within the RGWXMLDecode namespace (as RGWXMLDecode::XMLParser) +// the parser will parse the input and store info, but will not generate the target object. The object can be allocated outside +// of the parser (stack or heap), and require to implement the decode_xml() API for the values to be populated. +// note that the decode_xml() calls may throw exceptions if parsing fails +// +// (2) object creation while parsing: a new class needs to be derived from RGWXMLParser and implement alloc_obj() +// API that should create a set of classes derived from XMLObj implementing xml_end() to create the actual target objects +// +// There could be a mix-and-match of the 2 types, control over that is in the alloc_obj() call +// deciding for which tags objects are allocate during parsing and for which tags object allocation is external + +class RGWXMLParser : public XMLObj +{ +private: + XML_ParserStruct *p; + char *buf; + int buf_len; + XMLObj *cur_obj; + std::vector<XMLObj *> objs; + std::list<XMLObj *> allocated_objs; + std::list<XMLObj> unallocated_objs; + bool success; + bool init_called; + + // calls xml_start() on each parsed object + // passed as static callback to actual parser, passes itself as user_data + static void call_xml_start(void* user_data, const char *el, const char **attr); + // calls xml_end() on each parsed object + // passed as static callback to actual parser, passes itself as user_data + static void call_xml_end(void* user_data, const char *el); + // calls xml_handle_data() on each parsed object + // passed as static callback to actual parser, passes itself as user_data + static void call_xml_handle_data(void* user_data, const char *s, int len); + +protected: + // if objects are created while parsing, this should be implemented in the derived class + // and be a factory for creating the classes derived from XMLObj + // note that not all sub-tags has to be constructed here, any such tag which is not + // constructed will be lazily created when decode_xml() is invoked on it + // + // note that in case of different tags sharing the same name at different levels + // this method should not be used + virtual XMLObj *alloc_obj(const char *el) { + return nullptr; + } + +public: + RGWXMLParser(); + virtual ~RGWXMLParser() override; + + // initialize the parser, must be called before parsing + bool init(); + // parse the XML buffer (can be invoked multiple times for incremental parsing) + // receives the buffer to parse, its length, and boolean indication (0,1) + // whether this is the final chunk of the buffer + bool parse(const char *buf, int len, int done); + // get the XML blob being parsed + const char *get_xml() const { return buf; } +}; + +namespace RGWXMLDecoder { + struct err : std::runtime_error { + using runtime_error::runtime_error; + }; + + typedef RGWXMLParser XMLParser; + + template<class T> + bool decode_xml(const char *name, T& val, XMLObj* obj, bool mandatory = false); + + template<class T> + bool decode_xml(const char *name, std::vector<T>& v, XMLObj* obj, bool mandatory = false); + + template<class C> + bool decode_xml(const char *name, C& container, void (*cb)(C&, XMLObj *obj), XMLObj *obj, bool mandatory = false); + + template<class T> + void decode_xml(const char *name, T& val, T& default_val, XMLObj* obj); +} + +static inline std::ostream& operator<<(std::ostream &out, RGWXMLDecoder::err& err) +{ + return out << err.what(); +} + +template<class T> +void decode_xml_obj(T& val, XMLObj *obj) +{ + val.decode_xml(obj); +} + +static inline void decode_xml_obj(std::string& val, XMLObj *obj) +{ + val = obj->get_data(); +} + +void decode_xml_obj(unsigned long long& val, XMLObj *obj); +void decode_xml_obj(long long& val, XMLObj *obj); +void decode_xml_obj(unsigned long& val, XMLObj *obj); +void decode_xml_obj(long& val, XMLObj *obj); +void decode_xml_obj(unsigned& val, XMLObj *obj); +void decode_xml_obj(int& val, XMLObj *obj); +void decode_xml_obj(bool& val, XMLObj *obj); +void decode_xml_obj(bufferlist& val, XMLObj *obj); +class utime_t; +void decode_xml_obj(utime_t& val, XMLObj *obj); + +template<class T> +void decode_xml_obj(std::optional<T>& val, XMLObj *obj) +{ + val.emplace(); + decode_xml_obj(*val, obj); +} + +template<class T> +void do_decode_xml_obj(std::list<T>& l, const std::string& name, XMLObj *obj) +{ + l.clear(); + + XMLObjIter iter = obj->find(name); + XMLObj *o; + + while ((o = iter.get_next())) { + T val; + decode_xml_obj(val, o); + l.push_back(val); + } +} + +template<class T> +bool RGWXMLDecoder::decode_xml(const char *name, T& val, XMLObj *obj, bool mandatory) +{ + XMLObjIter iter = obj->find(name); + XMLObj *o = iter.get_next(); + if (!o) { + if (mandatory) { + std::string s = "missing mandatory field " + std::string(name); + throw err(s); + } + val = T(); + return false; + } + + try { + decode_xml_obj(val, o); + } catch (const err& e) { + std::string s = std::string(name) + ": "; + s.append(e.what()); + throw err(s); + } + + return true; +} + +template<class T> +bool RGWXMLDecoder::decode_xml(const char *name, std::vector<T>& v, XMLObj *obj, bool mandatory) +{ + XMLObjIter iter = obj->find(name); + XMLObj *o = iter.get_next(); + + v.clear(); + + if (!o) { + if (mandatory) { + std::string s = "missing mandatory field " + std::string(name); + throw err(s); + } + return false; + } + + do { + T val; + try { + decode_xml_obj(val, o); + } catch (const err& e) { + std::string s = std::string(name) + ": "; + s.append(e.what()); + throw err(s); + } + v.push_back(val); + } while ((o = iter.get_next())); + return true; +} + +template<class C> +bool RGWXMLDecoder::decode_xml(const char *name, C& container, void (*cb)(C&, XMLObj *), XMLObj *obj, bool mandatory) +{ + container.clear(); + + XMLObjIter iter = obj->find(name); + XMLObj *o = iter.get_next(); + if (!o) { + if (mandatory) { + std::string s = "missing mandatory field " + std::string(name); + throw err(s); + } + return false; + } + + try { + decode_xml_obj(container, cb, o); + } catch (const err& e) { + std::string s = std::string(name) + ": "; + s.append(e.what()); + throw err(s); + } + + return true; +} + +template<class T> +void RGWXMLDecoder::decode_xml(const char *name, T& val, T& default_val, XMLObj *obj) +{ + XMLObjIter iter = obj->find(name); + XMLObj *o = iter.get_next(); + if (!o) { + val = default_val; + return; + } + + try { + decode_xml_obj(val, o); + } catch (const err& e) { + val = default_val; + std::string s = std::string(name) + ": "; + s.append(e.what()); + throw err(s); + } +} + +template<class T> +static void encode_xml(const char *name, const T& val, ceph::Formatter *f) +{ + f->open_object_section(name); + val.dump_xml(f); + f->close_section(); +} + +template<class T> +static void encode_xml(const char *name, const char *ns, const T& val, ceph::Formatter *f) +{ + f->open_object_section_in_ns(name, ns); + val.dump_xml(f); + f->close_section(); +} + +void encode_xml(const char *name, const std::string& val, ceph::Formatter *f); +void encode_xml(const char *name, const char *val, ceph::Formatter *f); +void encode_xml(const char *name, bool val, ceph::Formatter *f); +void encode_xml(const char *name, int val, ceph::Formatter *f); +void encode_xml(const char *name, unsigned val, ceph::Formatter *f); +void encode_xml(const char *name, long val, ceph::Formatter *f); +void encode_xml(const char *name, unsigned long val, ceph::Formatter *f); +void encode_xml(const char *name, long long val, ceph::Formatter *f); +void encode_xml(const char *name, const utime_t& val, ceph::Formatter *f); +void encode_xml(const char *name, const bufferlist& bl, ceph::Formatter *f); +void encode_xml(const char *name, long long unsigned val, ceph::Formatter *f); + +template<class T> +static void do_encode_xml(const char *name, const std::list<T>& l, const char *entry_name, ceph::Formatter *f) +{ + f->open_array_section(name); + for (typename std::list<T>::const_iterator iter = l.begin(); iter != l.end(); ++iter) { + encode_xml(entry_name, *iter, f); + } + f->close_section(); +} + +template<class T> +static void encode_xml(const char *name, const std::vector<T>& l, ceph::Formatter *f) +{ + for (typename std::vector<T>::const_iterator iter = l.begin(); iter != l.end(); ++iter) { + encode_xml(name, *iter, f); + } +} + +template<class T> +static void encode_xml(const char *name, const std::optional<T>& o, ceph::Formatter *f) +{ + if (!o) { + return; + } + + encode_xml(name, *o, f); +} |