A library for working with phylogenetic and population genetic data.
v0.27.0
utils/text/table.cpp
Go to the documentation of this file.
1 /*
2  Genesis - A toolkit for working with phylogenetic data.
3  Copyright (C) 2014-2019 Lucas Czech and HITS gGmbH
4 
5  This program is free software: you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation, either version 3 of the License, or
8  (at your option) any later version.
9 
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License
16  along with this program. If not, see <http://www.gnu.org/licenses/>.
17 
18  Contact:
19  Lucas Czech <lucas.czech@h-its.org>
20  Exelixis Lab, Heidelberg Institute for Theoretical Studies
21  Schloss-Wolfsbrunnenweg 35, D-69118 Heidelberg, Germany
22 */
23 
32 
34 
35 #include <algorithm>
36 #include <cassert>
37 #include <iomanip>
38 #include <sstream>
39 #include <stdexcept>
40 #include <string>
41 #include <vector>
42 
43 namespace genesis {
44 namespace utils {
45 
46 // =================================================================================================
47 // Text Table
48 // =================================================================================================
49 
50 // ---------------------------------------------------------------------
51 // Accessors
52 // ---------------------------------------------------------------------
53 
54 size_t Table::length() const
55 {
56  if( columns_.size() == 0 ) {
57  return 0;
58  }
59 
60  size_t len = columns_[0].length();
61  for( auto const& c : columns_ ) {
62  if( len != c.length() ) {
63  throw std::length_error("Table columns are unevenly filled.");
64  }
65  }
66 
67  // If we are here, no exception was thrown. Thus, all columns are evenly filled. The current
68  // marker for inserting new values therefore needs to be at the first column, too. If not,
69  // we failed to set it correctly somewhere.
70  assert( current_col_ == 0 );
71 
72  return len;
73 }
74 
75 // ---------------------------------------------------------------------
76 // Modifiers
77 // ---------------------------------------------------------------------
78 
83 {
84  current_col_ = 0;
85  columns_.clear();
86 }
87 
92 {
93  current_col_ = 0;
94  for( auto& c : columns_ ) {
95  c.clear_content();
96  }
97 }
98 
105 Table::Column& Table::add_column( std::string label )
106 {
107  auto const len = length();
108  columns_.push_back( Column(label) );
109  for( size_t i = 0; i < len; ++i ) {
110  columns_.back().append("");
111  }
112  return columns_.back();
113 }
114 
115 Table& Table::operator << ( std::string value )
116 {
117  return append( value );
118 }
119 
120 // Table& Table::operator << ( Style const& value )
121 // {
122 // return append( value );
123 // }
124 
125 Table& Table::append ( std::string value )
126 {
127  columns_[ current_col_ ].append( value );
128 
129  ++current_col_;
130  if( current_col_ >= columns_.size() ) {
131  current_col_ = 0;
132  }
133 
134  return *this;
135 }
136 
137 Table& Table::append ( Style const& style, std::string value )
138 {
139  columns_[ current_col_ ].append( style, value );
140 
141  ++current_col_;
142  if( current_col_ >= columns_.size() ) {
143  current_col_ = 0;
144  }
145 
146  return *this;
147 }
148 
157 {
158  while( current_col_ < columns_.size() ) {
159  columns_[ current_col_ ].append("");
160  ++current_col_;
161  }
162 
163  current_col_ = 0;
164  return *this;
165 }
166 
167 // ---------------------------------------------------------------------
168 // Output
169 // ---------------------------------------------------------------------
170 
171 void Table::write( std::ostream& out ) const
172 {
173  // Write labels.
174  for( auto const& c : columns_ ) {
175  c.write_label(out);
176  out << " ";
177  }
178  out << "\n";
179 
180  // Write data.
181  for( size_t i = 0; i < length(); ++i ) {
182  for( auto const& c : columns_ ) {
183  c.write_row(out, i);
184  out << " ";
185  }
186  out << "\n";
187  }
188 }
189 
190 void Table::write( std::ostream& out, TableLayout const& layout ) const
191 {
192  // Take a TableLayout Line and print it according to the table data.
193  auto write_line = [&] (TableLayout::Line const& line) {
194  if( line.enabled ) {
195  out << line.left_border;
196  for( size_t ci = 0; ci < columns_.size(); ++ci ) {
197  // out << std::string( columns_[ci].width(), line.filler );
198  for( size_t i = 0; i < columns_[ci].width(); ++i ) {
199  out << line.filler;
200  }
201  if( ci < columns_.size() - 1 ) {
202  out << line.separator;
203  }
204  }
205  out << line.right_border << "\n";
206  }
207  };
208 
209  // Write line above header.
210  write_line(layout.top);
211 
212  // Write labels.
213  out << layout.header.left_border;
214  for( size_t ci = 0; ci < columns_.size(); ++ci ) {
215  columns_[ci].write_label(out);
216  if( ci < columns_.size() - 1 ) {
217  out << layout.header.separator;
218  }
219  }
220  out << layout.header.right_border << "\n";
221 
222  // Write line between header and content.
223  write_line(layout.separator);
224 
225  // Write data.
226  for( size_t i = 0; i < length(); ++i ) {
227  out << layout.row.left_border;
228  for( size_t ci = 0; ci < columns_.size(); ++ci ) {
229  columns_[ci].write_row(out, i);
230  if( ci < columns_.size() - 1 ) {
231  out << layout.row.separator;
232  }
233  }
234  out << layout.row.right_border << "\n";
235  }
236 
237  // Write line below content.
238  write_line(layout.bottom);
239 }
240 
241 std::string Table::to_string() const
242 {
243  std::stringstream ss;
244  write(ss);
245  return ss.str();
246 }
247 
248 std::string Table::to_string( TableLayout const& layout ) const
249 {
250  std::stringstream ss;
251  write(ss, layout);
252  return ss.str();
253 }
254 
255 std::ostream& operator << (std::ostream& out, Table const& table)
256 {
257  table.write(out);
258  return out;
259 }
260 
261 // =================================================================================================
262 // Table Column
263 // =================================================================================================
264 
265 // ---------------------------------------------------------------------
266 // Properties
267 // ---------------------------------------------------------------------
268 
269 void Table::Column::label( std::string value )
270 {
271  width_ = std::max( width_, value.size() );
272  label_ = value;
273 }
274 
275 std::string Table::Column::label() const
276 {
277  return label_;
278 }
279 
281 {
282  just_ = value;
283 }
284 
286 {
287  return just_;
288 }
289 
297 void Table::Column::width( size_t value )
298 {
299  if( value > width_ ) {
300  width_ = value;
301  }
302 }
303 
304 size_t Table::Column::width() const
305 {
306  return width_;
307 }
308 
318 {
319  size_t mx = label_.size();
320  for( auto const& v : data_ ) {
321  mx = std::max( mx, v.second.size() );
322  }
323  width_ = mx;
324 }
325 
326 // ---------------------------------------------------------------------
327 // Accessors
328 // ---------------------------------------------------------------------
329 
330 size_t Table::Column::length() const
331 {
332  return data_.size();
333 }
334 
335 std::string Table::Column::row( size_t i ) const
336 {
337  // Throws out of range if neccessary.
338  return data_.at( i ).second;
339 }
340 
341 // ---------------------------------------------------------------------
342 // Modifiers
343 // ---------------------------------------------------------------------
344 
346 {
347  width_ = label_.size();
348  data_.clear();
349 }
350 
351 void Table::Column::append( std::string value )
352 {
353  width_ = std::max( width_, value.size() );
354  data_.push_back({ Style(), value });
355 }
356 
357 void Table::Column::append( Style const& style, std::string value )
358 {
359  width_ = std::max( width_, value.size() );
360  data_.push_back({ style, value });
361 }
362 
363 // ---------------------------------------------------------------------
364 // Output
365 // ---------------------------------------------------------------------
366 
367 void Table::Column::write_row( std::ostream& stream, size_t row ) const
368 {
369  // Throws out_of_range if neccessary.
370  auto data = data_.at(row);
371  write( stream, data.first, data.second );
372 }
373 
374 void Table::Column::write_label( std::ostream& stream ) const
375 {
376  write( stream, Style(), label_ );
377 }
378 
379 void Table::Column::write( std::ostream& stream, Style style, std::string text ) const
380 {
381  assert( text.size() <= width_ );
382 
383  if( just_ == Justification::kLeft ) {
384  text = text + std::string(width_ - text.size(), ' ');
385  }
386  if( just_ == Justification::kCentered ) {
387  const size_t pad = (width_ - text.size()) / 2;
388  text = std::string(pad, ' ') + text + std::string(width_ - text.size() - pad, ' ');
389  }
390  if( just_ == Justification::kRight ) {
391  text = std::string(width_ - text.size(), ' ') + text;
392  }
393 
394  stream << style( text );
395 }
396 
397 // =================================================================================================
398 // TableLayout
399 // =================================================================================================
400 
401 // ---------------------------------------------------------------------
402 // Binding
403 // ---------------------------------------------------------------------
404 
405 std::ostream& operator << (std::ostream& out, TableLayout::Binder const& binder)
406 {
407  binder.table.write(out, binder.layout);
408  return out;
409 }
410 
431 {
432  return Binder(*this, table);
433 }
434 
435 // ---------------------------------------------------------------------
436 // Default TableLayouts
437 // ---------------------------------------------------------------------
438 
440 {
441  // TableLayout already has minimal settings (just a space as separator, nothing else).
442  return TableLayout();
443 }
444 
445 TableLayout simple_layout( bool condensed )
446 {
447  auto f = TableLayout();
448 
449  f.header.left_border = (condensed ? "" : " ");
450  f.header.separator = (condensed ? " " : " ");
451  f.header.right_border = (condensed ? "" : " ");
452 
453  f.separator.enabled = true;
454  f.separator.left_border = (condensed ? "" : "-");
455  f.separator.filler = "-";
456  f.separator.separator = (condensed ? " " : "- -");
457  f.separator.right_border = (condensed ? "" : "-");
458 
459  f.row = f.header;
460 
461  return f;
462 }
463 
464 TableLayout simple_grid( bool condensed )
465 {
466  auto f = TableLayout();
467 
468  f.header.left_border = (condensed ? "" : " ");
469  f.header.separator = (condensed ? "|" : " | ");
470  f.header.right_border = (condensed ? "" : " ");
471 
472  f.separator.enabled = true;
473  f.separator.left_border = (condensed ? "" : "-");
474  f.separator.filler = "-";
475  f.separator.separator = (condensed ? "+" : "-+-");
476  f.separator.right_border = (condensed ? "" : "-");
477 
478  f.row = f.header;
479 
480  return f;
481 }
482 
483 TableLayout simple_frame( bool condensed )
484 {
485  auto f = TableLayout();
486 
487  f.top.enabled = true;
488  f.top.left_border = (condensed ? "+" : "+-");
489  f.top.filler = "-";
490  f.top.separator = (condensed ? "+" : "-+-");
491  f.top.right_border = (condensed ? "+" : "-+");
492 
493  f.header.left_border = (condensed ? "|" : "| ");
494  f.header.separator = (condensed ? "|" : " | ");
495  f.header.right_border = (condensed ? "|" : " |");
496 
497  f.separator = f.top;
498  f.row = f.header;
499  f.bottom = f.top;
500 
501  return f;
502 }
503 
504 TableLayout extended_grid( bool condensed )
505 {
506  auto f = TableLayout();
507 
508  f.header.left_border = (condensed ? "" : " ");
509  f.header.separator = (condensed ? "│" : " │ ");
510  f.header.right_border = (condensed ? "" : " ");
511 
512  f.separator.enabled = true;
513  f.separator.left_border = (condensed ? "" : "─");
514  f.separator.filler = "─";
515  f.separator.separator = (condensed ? "┼" : "─┼─");
516  f.separator.right_border = (condensed ? "" : "─");
517 
518  f.row = f.header;
519 
520  return f;
521 }
522 
523 TableLayout extended_frame( bool condensed )
524 {
525  auto f = TableLayout();
526 
527  f.top.enabled = true;
528  f.top.left_border = (condensed ? "┌" : "┌─");
529  f.top.filler = "─";
530  f.top.separator = (condensed ? "┬" : "─┬─");
531  f.top.right_border = (condensed ? "┐" : "─┐");
532 
533  f.header.left_border = (condensed ? "│" : "│ ");
534  f.header.separator = (condensed ? "│" : " │ ");
535  f.header.right_border = (condensed ? "│" : " │");
536 
537  f.separator.enabled = true;
538  f.separator.left_border = (condensed ? "├" : "├─");
539  f.separator.filler = "─";
540  f.separator.separator = (condensed ? "┼" : "─┼─");
541  f.separator.right_border = (condensed ? "┤" : "─┤");
542 
543  f.row = f.header;
544 
545  f.bottom.enabled = true;
546  f.bottom.left_border = (condensed ? "└" : "└─");
547  f.bottom.filler = "─";
548  f.bottom.separator = (condensed ? "┴" : "─┴─");
549  f.bottom.right_border = (condensed ? "┘" : "─┘");
550 
551  return f;
552 }
553 
554 TableLayout double_grid( bool condensed )
555 {
556  auto f = TableLayout();
557 
558  f.header.left_border = (condensed ? "" : " ");
559  f.header.separator = (condensed ? "║" : " ║ ");
560  f.header.right_border = (condensed ? "" : " ");
561 
562  f.separator.enabled = true;
563  f.separator.left_border = (condensed ? "" : "═");
564  f.separator.filler = "═";
565  f.separator.separator = (condensed ? "╬" : "═╬═");
566  f.separator.right_border = (condensed ? "" : "═");
567 
568  f.row = f.header;
569 
570  return f;
571 }
572 
573 TableLayout double_frame( bool condensed )
574 {
575  auto f = TableLayout();
576 
577  f.top.enabled = true;
578  f.top.left_border = (condensed ? "╔" : "╔═");
579  f.top.filler = "═";
580  f.top.separator = (condensed ? "╦" : "═╦═");
581  f.top.right_border = (condensed ? "╗" : "═╗");
582 
583  f.header.left_border = (condensed ? "║" : "║ ");
584  f.header.separator = (condensed ? "║" : " ║ ");
585  f.header.right_border = (condensed ? "║" : " ║");
586 
587  f.separator.enabled = true;
588  f.separator.left_border = (condensed ? "╠" : "╠═");
589  f.separator.filler = "═";
590  f.separator.separator = (condensed ? "╬" : "═╬═");
591  f.separator.right_border = (condensed ? "╣" : "═╣");
592 
593  f.row = f.header;
594 
595  f.bottom.enabled = true;
596  f.bottom.left_border = (condensed ? "╚" : "╚═");
597  f.bottom.filler = "═";
598  f.bottom.separator = (condensed ? "╩" : "═╩═");
599  f.bottom.right_border = (condensed ? "╝" : "═╝");
600 
601  return f;
602 }
603 
604 } // namespace utils
605 } // namespace genesis
table.hpp
genesis::utils::Table::Column::clear_content
void clear_content()
Definition: utils/text/table.cpp:345
genesis::utils::operator<<
std::ostream & operator<<(std::ostream &os, const Matrix< signed char > &matrix)
Template specialization for signed char, in order to print nicely.
Definition: utils/containers/matrix/operators.cpp:89
genesis::utils::Table::append
Table & append(std::string value)
Definition: utils/text/table.cpp:125
genesis::utils::simple_frame
TableLayout simple_frame(bool condensed)
Definition: utils/text/table.cpp:483
genesis::utils::minimal_layout
TableLayout minimal_layout()
Definition: utils/text/table.cpp:439
genesis::utils::extended_frame
TableLayout extended_frame(bool condensed)
Definition: utils/text/table.cpp:523
genesis::utils::Table::Column::row
std::string row(size_t i) const
Definition: utils/text/table.cpp:335
genesis::utils::TableLayout::row
Line row
Definition: utils/text/table.hpp:308
genesis::utils::TableLayout::Line::left_border
std::string left_border
Definition: utils/text/table.hpp:269
genesis::utils::Table
Definition: utils/text/table.hpp:60
genesis::utils::Table::Column::append
void append(std::string value)
Definition: utils/text/table.cpp:351
genesis::utils::double_frame
TableLayout double_frame(bool condensed)
Definition: utils/text/table.cpp:573
genesis::utils::Table::Column::justify
Justification justify() const
Definition: utils/text/table.cpp:285
genesis::utils::Table::clear_content
void clear_content()
Clears the data contents of all columns. Their labels etc stay unchanged.
Definition: utils/text/table.cpp:91
genesis::utils::Table::add_column
Column & add_column(std::string label="")
Add a column to the table.
Definition: utils/text/table.cpp:105
string.hpp
Provides some commonly used string utility functions.
genesis::utils::Table::write
void write(std::ostream &out) const
Definition: utils/text/table.cpp:171
genesis::utils::Table::to_string
std::string to_string() const
Definition: utils/text/table.cpp:241
genesis::utils::Table::Column::length
size_t length() const
Definition: utils/text/table.cpp:330
genesis::utils::Table::clear
void clear()
Clears all columns and their data from the table.
Definition: utils/text/table.cpp:82
genesis::utils::TableLayout::Line::right_border
std::string right_border
Definition: utils/text/table.hpp:272
genesis::utils::TableLayout::bottom
Line bottom
Definition: utils/text/table.hpp:309
genesis::utils::TableLayout::Binder::table
Table const & table
Definition: utils/text/table.hpp:290
genesis::utils::Style
Simple text style class for colorized and bold output to a terminal.
Definition: style.hpp:81
genesis::utils::Table::line_break
Table & line_break()
Finish the currently line and move to the next one.
Definition: utils/text/table.cpp:156
genesis::utils::TableLayout::Binder
Helper struct to bind a layout to a table.
Definition: utils/text/table.hpp:282
genesis::utils::TableLayout::Binder::layout
TableLayout const & layout
Definition: utils/text/table.hpp:289
genesis
Container namespace for all symbols of genesis in order to keep them separate when used as a library.
Definition: placement/formats/edge_color.cpp:42
genesis::utils::Table::Column::write_row
void write_row(std::ostream &stream, size_t row) const
Definition: utils/text/table.cpp:367
genesis::utils::double_grid
TableLayout double_grid(bool condensed)
Definition: utils/text/table.cpp:554
genesis::utils::Table::Column::width
size_t width() const
Definition: utils/text/table.cpp:304
genesis::utils::simple_layout
TableLayout simple_layout(bool condensed)
Definition: utils/text/table.cpp:445
genesis::utils::TableLayout::operator()
Binder operator()(Table const &table)
Functional operator that allows to bind a TableLayout to a Table so that they can be used in one ostr...
Definition: utils/text/table.cpp:430
genesis::utils::Table::Column
Definition: utils/text/table.hpp:140
genesis::utils::TableLayout::header
Line header
Definition: utils/text/table.hpp:306
genesis::utils::Table::length
size_t length() const
Definition: utils/text/table.cpp:54
genesis::utils::Table::Column::shrink_width
void shrink_width()
Shrinks the column width to the minmal width that suffices to fit in all values of the column (i....
Definition: utils/text/table.cpp:317
genesis::utils::TableLayout
Definition: utils/text/table.hpp:245
genesis::utils::TableLayout::Line::separator
std::string separator
Definition: utils/text/table.hpp:271
genesis::utils::Table::Column::label
std::string label() const
Definition: utils/text/table.cpp:275
genesis::utils::extended_grid
TableLayout extended_grid(bool condensed)
Definition: utils/text/table.cpp:504
genesis::utils::Table::Column::Justification
Justification
Definition: utils/text/table.hpp:148
genesis::utils::TableLayout::Line
One line of the TableLayout.
Definition: utils/text/table.hpp:265
genesis::utils::Table::operator<<
Table & operator<<(std::string value)
Definition: utils/text/table.cpp:115
genesis::utils::TableLayout::separator
Line separator
Definition: utils/text/table.hpp:307
genesis::utils::simple_grid
TableLayout simple_grid(bool condensed)
Definition: utils/text/table.cpp:464
genesis::utils::TableLayout::top
Line top
Definition: utils/text/table.hpp:305
genesis::utils::Table::Column::write_label
void write_label(std::ostream &stream) const
Definition: utils/text/table.cpp:374