]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
common/formatter: json formattable is now also a formatter
authorYehuda Sadeh <yehuda@redhat.com>
Mon, 23 Jul 2018 23:39:30 +0000 (16:39 -0700)
committerYehuda Sadeh <yehuda@redhat.com>
Tue, 11 Dec 2018 08:10:42 +0000 (00:10 -0800)
This way we can construct a formattable structure by encoding
objects into it.

Signed-off-by: Yehuda Sadeh <yehuda@redhat.com>
src/common/Formatter.cc
src/common/Formatter.h
src/common/ceph_json.cc
src/common/ceph_json.h
src/test/common/test_json_formattable.cc

index 34c81317263209d98f5a42da6bde90f83d9f0899..2a013e18d6b4248860e580b3ffd4b12f37f97b36 100644 (file)
@@ -186,9 +186,18 @@ void JSONFormatter::print_name(const char *name)
   ++entry.size;
 }
 
-void JSONFormatter::open_section(const char *name, bool is_array)
+void JSONFormatter::open_section(const char *name, const char *ns, bool is_array)
 {
-  print_name(name);
+  if (handle_open_section(name, ns, is_array)) {
+    return;
+  }
+  if (ns) {
+    std::ostringstream oss;
+    oss << name << " " << ns;
+    print_name(oss.str().c_str());
+  } else {
+    print_name(name);
+  }
   if (is_array)
     m_ss << '[';
   else
@@ -201,30 +210,30 @@ void JSONFormatter::open_section(const char *name, bool is_array)
 
 void JSONFormatter::open_array_section(const char *name)
 {
-  open_section(name, true);
+  open_section(name, nullptr, true);
 }
 
 void JSONFormatter::open_array_section_in_ns(const char *name, const char *ns)
 {
-  std::ostringstream oss;
-  oss << name << " " << ns;
-  open_section(oss.str().c_str(), true);
+  open_section(name, ns, true);
 }
 
 void JSONFormatter::open_object_section(const char *name)
 {
-  open_section(name, false);
+  open_section(name, nullptr, false);
 }
 
 void JSONFormatter::open_object_section_in_ns(const char *name, const char *ns)
 {
-  std::ostringstream oss;
-  oss << name << " " << ns;
-  open_section(oss.str().c_str(), false);
+  open_section(name, ns, false);
 }
 
 void JSONFormatter::close_section()
 {
+
+  if (handle_close_section()) {
+    return;
+  }
   ceph_assert(!m_stack.empty());
   finish_pending_string();
 
@@ -243,41 +252,57 @@ void JSONFormatter::close_section()
 void JSONFormatter::finish_pending_string()
 {
   if (m_is_pending_string) {
-    print_quoted_string(m_pending_string.str());
-    m_pending_string.str(std::string());
     m_is_pending_string = false;
+    add_value(m_pending_name.c_str(), m_pending_string.str(), true);
   }
 }
 
-void JSONFormatter::dump_unsigned(const char *name, uint64_t u)
+template <class T>
+void JSONFormatter::add_value(const char *name, T val)
+{
+  std::stringstream ss;
+  ss << val;
+  add_value(name, ss.str(), false);
+}
+
+void JSONFormatter::add_value(const char *name, std::string_view val, bool quoted)
 {
+  if (handle_value(name, val, quoted)) {
+    return;
+  }
   print_name(name);
-  m_ss << u;
+  if (!quoted) {
+    m_ss << val;
+  } else {
+    print_quoted_string(val);
+  }
+}
+
+void JSONFormatter::dump_unsigned(const char *name, uint64_t u)
+{
+  add_value(name, u);
 }
 
 void JSONFormatter::dump_int(const char *name, int64_t s)
 {
-  print_name(name);
-  m_ss << s;
+  add_value(name, s);
 }
 
 void JSONFormatter::dump_float(const char *name, double d)
 {
-  print_name(name);
   char foo[30];
   snprintf(foo, sizeof(foo), "%lf", d);
-  m_ss << foo;
+  add_value(name, foo, false);
 }
 
 void JSONFormatter::dump_string(const char *name, std::string_view s)
 {
-  print_name(name);
-  print_quoted_string(s);
+  add_value(name, s, true);
 }
 
 std::ostream& JSONFormatter::dump_stream(const char *name)
 {
-  print_name(name);
+  m_pending_name = name;
   m_is_pending_string = true;
   return m_pending_string;
 }
@@ -287,12 +312,7 @@ void JSONFormatter::dump_format_va(const char *name, const char *ns, bool quoted
   char buf[LARGE_SIZE];
   vsnprintf(buf, LARGE_SIZE, fmt, ap);
 
-  print_name(name);
-  if (quoted) {
-    print_quoted_string(std::string(buf));
-  } else {
-    m_ss << std::string(buf);
-  }
+  add_value(name, buf, quoted);
 }
 
 int JSONFormatter::get_len() const
index 4a9ac3210e083104ff3538affbeb8c8f6cb1570b..9d984c11f87512e4bc4ddcafd9b7d06339e5e65d 100644 (file)
@@ -110,6 +110,18 @@ namespace ceph {
     }
   };
 
+  class copyable_sstream : public std::stringstream {
+  public:
+    copyable_sstream() {}
+    copyable_sstream(const copyable_sstream& rhs) {
+      str(rhs.str());
+    }
+    copyable_sstream& operator=(const copyable_sstream& rhs) {
+      str(rhs.str());
+      return *this;
+    }
+  };
+
   class JSONFormatter : public Formatter {
   public:
     explicit JSONFormatter(bool p = false);
@@ -135,6 +147,19 @@ namespace ceph {
     int get_len() const override;
     void write_raw_data(const char *data) override;
 
+  protected:
+    virtual bool handle_value(const char *name, std::string_view s, bool quoted) {
+      return false; /* is handling done? */
+    }
+
+    virtual bool handle_open_section(const char *name, const char *ns, bool is_array) {
+      return false; /* is handling done? */
+    }
+
+    virtual bool handle_close_section() {
+      return false; /* is handling done? */
+    }
+
   private:
 
     struct json_formatter_stack_entry_d {
@@ -144,18 +169,27 @@ namespace ceph {
     };
 
     bool m_pretty;
-    void open_section(const char *name, bool is_array);
+    void open_section(const char *name, const char *ns, bool is_array);
     void print_quoted_string(std::string_view s);
     void print_name(const char *name);
     void print_comma(json_formatter_stack_entry_d& entry);
     void finish_pending_string();
 
-    std::stringstream m_ss, m_pending_string;
+    template <class T>
+    void add_value(const char *name, T val);
+    void add_value(const char *name, std::string_view val, bool quoted);
+
+    copyable_sstream m_ss;
+    copyable_sstream m_pending_string;
+    std::string m_pending_name;
     std::list<json_formatter_stack_entry_d> m_stack;
     bool m_is_pending_string;
     bool m_line_break_enabled = false;
   };
 
+  template <class T>
+  void add_value(const char *name, T val);
+
   class XMLFormatter : public Formatter {
   public:
     static const char *XML_1_DTD;
@@ -254,6 +288,5 @@ namespace ceph {
     std::vector< std::string > m_column_name;
   };
 
-
 }
 #endif
index 612ecccbfba151bb30b342afbc7c99bc371d1690..0203f51f988d7d3f37501a824968b60f4ae03d25 100644 (file)
@@ -7,6 +7,9 @@
 
 #include <boost/algorithm/string.hpp>
 #include <boost/tokenizer.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include "json_spirit/json_spirit_writer_template.h"
 
 using namespace json_spirit;
 
@@ -16,6 +19,15 @@ using namespace json_spirit;
 static JSONFormattable default_formattable;
 
 
+void encode_json(const char *name, const JSONObj::data_val& v, Formatter *f)
+{
+  if (v.quoted) {
+    encode_json(name, v.str, f);
+  } else {
+    f->dump_format_unquoted(name, "%s", v.str.c_str());
+  }
+}
+
 JSONObjIter::JSONObjIter()
 {
 }
@@ -43,7 +55,7 @@ JSONObj *JSONObjIter::operator*()
 
 // does not work, FIXME
 ostream& operator<<(ostream &out, const JSONObj &obj) {
-   out << obj.name << ": " << obj.data_string;
+   out << obj.name << ": " << obj.val;
    return out;
 }
 
@@ -62,9 +74,9 @@ void JSONObj::add_child(string el, JSONObj *obj)
   children.insert(pair<string, JSONObj *>(el, obj));
 }
 
-bool JSONObj::get_attr(string name, string& attr)
+bool JSONObj::get_attr(string name, data_val& attr)
 {
-  map<string, string>::iterator iter = attr_map.find(name);
+  auto iter = attr_map.find(name);
   if (iter == attr_map.end())
     return false;
   attr = iter->second;
@@ -109,13 +121,13 @@ JSONObj *JSONObj::find_obj(const string& name)
   return *iter;
 }
 
-bool JSONObj::get_data(const string& key, string *dest)
+bool JSONObj::get_data(const string& key, data_val *dest)
 {
   JSONObj *obj = find_obj(key);
   if (!obj)
     return false;
 
-  *dest = obj->get_data();
+  *dest = obj->get_data_val();
 
   return true;
 }
@@ -158,11 +170,15 @@ void JSONObj::init(JSONObj *p, Value v, string n)
   data = v;
 
   handle_value(v);
-  if (v.type() == str_type)
-    data_string =  v.get_str();
-  else
-    data_string =  write(v, raw_utf8);
-  attr_map.insert(pair<string,string>(name, data_string));
+  if (v.type() != obj_type &&
+      v.type() != array_type) {
+    if (v.type() == str_type) {
+      val.set(v.get_str(), true);
+    } else {
+      val.set(json_spirit::write_string(v), false);
+    }
+  }
+  attr_map.insert(pair<string,data_val>(name, val));
 }
 
 JSONObj *JSONObj::get_parent()
@@ -230,10 +246,11 @@ bool JSONParser::parse(const char *buf_, int len)
     handle_value(data);
     if (data.type() != obj_type &&
         data.type() != array_type) {
-      if (data.type() == str_type)
-        data_string =  data.get_str();
-      else
-        data_string =  write(data, raw_utf8);
+      if (data.type() == str_type) {
+        val.set(data.get_str(), true);
+      } else {
+        val.set(json_spirit::write_string(data), false);
+      }
     }
   } else {
     set_failure();
@@ -590,14 +607,14 @@ bool JSONFormattable::find(const string& name, string *val) const
 }
 
 int JSONFormattable::val_int() const {
-  return atoi(str.c_str());
+  return atoi(value.str.c_str());
 }
 
 bool JSONFormattable::val_bool() const {
-  return (boost::iequals(str, "true") ||
-          boost::iequals(str, "on") ||
-          boost::iequals(str, "yes") ||
-          boost::iequals(str, "1"));
+  return (boost::iequals(value.str, "true") ||
+          boost::iequals(value.str, "on") ||
+          boost::iequals(value.str, "yes") ||
+          boost::iequals(value.str, "1"));
 }
 
 string JSONFormattable::def(const string& def_val) const {
@@ -685,6 +702,16 @@ static int parse_entity(const string& s, vector<field_entity> *result)
   return 0;
 }
 
