1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Ceph - scalable distributed file system
6 * Copyright (C) 2011 New Dream Network
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
15 #define LARGE_SIZE 1024
18 #include "Formatter.h"
19 #include "HTMLFormatter.h"
20 #include "common/escape.h"
21 #include "include/buffer.h"
30 #include <boost/format.hpp>
33 // -----------------------
37 * FormatterAttrs(const char *attr, ...)
39 * Requires a list of attrs followed by NULL. The attrs should be char *
40 * pairs, first one is the name, second one is the value. E.g.,
42 * FormatterAttrs("name1", "value1", "name2", "value2", NULL);
44 FormatterAttrs::FormatterAttrs(const char *attr
, ...)
50 const char *val
= va_arg(ap
, char *);
54 attrs
.push_back(make_pair(std::string(s
), std::string(val
)));
55 s
= va_arg(ap
, char *);
60 Formatter::Formatter() { }
62 Formatter::~Formatter() { }
64 Formatter
*Formatter::create(const std::string
&type
,
65 const std::string
& default_type
,
66 const std::string
& fallback
)
68 std::string mytype
= type
;
70 mytype
= default_type
;
73 return new JSONFormatter(false);
74 else if (mytype
== "json-pretty")
75 return new JSONFormatter(true);
76 else if (mytype
== "xml")
77 return new XMLFormatter(false);
78 else if (mytype
== "xml-pretty")
79 return new XMLFormatter(true);
80 else if (mytype
== "table")
81 return new TableFormatter();
82 else if (mytype
== "table-kv")
83 return new TableFormatter(true);
84 else if (mytype
== "html")
85 return new HTMLFormatter(false);
86 else if (mytype
== "html-pretty")
87 return new HTMLFormatter(true);
88 else if (fallback
!= "")
89 return create(fallback
, "", "");
91 return (Formatter
*) NULL
;
95 void Formatter::flush(bufferlist
&bl
)
102 void Formatter::dump_format(const char *name
, const char *fmt
, ...)
106 dump_format_va(name
, NULL
, true, fmt
, ap
);
110 void Formatter::dump_format_ns(const char *name
, const char *ns
, const char *fmt
, ...)
114 dump_format_va(name
, ns
, true, fmt
, ap
);
119 void Formatter::dump_format_unquoted(const char *name
, const char *fmt
, ...)
123 dump_format_va(name
, NULL
, false, fmt
, ap
);
127 // -----------------------
129 JSONFormatter::JSONFormatter(bool p
)
130 : m_pretty(p
), m_is_pending_string(false)
135 void JSONFormatter::flush(std::ostream
& os
)
137 finish_pending_string();
143 void JSONFormatter::reset()
148 m_pending_string
.clear();
149 m_pending_string
.str("");
152 void JSONFormatter::print_comma(json_formatter_stack_entry_d
& entry
)
157 for (unsigned i
= 1; i
< m_stack
.size(); i
++)
162 } else if (m_pretty
) {
164 for (unsigned i
= 1; i
< m_stack
.size(); i
++)
167 if (m_pretty
&& entry
.is_array
)
171 void JSONFormatter::print_quoted_string(const std::string
& s
)
173 int len
= escape_json_attr_len(s
.c_str(), s
.size());
175 escape_json_attr(s
.c_str(), s
.size(), escaped
);
176 m_ss
<< '\"' << escaped
<< '\"';
179 void JSONFormatter::print_name(const char *name
)
181 finish_pending_string();
184 struct json_formatter_stack_entry_d
& entry
= m_stack
.back();
186 if (!entry
.is_array
) {
190 m_ss
<< "\"" << name
<< "\"";
199 void JSONFormatter::open_section(const char *name
, bool is_array
)
207 json_formatter_stack_entry_d n
;
208 n
.is_array
= is_array
;
209 m_stack
.push_back(n
);
212 void JSONFormatter::open_array_section(const char *name
)
214 open_section(name
, true);
217 void JSONFormatter::open_array_section_in_ns(const char *name
, const char *ns
)
219 std::ostringstream oss
;
220 oss
<< name
<< " " << ns
;
221 open_section(oss
.str().c_str(), true);
224 void JSONFormatter::open_object_section(const char *name
)
226 open_section(name
, false);
229 void JSONFormatter::open_object_section_in_ns(const char *name
, const char *ns
)
231 std::ostringstream oss
;
232 oss
<< name
<< " " << ns
;
233 open_section(oss
.str().c_str(), false);
236 void JSONFormatter::close_section()
238 assert(!m_stack
.empty());
239 finish_pending_string();
241 struct json_formatter_stack_entry_d
& entry
= m_stack
.back();
242 if (m_pretty
&& entry
.size
) {
244 for (unsigned i
= 1; i
< m_stack
.size(); i
++)
247 m_ss
<< (entry
.is_array
? ']' : '}');
249 if (m_pretty
&& m_stack
.empty())
253 void JSONFormatter::finish_pending_string()
255 if (m_is_pending_string
) {
256 print_quoted_string(m_pending_string
.str());
257 m_pending_string
.str(std::string());
258 m_is_pending_string
= false;
262 void JSONFormatter::dump_unsigned(const char *name
, uint64_t u
)
268 void JSONFormatter::dump_int(const char *name
, int64_t s
)
274 void JSONFormatter::dump_float(const char *name
, double d
)
278 snprintf(foo
, sizeof(foo
), "%lf", d
);
282 void JSONFormatter::dump_string(const char *name
, const std::string
& s
)
285 print_quoted_string(s
);
288 std::ostream
& JSONFormatter::dump_stream(const char *name
)
291 m_is_pending_string
= true;
292 return m_pending_string
;
295 void JSONFormatter::dump_format_va(const char *name
, const char *ns
, bool quoted
, const char *fmt
, va_list ap
)
297 char buf
[LARGE_SIZE
];
298 vsnprintf(buf
, LARGE_SIZE
, fmt
, ap
);
302 print_quoted_string(std::string(buf
));
304 m_ss
<< std::string(buf
);
308 int JSONFormatter::get_len() const
310 return m_ss
.str().size();
313 void JSONFormatter::write_raw_data(const char *data
)
318 const char *XMLFormatter::XML_1_DTD
=
319 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
321 XMLFormatter::XMLFormatter(bool pretty
, bool lowercased
, bool underscored
)
323 m_lowercased(lowercased
),
324 m_underscored(underscored
)
329 void XMLFormatter::flush(std::ostream
& os
)
331 finish_pending_string();
332 std::string m_ss_str
= m_ss
.str();
334 /* There is a small catch here. If the rest of the formatter had NO output,
335 * we should NOT output a newline. This primarily triggers on HTTP redirects */
336 if (m_pretty
&& !m_ss_str
.empty())
342 void XMLFormatter::reset()
346 m_pending_string
.clear();
347 m_pending_string
.str("");
349 m_pending_string_name
.clear();
350 m_header_done
= false;
353 void XMLFormatter::output_header()
356 m_header_done
= true;
357 write_raw_data(XMLFormatter::XML_1_DTD
);;
363 void XMLFormatter::output_footer()
365 while(!m_sections
.empty()) {
370 void XMLFormatter::open_object_section(const char *name
)
372 open_section_in_ns(name
, NULL
, NULL
);
375 void XMLFormatter::open_object_section_with_attrs(const char *name
, const FormatterAttrs
& attrs
)
377 open_section_in_ns(name
, NULL
, &attrs
);
380 void XMLFormatter::open_object_section_in_ns(const char *name
, const char *ns
)
382 open_section_in_ns(name
, ns
, NULL
);
385 void XMLFormatter::open_array_section(const char *name
)
387 open_section_in_ns(name
, NULL
, NULL
);
390 void XMLFormatter::open_array_section_with_attrs(const char *name
, const FormatterAttrs
& attrs
)
392 open_section_in_ns(name
, NULL
, &attrs
);
395 void XMLFormatter::open_array_section_in_ns(const char *name
, const char *ns
)
397 open_section_in_ns(name
, ns
, NULL
);
400 void XMLFormatter::close_section()
402 assert(!m_sections
.empty());
403 finish_pending_string();
405 std::string section
= m_sections
.back();
406 std::transform(section
.begin(), section
.end(), section
.begin(),
407 [this](char c
) { return this->to_lower_underscore(c
); });
408 m_sections
.pop_back();
410 m_ss
<< "</" << section
<< ">";
415 void XMLFormatter::dump_unsigned(const char *name
, uint64_t u
)
418 std::transform(e
.begin(), e
.end(), e
.begin(),
419 [this](char c
) { return this->to_lower_underscore(c
); });
422 m_ss
<< "<" << e
<< ">" << u
<< "</" << e
<< ">";
427 void XMLFormatter::dump_int(const char *name
, int64_t u
)
430 std::transform(e
.begin(), e
.end(), e
.begin(),
431 [this](char c
) { return this->to_lower_underscore(c
); });
434 m_ss
<< "<" << e
<< ">" << u
<< "</" << e
<< ">";
439 void XMLFormatter::dump_float(const char *name
, double d
)
442 std::transform(e
.begin(), e
.end(), e
.begin(),
443 [this](char c
) { return this->to_lower_underscore(c
); });
446 m_ss
<< "<" << e
<< ">" << d
<< "</" << e
<< ">";
451 void XMLFormatter::dump_string(const char *name
, const std::string
& s
)
454 std::transform(e
.begin(), e
.end(), e
.begin(),
455 [this](char c
) { return this->to_lower_underscore(c
); });
458 m_ss
<< "<" << e
<< ">" << escape_xml_str(s
.c_str()) << "</" << e
<< ">";
463 void XMLFormatter::dump_string_with_attrs(const char *name
, const std::string
& s
, const FormatterAttrs
& attrs
)
466 std::transform(e
.begin(), e
.end(), e
.begin(),
467 [this](char c
) { return this->to_lower_underscore(c
); });
469 std::string attrs_str
;
470 get_attrs_str(&attrs
, attrs_str
);
472 m_ss
<< "<" << e
<< attrs_str
<< ">" << escape_xml_str(s
.c_str()) << "</" << e
<< ">";
477 std::ostream
& XMLFormatter::dump_stream(const char *name
)
480 m_pending_string_name
= name
;
481 m_ss
<< "<" << m_pending_string_name
<< ">";
482 return m_pending_string
;
485 void XMLFormatter::dump_format_va(const char* name
, const char *ns
, bool quoted
, const char *fmt
, va_list ap
)
487 char buf
[LARGE_SIZE
];
488 vsnprintf(buf
, LARGE_SIZE
, fmt
, ap
);
490 std::transform(e
.begin(), e
.end(), e
.begin(),
491 [this](char c
) { return this->to_lower_underscore(c
); });
495 m_ss
<< "<" << e
<< " xmlns=\"" << ns
<< "\">" << buf
<< "</" << e
<< ">";
497 m_ss
<< "<" << e
<< ">" << escape_xml_str(buf
) << "</" << e
<< ">";
504 int XMLFormatter::get_len() const
506 return m_ss
.str().size();
509 void XMLFormatter::write_raw_data(const char *data
)
514 void XMLFormatter::get_attrs_str(const FormatterAttrs
*attrs
, std::string
& attrs_str
)
516 std::stringstream attrs_ss
;
518 for (std::list
<std::pair
<std::string
, std::string
> >::const_iterator iter
= attrs
->attrs
.begin();
519 iter
!= attrs
->attrs
.end(); ++iter
) {
520 std::pair
<std::string
, std::string
> p
= *iter
;
521 attrs_ss
<< " " << p
.first
<< "=" << "\"" << p
.second
<< "\"";
524 attrs_str
= attrs_ss
.str();
527 void XMLFormatter::open_section_in_ns(const char *name
, const char *ns
, const FormatterAttrs
*attrs
)
530 std::string attrs_str
;
533 get_attrs_str(attrs
, attrs_str
);
537 std::transform(e
.begin(), e
.end(), e
.begin(),
538 [this](char c
) { return this->to_lower_underscore(c
); });
541 m_ss
<< "<" << e
<< attrs_str
<< " xmlns=\"" << ns
<< "\">";
543 m_ss
<< "<" << e
<< attrs_str
<< ">";
547 m_sections
.push_back(name
);
550 void XMLFormatter::finish_pending_string()
552 if (!m_pending_string_name
.empty()) {
553 m_ss
<< escape_xml_str(m_pending_string
.str().c_str())
554 << "</" << m_pending_string_name
<< ">";
555 m_pending_string_name
.clear();
556 m_pending_string
.str(std::string());
563 void XMLFormatter::print_spaces()
565 finish_pending_string();
567 std::string
spaces(m_sections
.size(), ' ');
572 std::string
XMLFormatter::escape_xml_str(const char *str
)
574 int len
= escape_xml_attr_len(str
);
575 std::vector
<char> escaped(len
, '\0');
576 escape_xml_attr(str
, &escaped
[0]);
577 return std::string(&escaped
[0]);
580 char XMLFormatter::to_lower_underscore(char c
) const
582 if (m_underscored
&& c
== ' ') {
584 } else if (m_lowercased
) {
585 return std::tolower(c
);
590 TableFormatter::TableFormatter(bool keyval
) : m_keyval(keyval
)
595 void TableFormatter::flush(std::ostream
& os
)
597 finish_pending_string();
598 std::vector
<size_t> column_size
= m_column_size
;
599 std::vector
<std::string
> column_name
= m_column_name
;
601 std::set
<int> need_header_set
;
603 // auto-sizing columns
604 for (size_t i
= 0; i
< m_vec
.size(); i
++) {
605 for (size_t j
= 0; j
< m_vec
[i
].size(); j
++) {
606 column_size
.resize(m_vec
[i
].size());
607 column_name
.resize(m_vec
[i
].size());
609 if (m_vec
[i
- 1][j
] != m_vec
[i
][j
]) {
610 // changing row labels require to show the header
611 need_header_set
.insert(i
);
612 column_name
[i
] = m_vec
[i
][j
].first
;
615 column_name
[i
] = m_vec
[i
][j
].first
;
618 if (m_vec
[i
][j
].second
.length() > column_size
[j
])
619 column_size
[j
] = m_vec
[i
][j
].second
.length();
620 if (m_vec
[i
][j
].first
.length() > column_size
[j
])
621 column_size
[j
] = m_vec
[i
][j
].first
.length();
625 bool need_header
= false;
626 if ((column_size
.size() == m_column_size
.size())) {
627 for (size_t i
= 0; i
< column_size
.size(); i
++) {
628 if (column_size
[i
] != m_column_size
[i
]) {
638 // first row always needs a header if there wasn't one before
639 need_header_set
.insert(0);
642 m_column_size
= column_size
;
643 for (size_t i
= 0; i
< m_vec
.size(); i
++) {
645 if (need_header_set
.count(i
)) {
649 for (size_t j
= 0; j
< m_vec
[i
].size(); j
++) {
650 for (size_t v
= 0; v
< m_column_size
[j
] + 3; v
++)
657 for (size_t j
= 0; j
< m_vec
[i
].size(); j
++) {
659 std::stringstream fs
;
660 fs
<< boost::format("%%-%is") % (m_column_size
[j
] + 2);
661 os
<< boost::format(fs
.str()) % m_vec
[i
][j
].first
;
666 for (size_t j
= 0; j
< m_vec
[i
].size(); j
++) {
667 for (size_t v
= 0; v
< m_column_size
[j
] + 3; v
++)
678 for (size_t j
= 0; j
< m_vec
[i
].size(); j
++) {
681 std::stringstream fs
;
685 os
<< m_vec
[i
][j
].first
;
688 os
<< m_vec
[i
][j
].second
;
691 fs
<< boost::format("%%-%is") % (m_column_size
[j
] + 2);
692 os
<< boost::format(fs
.str()) % m_vec
[i
][j
].second
;
699 if (i
== (m_vec
.size() - 1)) {
702 for (size_t j
= 0; j
< m_vec
[i
].size(); j
++) {
703 for (size_t v
= 0; v
< m_column_size
[j
] + 3; v
++)
715 void TableFormatter::reset()
719 m_section_cnt
.clear();
720 m_column_size
.clear();
724 void TableFormatter::open_object_section(const char *name
)
726 open_section_in_ns(name
, NULL
, NULL
);
729 void TableFormatter::open_object_section_with_attrs(const char *name
, const FormatterAttrs
& attrs
)
731 open_section_in_ns(name
, NULL
, NULL
);
734 void TableFormatter::open_object_section_in_ns(const char *name
, const char *ns
)
736 open_section_in_ns(name
, NULL
, NULL
);
739 void TableFormatter::open_array_section(const char *name
)
741 open_section_in_ns(name
, NULL
, NULL
);
744 void TableFormatter::open_array_section_with_attrs(const char *name
, const FormatterAttrs
& attrs
)
746 open_section_in_ns(name
, NULL
, NULL
);
749 void TableFormatter::open_array_section_in_ns(const char *name
, const char *ns
)
751 open_section_in_ns(name
, NULL
, NULL
);
754 void TableFormatter::open_section_in_ns(const char *name
, const char *ns
, const FormatterAttrs
*attrs
)
756 m_section
.push_back(name
);
760 void TableFormatter::close_section()
764 if (m_section
.size()) {
765 m_section_cnt
[m_section
.back()] = 0;
766 m_section
.pop_back();
770 size_t TableFormatter::m_vec_index(const char *name
)
772 std::string
key(name
);
774 size_t i
= m_vec
.size();
778 // make sure there are vectors to push back key/val pairs
783 if (m_vec
[i
].size()) {
784 if (m_vec
[i
][0].first
== key
) {
785 // start a new column if a key is repeated
786 m_vec
.resize(m_vec
.size() + 1);
795 std::string
TableFormatter::get_section_name(const char* name
)
797 std::string t_name
= name
;
798 for (size_t i
= 0; i
< m_section
.size(); i
++) {
799 t_name
.insert(0, ":");
800 t_name
.insert(0, m_section
[i
]);
802 if (m_section_open
) {
803 std::stringstream lss
;
806 lss
<< m_section_cnt
[t_name
]++;
814 void TableFormatter::dump_unsigned(const char *name
, uint64_t u
)
816 finish_pending_string();
817 size_t i
= m_vec_index(name
);
819 m_vec
[i
].push_back(std::make_pair(get_section_name(name
), m_ss
.str()));
824 void TableFormatter::dump_int(const char *name
, int64_t u
)
826 finish_pending_string();
827 size_t i
= m_vec_index(name
);
829 m_vec
[i
].push_back(std::make_pair(get_section_name(name
), m_ss
.str()));
834 void TableFormatter::dump_float(const char *name
, double d
)
836 finish_pending_string();
837 size_t i
= m_vec_index(name
);
840 m_vec
[i
].push_back(std::make_pair(get_section_name(name
), m_ss
.str()));
845 void TableFormatter::dump_string(const char *name
, const std::string
& s
)
847 finish_pending_string();
848 size_t i
= m_vec_index(name
);
851 m_vec
[i
].push_back(std::make_pair(get_section_name(name
), m_ss
.str()));
856 void TableFormatter::dump_string_with_attrs(const char *name
, const std::string
& s
, const FormatterAttrs
& attrs
)
858 finish_pending_string();
859 size_t i
= m_vec_index(name
);
861 std::string attrs_str
;
862 get_attrs_str(&attrs
, attrs_str
);
863 m_ss
<< attrs_str
<< s
;
865 m_vec
[i
].push_back(std::make_pair(get_section_name(name
), m_ss
.str()));
870 void TableFormatter::dump_format_va(const char* name
, const char *ns
, bool quoted
, const char *fmt
, va_list ap
)
872 finish_pending_string();
873 char buf
[LARGE_SIZE
];
874 vsnprintf(buf
, LARGE_SIZE
, fmt
, ap
);
876 size_t i
= m_vec_index(name
);
878 m_ss
<< ns
<< "." << buf
;
882 m_vec
[i
].push_back(std::make_pair(get_section_name(name
), m_ss
.str()));
887 std::ostream
& TableFormatter::dump_stream(const char *name
)
889 finish_pending_string();
890 // we don't support this
891 m_pending_name
= name
;
895 int TableFormatter::get_len() const
897 // we don't know the size until flush is called
901 void TableFormatter::write_raw_data(const char *data
) {
905 void TableFormatter::get_attrs_str(const FormatterAttrs
*attrs
, std::string
& attrs_str
)
907 std::stringstream attrs_ss
;
909 for (std::list
<std::pair
<std::string
, std::string
> >::const_iterator iter
= attrs
->attrs
.begin();
910 iter
!= attrs
->attrs
.end(); ++iter
) {
911 std::pair
<std::string
, std::string
> p
= *iter
;
912 attrs_ss
<< " " << p
.first
<< "=" << "\"" << p
.second
<< "\"";
915 attrs_str
= attrs_ss
.str();
918 void TableFormatter::finish_pending_string()
920 if (m_pending_name
.length()) {
921 std::string ss
= m_ss
.str();
924 std::string pending_name
= m_pending_name
;
926 dump_string(pending_name
.c_str(), ss
);