summaryrefslogtreecommitdiffstats
path: root/src/rgw/rgw_xml.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/rgw/rgw_xml.h')
-rw-r--r--src/rgw/rgw_xml.h371
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);
+}