Rosetta
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
Emitter.hh
Go to the documentation of this file.
1 // -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*-
2 // vi: set ts=2 noet:
3 //
4 // (c) Copyright Rosetta Commons Member Institutions.
5 // (c) This file is part of the Rosetta software suite and is made available under license.
6 // (c) The Rosetta software is developed by the contributing members of the Rosetta Commons.
7 // (c) For more information, see http://www.rosettacommons.org. Questions about this can be
8 // (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu.
9 
10 /// @file basic/Emitter.hh
11 ///
12 /// @brief Lightweight class to ease writting YAML documents
13 /// @author Ian W. Davis
14 
15 #ifndef INCLUDED_basic_Emitter_hh
16 #define INCLUDED_basic_Emitter_hh
17 
18 #include <basic/Tracer.hh>
19 
20 #include <cstddef>
21 #include <iosfwd>
22 #include <ostream>
23 
24 
25 namespace basic {
26 
27 class Emitter; // fwd declaration
28 typedef utility::pointer::shared_ptr< Emitter > EmitterOP;
29 typedef utility::pointer::shared_ptr< Emitter const > EmitterCOP;
30 
31 class YamlEmitter; // fwd declaration
32 typedef utility::pointer::shared_ptr< YamlEmitter > YamlEmitterOP;
33 typedef utility::pointer::shared_ptr< YamlEmitter const > YamlEmitterCOP;
34 
35 class JsonEmitter; // fwd declaration
36 typedef utility::pointer::shared_ptr< JsonEmitter > JsonEmitterOP;
37 typedef utility::pointer::shared_ptr< JsonEmitter const > JsonEmitterCOP;
38 
39 /// @brief Lightweight class to ease writting YAML documents (http://yaml.org)
40 /// @details YAML is a structured data format, a more human-readable
41 /// alternative to XML.
42 ///
43 /// I use the terms "map" and "list" here, but you may also see "mapping" and "sequence".
44 /// The former is a series of key : value pairs, while the latter is a simple series of items.
45 /// This class is not entirely able to enforce the generation of valid YAML --
46 /// you can still e.g. write a key : value pair in the middle of a list.
47 /// It will print a warning message but will otherwise try to continue on.
48 ///
49 /// YAML documents have optional explicit start/end markers; if the emitter
50 /// supports them, they will be auto-generated when the class is initialized
51 /// and when you're finished and you call end().
52 ///
53 /// Whitespace is YAML documents is semi-significant. By default, this class
54 /// will try to pretty-print, showing the depth of nesting through indentation.
55 /// When starting a new list or map, you can request that it not be indented
56 /// (i.e., be printed all on one line), but all lists and maps nested inside it
57 /// will also be printed without linebreaks, regardless of the requested indentation.
58 /// YAML refers to these two styles as "block" and "flow", respectively.
60 {
61 public:
62 
63  Emitter(std::ostream & out):
64  out_(out),
65  indent_str_(" "),
66  in_map_(),
67  first_(),
68  indent_(),
70  {}
71 
72  virtual ~Emitter() {}
73 
74  /// @brief Flush the underlying output stream.
75  void flush() { out_.flush(); }
76 
77  /// @brief Write method for use inside lists / arrays
78  template <typename T>
79  void write(T const & data);
80  /// @brief Write method for use inside lists / arrays
81  void start_list(bool indent=true);
82  /// @brief Write method for use inside lists / arrays
83  void start_map(bool indent=true);
84 
85  /// @brief Write method for use inside maps / dicts / objects
86  template <typename T>
87  void write(std::string const & label, T const & data);
88 
89  // The const char* variants are required here so that string literals
90  // won't be cast to booleans (resulting in calls to the list methods above!).
91 
92  /// @brief Write method for use inside maps / dicts / objects
93  void start_list(std::string const & label, bool indent=true)
94  { start_list(label.c_str(), indent); }
95  /// @brief Write method for use inside maps / dicts / objects
96  void start_list(const char * label, bool indent=true);
97  /// @brief Write method for use inside maps / dicts / objects
98  void start_map(std::string const & label, bool indent=true)
99  { start_map(label.c_str(), indent); }
100  /// @brief Write method for use inside maps / dicts / objects
101  void start_map(const char * label, bool indent=true);
102 
103  /// @brief Counterpart to start_list() -- writes closing bracket.
104  void end_list();
105  /// @brief Counterpart to start_map() -- writes closing brace.
106  void end_map();
107  /// @brief By default, closes all open maps/lists, ending the document.
108  void end(size_t desired_depth=0);
109  /// @brief Number of closing brackets/braces required to end document (non-negative)
110  int depth() const { return in_map_.size(); }
111  /// @brief Number of spaces used for indenting. Default is one.
112  void set_indent(int num_spaces)
113  {
114  std::ostringstream s;
115  for ( int i = 0; i < num_spaces; ++i ) s << ' ';
116  indent_str_ = s.str();
117  }
118 
119  /// @brief Start a new document, ending the previous one first if necessary.
120  /// A no-op for some types of output (e.g. JSON) that don't allow multiple documents.
121  virtual void start_doc() = 0;
122 
123 protected:
124 
125  Emitter(); // no null ctor
126  Emitter(Emitter const &); // no copy ctor
127 
128  /// @brief Check that we're in the expected context (either map or list)
129  bool assert_in(bool in_map, std::string const & msg)
130  {
131  if ( in_map_.empty() || in_map_.back() != in_map ) {
132  basic::Warning() << "Bad YAML: " << msg << "\n";
133  return false;
134  } else return true;
135  }
136 
137  /// @brief Write the key part of a key-value pair.
138  void write_label(std::string const & label)
139  {
140  // Space is required after the colon for it to also be legal YAML
141  out_ << quote_string(label) << ": ";
142  }
143  void write_raw(bool b)
144  { out_ << (b ? "true" : "false"); }
145  void write_raw(int i)
146  { out_ << i; }
147  void write_raw(long l)
148  { out_ << l; }
149  void write_raw(float f)
150  { out_ << f; }
151  void write_raw(double d)
152  { out_ << d; }
153  void write_raw(std::string const & s)
154  { out_ << quote_string(s); }
155 
156  /// @brief Converts special characters (newlines, etc) to escape codes (\n, etc).
157  std::string escape_string(std::string const & s, bool & needs_quotes_out);
158 
159  /// @brief Quotes strings as much as needed for this format (e.g. always for JSON).
160  virtual
161  std::string quote_string(std::string const & s) = 0;
162 
163  /// @brief Handle pretty-printing indentation.
164  /// Don't want to use commas for opening/closing brace/bracket.
165  virtual
166  void do_indent(bool write_comma=true) = 0;
167 
168  /// @brief Used for traditional YAML output, writes the "-" marker.
169  virtual
170  void write_list_marker() = 0;
171 
172  /// @brief Actual implementation of start_map() and start_list().
173  virtual
174  void start_raw(bool is_map, bool indent) = 0;
175 
176  /// @brief Actual implementation of end_map() and end_list().
177  virtual
178  void end_raw() = 0;
179 
180 protected:
181  std::ostream & out_;
182  std::string indent_str_;
183 
184  // stacks (pushed down by start_list/map)
185  std::vector<bool> in_map_; //< if false, we're in a list, not a map
186  std::vector<bool> first_; //< first element in this list/map?
187  std::vector<bool> indent_; //< if false, print elements on one line
188  std::vector<int> indent_depth_; //< depth of indent (if being used)
189 }; // Emitter
190 
191 
192 // Templated functions must go in header file:
193 template <typename T>
194 void Emitter::write(T const & data)
195 {
196  assert_in(false, "Tried to write list data inside a map");
197  do_indent();
199  write_raw(data);
200 }
201 
202 template <typename T>
203 void Emitter::write(std::string const & label, T const & data)
204 {
205  assert_in(true, "Tried to write map data inside a list");
206  do_indent();
207  write_label(label);
208  write_raw(data);
209 }
210 
211 
212 //////////////////////////////////////////////////////////////////////////////
213 
214 
215 /// @brief Emitter for more classically formatted YAML
216 class YamlEmitter : public Emitter
217 {
218 public:
219 
220  YamlEmitter(std::ostream & out):
221  Emitter(out)
222  { start_raw(true, true); } // can't call virtual func from base class ctor
223 
224  virtual ~YamlEmitter() {}
225 
226  /// @brief Start a new YAML document, ending the previous one first if necessary
227  virtual void start_doc()
228  { end(); out_ << "\n---\n"; start_raw(true, true); }
229 
230 private:
231 
232  YamlEmitter(); // no null ctor
233  YamlEmitter(YamlEmitter const &); // no copy ctor
234 
235 protected:
236 
237  /// @brief YAML only quotes strings when they contain special characters.
238  virtual
239  std::string quote_string(std::string const & s)
240  {
241  bool needs_quotes(false); // dummy val; will be overwritten below
242  std::string t = escape_string(s, needs_quotes);
243  if ( needs_quotes ) return "\""+t+"\"";
244  else return t;
245  }
246 
247  virtual
248  void do_indent(bool write_comma=true)
249  {
250  bool indent = indent_.back();
251  if ( indent ) {
252  out_ << "\n";
253  int depth = (indent_depth_.empty() ? 0 : indent_depth_.back());
254  for ( int i = 0; i < depth; ++i ) out_ << indent_str_;
255  } else {
256  if ( write_comma ) {
257  bool first = first_.back();
258  if ( first ) first_[ first_.size()-1 ] = false;
259  else out_ << ",";
260  }
261  out_ << " ";
262  }
263  }
264 
265  /// @brief YAML uses "-" for list items when in block (indented) mode.
266  virtual
268  {
269  bool indent = indent_.back();
270  if ( indent ) out_ << "- ";
271  }
272 
273  /// @details YAML only uses brackets and braces if data is not being indented.
274  virtual
275  void start_raw(bool is_map, bool indent)
276  {
277  if ( !indent ) {
278  if ( is_map ) out_ << "{ ";
279  else out_ << "[ ";
280  }
281  in_map_.push_back(is_map);
282  first_.push_back(true);
283  // Once you start not indenting, no children can be indented
284  if ( indent_.empty() ) indent_.push_back(indent);
285  else indent_.push_back( indent_.back() & indent );
286  if ( indent_depth_.empty() ) indent_depth_.push_back(1);
287  else indent_depth_.push_back(indent_depth_.back() + 1);
288  }
289 
290  /// @details YAML only uses brackets and braces if data is not being indented.
291  virtual
292  void end_raw()
293  {
294  if ( in_map_.empty() ) return; // bad op
295  bool indent = indent_.back();
296  bool is_map = in_map_.back();
297  // This is popped first to get the closing brace/bracket to line up right.
298  // Other pops go after do_indent() or weird stuff could happen.
299  indent_depth_.pop_back();
300  if ( !indent ) {
301  //do_indent(false /* no trailing comma */);
302  if ( is_map ) out_ << " }";
303  else out_ << " ]";
304  }
305  in_map_.pop_back();
306  first_.pop_back();
307  indent_.pop_back();
308  }
309 }; // YamlEmitter
310 
311 
312 //////////////////////////////////////////////////////////////////////////////
313 
314 
315 /// @brief Lightweight class to ease writting JSON documents, a subset of YAML (http://json.org)
316 /// @details Using this class is not recommended -- use YamlEmitter instead.
317 ///
318 /// JSON is JavaScript Object Notation, the format for
319 /// object and array literals in the JavaScript language.
320 /// It also looks a great deal like nested Python dicts and lists,
321 /// except for the special JSON values true, false, and null.
322 /// (Python uses True, False, and None instead.
323 /// Otherwise, JSON can be embedded directly into Python scripts.)
324 ///
325 /// JSON is legal subset of YAML, but leaves out much of YAML's complexity and advanced features.
326 /// At the present time, there appears to be more library support
327 /// for JSON than for YAML, especially for reading and parsing.
328 /// (Both are fairly easy to write, as demonstrated here.)
329 ///
330 /// The topmost level of every valid JSON document consists of exactly one map,
331 /// which is automatically started for you when the class is initialized.
332 /// When you're finished you should call end(), and then probably flush().
333 ///
334 /// Whitespace is JSON documents is not significant. By default, this class
335 /// will try to pretty-print, showing the depth of nesting through indentation.
336 /// When starting a new list or map, you can request that it not be indented
337 /// (i.e., be printed all on one line), but all lists and maps nested inside it
338 /// will also be printed without linebreaks, regardless of the requested indentation.
339 class JsonEmitter : public Emitter
340 {
341 public:
342 
343  JsonEmitter(std::ostream & out):
344  Emitter(out)
345  { start_raw(true, true); } // can't call virtual func from base class ctor
346 
347  virtual ~JsonEmitter() {}
348 
349  /// @brief JSON doesn't support multiple documents, so this just calls end(1)
350  /// to return you to the top (least-nested) level.
351  virtual void start_doc()
352  { end(1); }
353 
354 private:
355 
356  JsonEmitter(); // no null ctor
357  JsonEmitter(JsonEmitter const &); // no copy ctor
358 
359 protected:
360 
361  /// @brief JSON always quotes strings, regardless.
362  virtual
363  std::string quote_string(std::string const & s)
364  {
365  bool needs_quotes(false); // dummy val; will be overwritten below
366  std::string t = escape_string(s, needs_quotes);
367  return "\""+t+"\""; // JSON always uses quotes, whether needed or not
368  }
369 
370  virtual
371  void do_indent(bool write_comma=true)
372  {
373  if ( write_comma ) {
374  bool first = first_.back();
375  if ( first ) first_[ first_.size()-1 ] = false;
376  else out_ << ",";
377  }
378  bool indent = indent_.back();
379  if ( indent ) {
380  out_ << "\n";
381  int depth = (indent_depth_.empty() ? 0 : indent_depth_.back());
382  for ( int i = 0; i < depth; ++i ) out_ << indent_str_;
383  } else out_ << " ";
384  }
385 
386  /// @brief JSON has no special marker for list items
387  virtual
388  void write_list_marker() {} // not used in JSON
389 
390  /// @details JSON always uses brackets and braces, regardless of whether we're indenting.
391  virtual
392  void start_raw(bool is_map, bool indent)
393  {
394  if ( is_map ) out_ << "{";
395  else out_ << "[";
396  //if( !indent ) out_ << " "; // not needed
397  in_map_.push_back(is_map);
398  first_.push_back(true);
399  // Once you start not indenting, no children can be indented
400  if ( indent_.empty() ) indent_.push_back(indent);
401  else indent_.push_back( indent_.back() & indent );
402  if ( indent_depth_.empty() ) indent_depth_.push_back(1);
403  else indent_depth_.push_back(indent_depth_.back() + 1);
404  }
405 
406  /// @details JSON always uses brackets and braces, regardless of whether we're indenting.
407  virtual
408  void end_raw()
409  {
410  if ( in_map_.empty() ) return; // bad op
411  bool is_map = in_map_.back();
412  // This is popped first to get the closing brace/bracket to line up right.
413  // Other pops go after do_indent() or weird stuff could happen.
414  indent_depth_.pop_back();
415  do_indent(false /* no trailing comma */);
416  if ( is_map ) out_ << "}";
417  else out_ << "]";
418  in_map_.pop_back();
419  first_.pop_back();
420  indent_.pop_back();
421  }
422 }; // JsonEmitter
423 
424 
425 } // basic
426 
427 #endif // INCLUDED_basic_Emitter_HH
utility::pointer::shared_ptr< YamlEmitter > YamlEmitterOP
Definition: Emitter.hh:31
void write_raw(int i)
Definition: Emitter.hh:145
int depth() const
Number of closing brackets/braces required to end document (non-negative)
Definition: Emitter.hh:110
virtual void do_indent(bool write_comma=true)
Handle pretty-printing indentation. Don't want to use commas for opening/closing brace/bracket.
Definition: Emitter.hh:248
void write_raw(double d)
Definition: Emitter.hh:151
virtual void write_list_marker()
YAML uses "-" for list items when in block (indented) mode.
Definition: Emitter.hh:267
void end_list()
Counterpart to start_list() – writes closing bracket.
Definition: Emitter.cc:57
virtual void end_raw()
Definition: Emitter.hh:292
Emitter(std::ostream &out)
Definition: Emitter.hh:63
Emitter for more classically formatted YAML.
Definition: Emitter.hh:216
JsonEmitter(std::ostream &out)
Definition: Emitter.hh:343
utility::pointer::shared_ptr< JsonEmitter > JsonEmitterOP
Definition: Emitter.hh:35
void start_map(std::string const &label, bool indent=true)
Write method for use inside maps / dicts / objects.
Definition: Emitter.hh:98
void write_raw(std::string const &s)
Definition: Emitter.hh:153
bool assert_in(bool in_map, std::string const &msg)
Check that we're in the expected context (either map or list)
Definition: Emitter.hh:129
virtual void start_doc()
Start a new YAML document, ending the previous one first if necessary.
Definition: Emitter.hh:227
void end_map()
Counterpart to start_map() – writes closing brace.
Definition: Emitter.cc:62
virtual std::string quote_string(std::string const &s)
JSON always quotes strings, regardless.
Definition: Emitter.hh:363
std::vector< bool > in_map_
Definition: Emitter.hh:185
utility::pointer::shared_ptr< JsonEmitter const > JsonEmitterCOP
Definition: Emitter.hh:37
void start_list(bool indent=true)
Write method for use inside lists / arrays.
Definition: Emitter.cc:27
void write(T const &data)
Write method for use inside lists / arrays.
Definition: Emitter.hh:194
std::vector< bool > indent_
Definition: Emitter.hh:187
virtual void start_raw(bool is_map, bool indent)
Definition: Emitter.hh:275
utility::pointer::shared_ptr< Emitter const > EmitterCOP
Definition: Emitter.hh:29
YamlEmitter(std::ostream &out)
Definition: Emitter.hh:220
std::vector< bool > first_
Definition: Emitter.hh:186
Tracer & T(std::string const &channel, TracerPriority priority)
T is special function for assign tracer property on the static object.
Definition: Tracer.cc:567
void end(size_t desired_depth=0)
By default, closes all open maps/lists, ending the document.
Definition: Emitter.cc:67
virtual void start_doc()
JSON doesn't support multiple documents, so this just calls end(1) to return you to the top (least-ne...
Definition: Emitter.hh:351
utility::pointer::shared_ptr< YamlEmitter const > YamlEmitterCOP
Definition: Emitter.hh:33
std::string indent_str_
Definition: Emitter.hh:182
virtual std::string quote_string(std::string const &s)
YAML only quotes strings when they contain special characters.
Definition: Emitter.hh:239
void start_list(std::string const &label, bool indent=true)
Write method for use inside maps / dicts / objects.
Definition: Emitter.hh:93
void start_map(bool indent=true)
Write method for use inside lists / arrays.
Definition: Emitter.cc:34
Base class for reference-counted polymorphic classes.
virtual void start_raw(bool is_map, bool indent)=0
Actual implementation of start_map() and start_list().
std::vector< int > indent_depth_
Definition: Emitter.hh:188
virtual void write_list_marker()=0
Used for traditional YAML output, writes the "-" marker.
utility::pointer::shared_ptr< Emitter > EmitterOP
Definition: Emitter.hh:27
virtual void end_raw()
Definition: Emitter.hh:408
virtual ~Emitter()
Definition: Emitter.hh:72
Tracer IO system.
Lightweight class to ease writting YAML documents (http://yaml.org)
Definition: Emitter.hh:59
virtual ~JsonEmitter()
Definition: Emitter.hh:347
Tracer & Warning(TracerPriority priority=t_warning)
Predefined Warning tracer.
Definition: Tracer.hh:398
void write_raw(float f)
Definition: Emitter.hh:149
virtual void start_doc()=0
Start a new document, ending the previous one first if necessary. A no-op for some types of output (e...
virtual void end_raw()=0
Actual implementation of end_map() and end_list().
Lightweight class to ease writting JSON documents, a subset of YAML (http://json.org) ...
Definition: Emitter.hh:339
virtual void do_indent(bool write_comma=true)=0
Handle pretty-printing indentation. Don't want to use commas for opening/closing brace/bracket.
void write_raw(bool b)
Definition: Emitter.hh:143
void set_indent(int num_spaces)
Number of spaces used for indenting. Default is one.
Definition: Emitter.hh:112
virtual void do_indent(bool write_comma=true)
Handle pretty-printing indentation. Don't want to use commas for opening/closing brace/bracket.
Definition: Emitter.hh:371
void write_label(std::string const &label)
Write the key part of a key-value pair.
Definition: Emitter.hh:138
void flush()
Flush the underlying output stream.
Definition: Emitter.hh:75
std::ostream & out_
Definition: Emitter.hh:181
virtual void write_list_marker()
JSON has no special marker for list items.
Definition: Emitter.hh:388
std::string escape_string(std::string const &s, bool &needs_quotes_out)
Converts special characters (newlines, etc) to escape codes ( , etc).
Definition: Emitter.cc:76
virtual void start_raw(bool is_map, bool indent)
Definition: Emitter.hh:392
virtual ~YamlEmitter()
Definition: Emitter.hh:224
virtual std::string quote_string(std::string const &s)=0
Quotes strings as much as needed for this format (e.g. always for JSON).
void write_raw(long l)
Definition: Emitter.hh:147