#ifndef _utl_TabularStream_h_ #define _utl_TabularStream_h_ #include #include #include #include #include #include namespace utl { /// Holds TabularStream cell entry class TableCell { public: TableCell() { } TableCell(const TableCell& c) : fEntry(c.fEntry.str()) { } TableCell& operator=(const TableCell& c) { fEntry.str(c.fEntry.str()); return *this; } private: enum Justification { eFlushLeft, eFlushRight, eCenter, eCharAligned }; template TableCell& operator<<(const T& item) { fEntry << item; return *this; } std::string GetEntry(const int left = 0, const int right = 0) const { return std::string(left, ' ') + fEntry.str() + std::string(right, ' '); } std::string GetEntry(const Justification just, const int totalSize, const int leftSize = 0, const char mid = '\0') const; int GetSize() const { return fEntry.str().size(); } std::pair GetSize(const char mid) const; std::ostringstream fEntry; friend class TableColumn; friend class TabularStream; }; // Holds one column of TableCells in the TabularStream class class TableColumn { public: TableColumn() : fWidth(-1) { } private: template TableColumn& operator<<(const T& junk) { Front() << junk; return *this; } int GetLength() const { return fCells.size(); } void SetMargins(const std::string& prefix, const std::string& postfix = std::string()) { fPrefix = prefix; fPostfix = postfix; } void SetJustification(const TableCell::Justification just, const char mid = '\0') { fJustification = just; fMidChar = mid; } std::string Pop(); void PopFront() { if (!fCells.empty()) fCells.pop_front(); } TableCell& Front() { return fCells.front(); } void Push() { fCells.push_front(TableCell()); } bool Empty() { return fCells.empty(); } void CalculateWidths(); std::string fPrefix; std::string fPostfix; TableCell::Justification fJustification; char fMidChar; std::deque fCells; int fWidth; int fLeftWidth; friend class TabularStream; }; /// type-only supplying class that triggers end-of-column in the TabularStream class EndColumn { }; const EndColumn endc = EndColumn(); /// type-only supplying class that triggers end-of-row in the TabularStream class EndRow { }; const EndRow endr = EndRow(); /** type-only supplying class that triggers deleting of the last row in TabularStream Useful when in for-loops one too many endr (end-row) is pushed into the TabularStream class */ class DeleteRow { }; const DeleteRow delr = DeleteRow(); /// class that triggers insertion of the line row in the TabularStream class HLine { public: HLine(const char sign) : fChar(sign) { } char GetChar() const { return fChar; } private: char fChar; }; const HLine hline('-'); /** * \class TabularStream TabularStream.h "utl/TabularStream.h" * * \brief class to format data in tabular form * * \code * TabularStream tab(". r l"); * * tab << 3 << ".14" << endc << "bla" << endc << 1111U << endr * << 13.07 << endc << "foobar" << endc << 22 << endr * << 123.456 << endc << 'a' << endc << 333; * * cout << tab; * \endcode * gives you * \code * 3.14 bla 1111 * 13.07 foobar 22 * 123.456 a 333 * \endcode * Use "endc" to end filling the current cell, use "endr" to end a row. * Filling the TabularStream in a for-loop will produce one "endr" too * many. Use "delr" to delete the last row (useful if one line too many * is added in loops). More lines around columns or rows you can add * as in the following example: * \code * TabularStream tab("|l|.|r|"); * * tab << hline * << "gain" << endc << 1.23 << endc << "ok" << endr * << HLine('=') * << "jitter" << endc << 0.2 << endc << "+1" << endr * << "adc" << endc << 13 << endc << "aha" << endr * << hline; * * cout << tab; * \endcode * which produces * \code * +------+-----+---+ * |gain | 1.23| ok| * ================== * |jitter| 0.2 | +1| * |adc |13 |aha| * +------+-----+---+ * \endcode * Note that every row of the table will be terminated with a * newline. * * In short, "endc" has the same meaning as "&" in LaTeX tabular environment, * "endr" behaves as LaTeX newline "\\", and "hline" even has the same name... * * \author Darko Veberic * \date 14 Aug 2007 * \version $Id$ */ class TabularStream { public: TabularStream(const std::string& format) { Clear(format); } void Clear() { fColumns.clear(); fMakeNewRow = true; } void Clear(const std::string& format) { Clear(); MakeFormat(format); } template TabularStream& operator<<(const T& junk) { CheckFirst(); *fCurrentColumn << junk; return *this; } TabularStream& operator<<(const EndColumn) { CheckFirst(); NextColumn(); return *this; } TabularStream& operator<<(const EndRow) { CheckFirst(); NextRow(); return *this; } TabularStream& operator<<(const HLine hl) { CheckFirst(); if (fCurrentColumn != fColumns.begin()) *this << "ERROR: hline can be used only at the begining of a new line"; else { for (Columns::iterator cIt = fColumns.begin(); cIt != fColumns.end(); ++cIt) *cIt << ((std::string("\r") + hl.GetChar())); } fMakeNewRow = true; return *this; } TabularStream& operator<<(const DeleteRow) { for (Columns::iterator cIt = fColumns.begin(); cIt != fColumns.end(); ++cIt) cIt->PopFront(); fMakeNewRow = true; return *this; } std::string Str(); private: void MakeFormat(const std::string& format); void CheckFirst() { if (fMakeNewRow) { NextRow(); fMakeNewRow = false; } } void NextColumn() { ++fCurrentColumn; if (fCurrentColumn == fColumns.end()) { ERROR("Running out of columns, will fill the last one!"); --fCurrentColumn; } } void NextRow() { for (Columns::iterator cIt = fColumns.begin(); cIt != fColumns.end(); ++cIt) cIt->Push(); fCurrentColumn = fColumns.begin(); } typedef std::vector Columns; Columns::iterator fCurrentColumn; Columns fColumns; bool fMakeNewRow; }; inline std::ostream& operator<<(std::ostream& os, TabularStream& ts) { return os << ts.Str(); } } #endif