summaryrefslogtreecommitdiff
path: root/src/common/TextTable.h
blob: 44d0aa4cd4625360ed7e4c9e50f5a179c1ba2653 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
/*
 * Ceph - scalable distributed file system
 *
 * Copyright (C) 2012 Inktank Storage, Inc.
 *
 * This is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1, as published by the Free Software
 * Foundation.  See file COPYING.
 *
 */
#include <vector>
#include <sstream>
#include <iomanip>
#include <string>
#include "include/assert.h"

/**
 * TextTable:
 * Manage tabular output of data.  Caller defines heading of each column
 * and alignment of heading and column data,
 * then inserts rows of data including tuples of
 * length (ncolumns) terminated by TextTable::endrow.  When all rows
 * are inserted, caller asks for output with ostream <<
 * which sizes/pads/dumps the table to ostream.
 *
 * Columns autosize to largest heading or datum.  One space is printed
 * between columns.
 */

class TextTable {

public:
  enum Align {LEFT = 1, CENTER, RIGHT};

private:
  struct TextTableColumn {
    std::string heading;
    int width;
    Align hd_align;
    Align col_align;

    TextTableColumn() {};
    TextTableColumn(std::string h, int w, Align ha, Align ca) :
		    heading(h), width(w), hd_align(ha), col_align(ca) { }
    ~TextTableColumn() {}
  };

  std::vector<TextTableColumn> col;	// column definitions
  std::vector<std::vector<std::string> > row;	// row data array
  unsigned int curcol, currow;		// col, row being inserted into
  unsigned int indent;			// indent width when rendering

public:
  TextTable(): curcol(0), currow(0), indent(0) {}
  ~TextTable() {}

  /**
   * Define a column in the table.
   *
   * @param heading Column heading string (or "")
   * @param hd_align Alignment for heading in column
   * @param col_align Data alignment
   *
   * @note alignment is of type TextTable::Align; values are
   * TextTable::LEFT, TextTable::CENTER, or TextTable::RIGHT
   *
   */
  void define_column(const std::string heading, Align hd_align,
		     Align col_align);

  /**
   * Set indent for table.  Only affects table output.
   *
   * @param i Number of spaces to indent
   */
  void set_indent(int i) { indent = i; }

  /**
   * Add item to table, perhaps on new row.
   * table << val1 << val2 << TextTable::endrow;
   *
   * @param: value to output.
   *
   * @note: Numerics are output in decimal; strings are not truncated.
   * Output formatting choice is limited to alignment in define_column().
   *
   * @return TextTable& for chaining.
   */

  template<typename T> TextTable& operator<<(const T& item)
  {
    if (row.size() < currow + 1)
      row.resize(currow + 1);

    /**
     * col.size() is a good guess for how big row[currow] needs to be,
     * so just expand it out now
     */
    if (row[currow].size() < col.size()) {
      row[currow].resize(col.size());
    }

    // inserting more items than defined columns is a coding error
    assert(curcol + 1 <= col.size());

    // get rendered width of item alone
    std::ostringstream oss;
    oss << item;
    int width = oss.str().length();
    oss.seekp(0);

    // expand column width if necessary
    if (width > col[curcol].width) {
      col[curcol].width = width;
    }

    // now store the rendered item with its proper width
    oss << std::setw(width) << item;
    row[currow][curcol] = oss.str();

    curcol++;
    return *this;
  }

  /**
   * Degenerate type/variable here is just to allow selection of the
   * following operator<< for "<< TextTable::endrow"
   */

  struct endrow_t {};
  static endrow_t endrow;

  /**
   * Implements TextTable::endrow
   */

  TextTable &operator<<(endrow_t)
  {
    curcol = 0;
    currow++;
    return *this;
  }

  /**
   * Render table to ostream (i.e. cout << table)
   */

  friend std::ostream &operator<<(std::ostream& out, TextTable &t);

  /**
   * clear: Reset everything in a TextTable except column defs
   * resize cols to heading widths, clear indent
   */

  void clear();
};