Logo ROOT  
Reference Guide
Loading...
Searching...
No Matches
RDFDisplay.cxx
Go to the documentation of this file.
1/*************************************************************************
2 * Copyright (C) 1995-2021, Rene Brun and Fons Rademakers. *
3 * All rights reserved. *
4 * *
5 * For the licensing terms see $ROOTSYS/LICENSE. *
6 * For the list of contributors see $ROOTSYS/README/CREDITS. *
7 *************************************************************************/
8
10
11#include <iomanip>
12#include <iostream>
13#include <limits>
14#include <sstream>
15#include <string>
16#include <vector>
17
18namespace ROOT {
19namespace Internal {
20namespace RDF {
21
22
23/**
24 * \class ROOT::Internal::RDF::RDisplayElement
25 * Helper class to let Display print compact tabular representations of the events
26 *
27 * This class is internal and not meant to be explicitly instantiated by the user.
28 * It is needed during printing to understand if a value can be
29 * skipped or must be printed. Each RDisplayElement represents a cell.
30 */
31
32////////////////////////////////////////////////////////////////////////////
33/// Constructor
34/// \param[in] representation The representation string
35RDisplayElement::RDisplayElement(const std::string &representation) : fRepresentation(representation)
36{
37 SetPrint();
38}
39
40////////////////////////////////////////////////////////////////////////////
41/// Constructor assuming an empty representation to be printed
46
47////////////////////////////////////////////////////////////////////////////
48/// Flag this cell as to be printed
53
54////////////////////////////////////////////////////////////////////////////
55/// Flag this cell as to be skipped
60
61////////////////////////////////////////////////////////////////////////////
62/// Flag this cell to be replaced by "..."
67
68////////////////////////////////////////////////////////////////////////////
69/// Return if the cell has to be printed
74
75////////////////////////////////////////////////////////////////////////////
76/// Return if the cell has to be skipped
81
82////////////////////////////////////////////////////////////////////////////
83/// Return if the cell has to be replaced by "..."
88
89const std::string &RDisplayElement::GetRepresentation() const
90{
91 return fRepresentation;
92}
93
95{
96 return fRepresentation.empty();
97}
98
99} // namespace RDF
100} // namespace Internal
101
102namespace RDF {
103
105{
106 // If the current element is wider than the widest element found, update the width
107 if (fWidths[fCurrentColumn] < w) {
108 if (w > std::numeric_limits<unsigned short>::max()) {
109 w = std::numeric_limits<unsigned short>::max();
110 }
111 fWidths[fCurrentColumn] = (unsigned short) w;
112 }
113}
114
115void RDisplay::AddToRow(const std::string &stringEle)
116{
117 // If the current element is wider than the widest element found, update the width
118 EnsureCurrentColumnWidth(stringEle.length());
119
120 // Save the element...
122
123 // ...and move to the next
124 MovePosition();
125}
126
127void RDisplay::AddCollectionToRow(const std::vector<std::string> &collection)
128{
129 auto row = fCurrentRow;
130 // For each element of the collection, save it. The first element will be in the current row, next ones will have
131 // their own row.
132 size_t collectionSize = collection.size();
133 for (size_t index = 0; index < collectionSize; ++index) {
134 auto stringEle = collection[index];
135 auto element = DElement_t(stringEle);
136
137 // Update the width if this element is the biggest found
138 EnsureCurrentColumnWidth(stringEle.length());
139
140 if (index < fNMaxCollectionElements) {
141 // Do nothing, by default DisplayElement is printed
142 } else if (index == fNMaxCollectionElements) {
143 element.SetDots();
144 // Be sure the "..." fit
146 } else {
147 // In the Print(), after the dots, all element will just be ignored.
148 element.SetIgnore();
149 }
150
151 // Save the element
152 fTable[row][fCurrentColumn] = element;
153 ++row;
154
155 if (index != collectionSize - 1 && fTable.size() <= row) {
156 // This is not the last element, prepare the next row for the next element, if not already done by another
157 // collection
158 fTable.push_back(std::vector<DElement_t>(fNColumns));
159 }
160 }
161 fNextRow = (fNextRow > row) ? fNextRow : row;
162 MovePosition();
163}
164
166{
167 // Go to the next element. If it is outside the row, just go the first element of the next row.
169 if (fCurrentColumn == fNColumns) {
171 fCurrentColumn = 0;
172 fNextRow = fCurrentRow + 1;
173 fTable.push_back(std::vector<DElement_t>(fNColumns));
174 }
175}
176
177RDisplay::RDisplay(const VecStr_t &columnNames, const VecStr_t &types, size_t nMaxCollectionElements)
178 : fTypes(types), fWidths(columnNames.size(), 0), fRepresentations(columnNames.size()),
179 fCollectionsRepresentations(columnNames.size()), fNColumns(columnNames.size()),
180 fNMaxCollectionElements(nMaxCollectionElements)
181{
182 // Add the first row with the names of the columns
183 fTable.push_back(std::vector<DElement_t>(columnNames.size()));
184 AddToRow("Row"); // Change the name of the first column from rdfentry_ to Row
185 for (auto name = columnNames.begin() + 1; name != columnNames.end(); ++name) {
186 AddToRow(*name);
187 }
188}
189
191{
192 size_t totalWidth = 0;
193
194 auto size = fWidths.size();
195 for (size_t i = 0; i < size; ++i) {
196 // The total width of the printed table also includes two spaces and a |,
197 // which are 3 extra characters per entry on the table.
198 totalWidth += fWidths[i] + 3;
199 if (totalWidth > fgMaxWidth) {
200 return size - i;
201 }
202 }
203 return 0;
204}
205
206std::string RDisplay::DashesBetweenLines(size_t lastColToPrint, bool allColumnsFit) const
207{
208 std::string DashesStr = "+";
209 for (size_t i = 0; i < lastColToPrint; ++i){
210 DashesStr += std::string(fWidths[i] + 2, '-'); // Need to add 2, because of the spaces, when printing
211 DashesStr += "+";
212 }
213 if (!allColumnsFit){ // The Print method has ... in case of long columns, which need to be surrounded by dashes
214 DashesStr += "-----+";
215 }
216 DashesStr += "\n";
217 return DashesStr;
218}
219
220void RDisplay::Print(const RPrintOptions &options) const
221{
222 auto ret = AsStringInternal(true, options);
223 std::cout << ret;
224}
225
226std::string RDisplay::AsString(const RPrintOptions &options) const
227{
228 return AsStringInternal(false, options);
229}
230
231std::string RDisplay::AsStringInternal(bool considerDots, const RPrintOptions &options) const
232{
233 switch (options.fFormat) {
234 case EPrintFormat::kMarkdown: return AsStringMarkdown(considerDots);
235 case EPrintFormat::kHtml: return AsStringHtml();
236 default: R__ASSERT(false);
237 }
238 return {};
239}
240
241std::string RDisplay::AsStringHtml() const
242{
243 std::stringstream ss;
244
245 ss << "<table style=\"border: 1px solid black; border-collapse: collapse;\">\n";
246 auto nrRows = fTable.size();
247 std::string elemType = "th";
248 int bgColorIdx = 0;
249 for (size_t rowIndex = 0; rowIndex < nrRows; ++rowIndex) {
250 const auto &row = fTable[rowIndex];
251
252 bool isRowSeparator =
253 std::any_of(row[0].GetRepresentation().begin(), row[0].GetRepresentation().end(), ::isdigit);
254
255 // Alternate rows' background color
256 static const char *bgColors[2] = {"#fff", "#eee"};
257 bgColorIdx = (bgColorIdx + isRowSeparator) & 1;
258 std::string bgColor = bgColors[bgColorIdx];
259
260 if (isRowSeparator) {
261 ss << " <tr style=\"border-top: 1px dotted; background: " + bgColor + "\">\n";
262 } else {
263 ss << " <tr style=\"background: " + bgColor + "\">\n";
264 }
265
266 for (const auto &element : row) {
267 ss << " <" + elemType + " style=\"padding: 1px 4px; border-right: 1px solid\">"
268 << element.GetRepresentation() << "</" + elemType + ">\n";
269 }
270 ss << " </tr>\n";
271
272 elemType = "td";
273 }
274 ss << "</table>";
275
276 return ss.str();
277}
278
279std::string RDisplay::AsStringMarkdown(bool considerDots) const
280{
281 std::stringstream ss;
282
283 size_t columnsToPrint = fNColumns;
284 const size_t columnsToShorten = GetNColumnsToShorten();
285 bool allColumnsFit = true;
286 if (fNColumns > 2u && columnsToShorten > 0u){ // Checking 2u, since first column is keeping track of rows
287 if (fNColumns > columnsToShorten + 1) { // Provided that the first column is "Row",
288 // columnsToShorten is guaranteed to be smaller than fNColumns
289 // Need to check if actual first column is being shortened
290 columnsToPrint = fNColumns - columnsToShorten;
291 } else { // Table has many columns and the first column is very wide;
292 // Thus, the first column is only the Row column and the actual first column is printed
293 columnsToPrint = 2;
294 }
295 if (considerDots)
296 Info("Print", "Only showing %zu columns out of %zu\n", columnsToPrint, fNColumns);
297
298 allColumnsFit = false;
299 }
300
302 Info("Print", "No collections shown since fNMaxCollectionElements is 0\n");
303
304 auto nrRows = fTable.size();
305 ss << DashesBetweenLines(columnsToPrint, allColumnsFit); // Print dashes in the top of the table
306 for (size_t rowIndex = 0; rowIndex < nrRows; ++rowIndex) {
307 const auto &row = fTable[rowIndex];
308
309 std::stringstream stringRow;
310 bool isRowEmpty = true; // It may happen during compacting that some rows are empty, this happens for example if
311 // collections have different size. Thanks to this flag, these rows are just ignored.
312 if (std::any_of(row[0].GetRepresentation().begin(), row[0].GetRepresentation().end(), ::isdigit)) {
313 // Check if the first column (Row) contains a digit to use it as indication for new row/entry
314 ss << DashesBetweenLines(columnsToPrint, allColumnsFit);
315 }
316 stringRow << "| ";
317 for (size_t columnIndex = 0; columnIndex < columnsToPrint; ++columnIndex) {
318 const auto &element = row[columnIndex];
319 std::string printedElement = "";
320
321 // TODO: add a function option to avoid this behavior
322 if (considerDots && element.IsDot()) {
323 printedElement = "...";
324 } else if (!considerDots || element.IsPrint()) {
325 printedElement = element.GetRepresentation();
326 } else { // IsIgnore
327 // Do nothing, printedElement remains ""
328 }
329 if (!printedElement.empty()) {
330 // Found at least one element, so the row is not empty.
331 isRowEmpty = false;
332 }
333
334 stringRow << std::left << std::setw(fWidths[columnIndex]) << std::setfill(fgSeparator) << printedElement
335 << " | ";
336 }
337 if (!isRowEmpty) {
338 if (!allColumnsFit) { // If there are column(s), that do not fit, a single column of dots is displayed
339 // in the right end of each (non-empty) row.
340 stringRow << "... | ";
341 }
342 ss << stringRow.str() << "\n";
343 }
344 }
345 ss << DashesBetweenLines(columnsToPrint, allColumnsFit); // Print dashes in the bottom of the table
346
347 return ss.str();
348}
349
350} // namespace RDF
351} // namespace ROOT
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
char * ret
Definition Rotated.cxx:221
#define R__ASSERT(e)
Checks condition e and reports a fatal error if it's false.
Definition TError.h:125
void Info(const char *location, const char *msgfmt,...)
Use this function for informational messages.
Definition TError.cxx:241
char name[80]
Definition TGX11.cxx:148
bool IsIgnore() const
Return if the cell has to be skipped.
bool IsDot() const
Return if the cell has to be replaced by "...".
const std::string & GetRepresentation() const
bool IsPrint() const
Return if the cell has to be printed.
void SetDots()
Flag this cell to be replaced by "...".
void SetPrint()
Flag this cell as to be printed.
void SetIgnore()
Flag this cell as to be skipped.
RDisplayElement()
Constructor assuming an empty representation to be printed.
void AddCollectionToRow(const VecStr_t &collection)
Adds a collection to the table.
size_t fCurrentColumn
Column that is being filled.
Definition RDisplay.hxx:98
ROOT::Internal::RDF::RDisplayElement DElement_t
Definition RDisplay.hxx:81
RDisplay(const VecStr_t &columnNames, const VecStr_t &types, size_t nMaxCollectionElements)
Creates an RDisplay to print the event values.
size_t fNMaxCollectionElements
Definition RDisplay.hxx:100
std::vector< std::vector< DElement_t > > fTable
String representation of the data to be printed.
Definition RDisplay.hxx:86
std::string AsStringHtml() const
void AddToRow(const std::string &stringEle)
Adds a single element to the next slot in the table.
size_t fCurrentRow
Row that is being filled.
Definition RDisplay.hxx:96
VecStr_t fRepresentations
Used by the JITted code to store the string representation of the data.
Definition RDisplay.hxx:89
std::string AsStringMarkdown(bool considerDots) const
void MovePosition()
Moves to the next cell.
std::vector< unsigned short > fWidths
Tracks the maximum width of each column, based on the largest element.
Definition RDisplay.hxx:87
void Print(const RPrintOptions &options={EPrintFormat::kMarkdown}) const
Prints the representation to the standard output.
static constexpr unsigned fgMaxWidth
Maximum width of the table that Print() displays.
Definition RDisplay.hxx:83
std::string AsString(const RPrintOptions &options={EPrintFormat::kMarkdown}) const
Returns the representation as a string.
void EnsureCurrentColumnWidth(size_t w)
size_t fNextRow
Next row to be filled.
Definition RDisplay.hxx:97
std::string DashesBetweenLines(size_t lastColToPrint, bool allColumnsFit) const
Generate dashes between entries in Print() and AsString() Methods.
std::string AsStringInternal(bool considerDots, const RPrintOptions &options={EPrintFormat::kMarkdown}) const
std::vector< std::string > VecStr_t
Definition RDisplay.hxx:80
std::vector< VecStr_t > fCollectionsRepresentations
Used by the JITted code to store the string representation of the data in case of collection.
Definition RDisplay.hxx:90
size_t GetNColumnsToShorten() const
Get the number of columns that do NOT fit in the characters limit.
static constexpr char fgSeparator
Spacing used to align the table entries.
Definition RDisplay.hxx:82
VecStr_t fTypes
This attribute stores the type of each column. It is needed by the interpreter to print it.
Definition RDisplay.hxx:85
size_t fNColumns
Number of columns to be printed.
Definition RDisplay.hxx:94