+static bool is_numeric(const string& val)
+{
+  try {
+    boost::lexical_cast<double>(val);
+  } catch (const boost::bad_lexical_cast& e) {
+    return false;
+  }
+  return true;
+}
+
 int JSONFormattable::set(const string& name, const string& val)
 {
   boost::escaped_list_separator<char> els('\\', '.', '"');
@@ -740,8 +767,8 @@ int JSONFormattable::set(const string& name, const string& val)
   if (is_valid_json) {
     f->decode_json(&jp);
   } else {
-    f->type = FMT_STRING;
-    f->str = val;
+    f->type = FMT_VALUE;
+    f->value.set(val, !is_numeric(val));
   }
 
   return 0;
@@ -764,7 +791,7 @@ int JSONFormattable::erase(const string& name)
     }
     for (const auto& vi : v) {
       if (f->type == FMT_NONE ||
-          f->type == FMT_STRING) {
+          f->type == FMT_VALUE) {
         if (vi.is_obj) {
           f->type = FMT_OBJ;
         } else {
@@ -831,17 +858,22 @@ void JSONFormattable::derive_from(const JSONFormattable& parent)
 
 void encode_json(const char *name, const JSONFormattable& v, Formatter *f)
 {
-  switch (v.type) {
-    case JSONFormattable::FMT_STRING:
-      encode_json(name, v.str, f);
+  v.encode_json(name, f);
+}
+
+void JSONFormattable::encode_json(const char *name, Formatter *f) const
+{
+  switch (type) {
+    case JSONFormattable::FMT_VALUE:
+      ::encode_json(name, value, f);
       break;
     case JSONFormattable::FMT_ARRAY:
-      encode_json(name, v.arr, f);
+      ::encode_json(name, arr, f);
       break;
     case JSONFormattable::FMT_OBJ:
       f->open_object_section(name);
-      for (auto iter : v.obj) {
-        encode_json(iter.first.c_str(), iter.second, f);
+      for (auto iter : obj) {
+        ::encode_json(iter.first.c_str(), iter.second, f);
       }
       f->close_section();
       break;
index d021f888b91bbb7c11b70673e613041a4ece4336..195284c60d3a3fdaae6e61329c42d7ef62fe815d 100644 (file)
@@ -43,12 +43,23 @@ public:
 class JSONObj
 {
   JSONObj *parent;
+public:
+  struct data_val {
+    string str;
+    bool quoted{false};
+
+    void set(std::string_view s, bool q) {
+      str = s;
+      quoted = q;
+    }
+  };
 protected:
   string name; // corresponds to obj_type in XMLObj
   Value data;
-  string data_string;
+  struct data_val val;
+  bool data_quoted{false};
   multimap<string, JSONObj *> children;
-  map<string, string> attr_map;
+  map<string, data_val> attr_map;
   void handle_value(Value v);
 
 public:
@@ -60,11 +71,12 @@ public:
   void init(JSONObj *p, Value v, string n);
 
   string& get_name() { return name; }
-  string& get_data() { return data_string; }
-  bool get_data(const string& key, string *dest);
+  data_val& get_data_val() { return val; }
+  const string& get_data() { return val.str; }
+  bool get_data(const string& key, data_val *dest);
   JSONObj *get_parent();
   void add_child(string el, JSONObj *child);
-  bool get_attr(string name, string& attr);
+  bool get_attr(string name, data_val& attr);
   JSONObjIter find(const string& name);
   JSONObjIter find_first();
   JSONObjIter find_first(const string& name);
@@ -78,6 +90,12 @@ public:
   vector<string> get_array_elements();
 };
 
+static inline ostream& operator<<(ostream &out, const JSONObj::data_val& dv) {
+  const char *q = (dv.quoted ? "\"" : "");
+   out << q << dv.str << q;
+   return out;
+}
+
 class JSONParser : public JSONObj
 {
   int buf_len;
@@ -97,6 +115,7 @@ public:
   void set_failure() { success = false; }
 };
 
+void encode_json(const char *name, const JSONObj::data_val& v, Formatter *f);
 
 class JSONDecoder {
 public:
@@ -136,6 +155,11 @@ static inline void decode_json_obj(string& val, JSONObj *obj)
   val = obj->get_data();
 }
 
+static inline void decode_json_obj(JSONObj::data_val& val, JSONObj *obj)
+{
+  val = obj->get_data_val();
+}
+
 void decode_json_obj(unsigned long long& val, JSONObj *obj);
 void decode_json_obj(long long& val, JSONObj *obj);
 void decode_json_obj(unsigned long& val, JSONObj *obj);
@@ -467,56 +491,117 @@ void encode_json_map(const char *name, const char *index_name, const char *value
   encode_json_map<K, V>(name, index_name, NULL, value_name, NULL, NULL, m, f);
 }
 
-struct JSONFormattable {
+class JSONFormattable : public ceph::JSONFormatter {
+  JSONObj::data_val value;
+  vector<JSONFormattable> arr;
+  map<std::string, JSONFormattable> obj;
+
+  vector<JSONFormattable *> enc_stack;
+  JSONFormattable *cur_enc;
+
+protected:
+  bool handle_value(const char *name, std::string_view s, bool quoted) override {
+    JSONFormattable *new_val;
+    if (cur_enc->is_array()) {
+      cur_enc->arr.push_back(JSONFormattable());
+      new_val = &cur_enc->arr.back();
+    } else {
+      new_val  = &cur_enc->obj[name];
+    }
+    new_val->set_type(JSONFormattable::FMT_VALUE);
+    new_val->value.set(s, quoted);
+
+    return false;
+  }
+  bool handle_open_section(const char *name, const char *ns, bool section_is_array) override {
+    if (cur_enc->is_array()) {
+      cur_enc->arr.push_back(JSONFormattable());
+      cur_enc = &cur_enc->arr.back();
+    } else {
+      cur_enc = &obj[name];
+    }
+    enc_stack.push_back(cur_enc);
+
+    if (section_is_array) {
+      cur_enc->set_type(JSONFormattable::FMT_ARRAY);
+    } else {
+      cur_enc->set_type(JSONFormattable::FMT_OBJ);
+    }
+
+    return false; /* continue processing */
+  }
+  bool handle_close_section() override {
+    if (enc_stack.size() <= 1) {
+      return false;
+    }
+
+    enc_stack.pop_back();
+    cur_enc = enc_stack.back();
+    return false; /* continue processing */
+  }
+
+public:
+  JSONFormattable(bool p = false) : JSONFormatter(p) {
+    cur_enc = this;
+    enc_stack.push_back(cur_enc);
+  }
+
   enum Type {
     FMT_NONE,
-    FMT_STRING,
+    FMT_VALUE,
     FMT_ARRAY,
     FMT_OBJ,
   } type{FMT_NONE};
 
-  string str;
-  vector<JSONFormattable> arr;
-  map<std::string, JSONFormattable> obj;
+  void set_type(Type t) {
+    type = t;
+  }
+
   void decode_json(JSONObj *jo) {
     if (jo->is_array()) {
-      type = JSONFormattable::FMT_ARRAY;
+      set_type(JSONFormattable::FMT_ARRAY);
       decode_json_obj(arr, jo);
     } else if (jo->is_object()) {
-      type = JSONFormattable::FMT_OBJ;
+      set_type(JSONFormattable::FMT_OBJ);
       auto iter = jo->find_first();
       for (;!iter.end(); ++iter) {
         JSONObj *field = *iter;
         decode_json_obj(obj[field->get_name()], field);
       }
     } else {
-      type = JSONFormattable::FMT_STRING;
-      decode_json_obj(str, jo);
+      set_type(JSONFormattable::FMT_VALUE);
+      decode_json_obj(value, jo);
     }
   }
 
   void encode(bufferlist& bl) const {
-    ENCODE_START(1, 1, bl);
+    ENCODE_START(2, 1, bl);
     encode((uint8_t)type, bl);
-    encode(str, bl);
+    encode(value.str, bl);
     encode(arr, bl);
     encode(obj, bl);
+    encode(value.quoted, bl);
     ENCODE_FINISH(bl);
   }
 
   void decode(bufferlist::const_iterator& bl) {
-    DECODE_START(1, bl);
+    DECODE_START(2, bl);
     uint8_t t;
     decode(t, bl);
     type = (Type)t;
-    decode(str, bl);
+    decode(value.str, bl);
     decode(arr, bl);
     decode(obj, bl);
+    if (struct_v >= 2) {
+      decode(value.quoted, bl);
+    } else {
+      value.quoted = true;
+    }
     DECODE_FINISH(bl);
   }
 
   const std::string& val() const {
-    return str;
+    return value.str;
   }
 
   int val_int() const;
@@ -533,7 +618,7 @@ struct JSONFormattable {
   JSONFormattable& operator[](size_t index);
 
   operator std::string() const {
-    return str;
+    return value.str;
   }
 
   explicit operator int() const {
@@ -558,12 +643,6 @@ struct JSONFormattable {
     return def(string(def_val));
   }
 
-#if 0
-  string operator ()(const string& def_val) const {
-    return def(def_val);
-  }
-#endif
-
   int operator()(int def_val) const {
     return def(def_val);
   }
@@ -590,6 +669,12 @@ struct JSONFormattable {
   int erase(const string& name);
 
   void derive_from(const JSONFormattable& jf);
+
+  void encode_json(const char *name, Formatter *f) const;
+
+  bool is_array() const {
+    return (type == FMT_ARRAY);
+  }
 };
 WRITE_CLASS_ENCODER(JSONFormattable)
 
index 5cb6505300de8cd861af5caf58d99b4c4845f0ea..b6a7d37b286a654e0620f0b0a298f63ca3d08a96 100644 (file)
@@ -26,6 +26,9 @@ static void get_jf(const string& s, JSONFormattable *f)
 {
   JSONParser p;
   bool result = p.parse(s.c_str(), s.size());
+  if (!result) {
+    cout << "Failed to parse: '" << s << "'" << std::endl;
+  }
   ASSERT_EQ(true, result);
   try {
     decode_json_obj(*f, &p);