38 |
38 |
39 using namespace relpipe::reader; |
39 using namespace relpipe::reader; |
40 |
40 |
41 class TabularPrefetchingHandler : public handlers::RelationalReaderStringHandler { |
41 class TabularPrefetchingHandler : public handlers::RelationalReaderStringHandler { |
42 private: |
42 private: |
43 |
43 |
44 class ColorScheme { |
44 class ColorScheme { |
45 }; |
45 public: |
46 |
46 const char* ESC_BRIGHT = "\u001b[1m"; |
|
47 const char* ESC_RED = "\u001b[31m"; |
|
48 const char* ESC_GREEN = "\u001b[32m"; |
|
49 const char* ESC_BLUE = "\u001b[34m"; |
|
50 const char* ESC_YELLOW = "\u001b[33m"; |
|
51 const char* ESC_AMBER = "\u001b[38;5;166m"; |
|
52 const char* ESC_CYAN = "\u001b[36m"; |
|
53 const char* ESC_RESET = "\u001b[0m"; |
|
54 const char* ESC_EMPTY = ""; |
|
55 |
|
56 const char* header = ESC_BRIGHT; |
|
57 const char* border = ESC_GREEN; |
|
58 const char* value = ESC_CYAN; |
|
59 const char* relation = ESC_RED; |
|
60 const char* replacement = ESC_RED; |
|
61 const char* count = ESC_YELLOW; |
|
62 const char* reset = ESC_RESET; |
|
63 } cs; |
|
64 |
47 std::wstring_convert<std::codecvt_utf8<wchar_t>> convertor; // TODO: support also other encodings. |
65 std::wstring_convert<std::codecvt_utf8<wchar_t>> convertor; // TODO: support also other encodings. |
48 const char* ESC_BRIGHT = "\u001b[1m"; |
66 |
49 const char* ESC_RED = "\u001b[31m"; |
|
50 const char* ESC_GREEN = "\u001b[32m"; |
|
51 const char* ESC_YELLOW = "\u001b[33m"; |
|
52 const char* ESC_CYAN = "\u001b[36m"; |
|
53 const char* ESC_RESET = "\u001b[0m"; |
|
54 |
|
55 const char* ESC_HEADER = ESC_BRIGHT; |
|
56 const char* ESC_BORDER = ESC_GREEN; |
|
57 const char* ESC_VALUE = ESC_CYAN; |
|
58 const char* ESC_RELATION = ESC_RED; |
|
59 const char* ESC_REPLACEMENT = ESC_RED; |
|
60 const char* ESC_COUNT = ESC_YELLOW; |
|
61 |
67 |
62 const char* INDENT = " "; // table indent from the left |
68 const char* INDENT = " "; // table indent from the left |
63 |
69 |
64 std::ostream& output; |
70 std::ostream& output; |
65 Configuration& configuration; |
71 Configuration& configuration; |
73 std::vector<integer_t> columnWidths; |
79 std::vector<integer_t> columnWidths; |
74 std::vector<string_t> values; // all values are saved here and processed at the end of the relation |
80 std::vector<string_t> values; // all values are saved here and processed at the end of the relation |
75 integer_t columnCount = 0; |
81 integer_t columnCount = 0; |
76 |
82 |
77 const string_t colorizeReplacement(const string_t &replacement, const char* valueColor) { |
83 const string_t colorizeReplacement(const string_t &replacement, const char* valueColor) { |
78 return convertor.from_bytes(ESC_RESET) + convertor.from_bytes(ESC_REPLACEMENT) + replacement + convertor.from_bytes(ESC_RESET) + convertor.from_bytes(valueColor); |
84 return convertor.from_bytes(cs.reset) + convertor.from_bytes(cs.replacement) + replacement + convertor.from_bytes(cs.reset) + convertor.from_bytes(valueColor); |
79 } |
85 } |
80 |
86 |
81 /** |
87 /** |
82 * Sanitizes whitespace that could broke table layout. |
88 * Sanitizes whitespace that could broke table layout. |
83 * |
89 * |
144 } |
150 } |
145 return width; |
151 return width; |
146 } |
152 } |
147 |
153 |
148 void printHorizontalLine(const string_t& left, const string_t& middle, const string_t& right, const string_t& bar) { |
154 void printHorizontalLine(const string_t& left, const string_t& middle, const string_t& right, const string_t& bar) { |
149 output << INDENT << ESC_BORDER; |
155 output << INDENT << cs.border; |
150 output << convertor.to_bytes(left); |
156 output << convertor.to_bytes(left); |
151 for (size_t c = 0; c < columnCount; c++) { |
157 for (size_t c = 0; c < columnCount; c++) { |
152 integer_t width = columnWidths[c]; |
158 integer_t width = columnWidths[c]; |
153 for (integer_t w = 0; w < (width + 2); w++) output << convertor.to_bytes(bar); // 2 = left and right padding of the value |
159 for (integer_t w = 0; w < (width + 2); w++) output << convertor.to_bytes(bar); // 2 = left and right padding of the value |
154 if (c < (columnCount - 1)) output << convertor.to_bytes(middle); |
160 if (c < (columnCount - 1)) output << convertor.to_bytes(middle); |
155 } |
161 } |
156 output << convertor.to_bytes(right); |
162 output << convertor.to_bytes(right); |
157 output << ESC_RESET << std::endl; |
163 output << cs.reset << std::endl; |
158 |
164 |
159 } |
165 } |
160 |
166 |
161 void printCachedData() { |
167 void printCachedData() { |
162 // Compute column widths and paddings: |
168 // Compute column widths and paddings: |
183 else if (configuration.tableStyle == Configuration::TableStyle::HorizontalOnly) verticalSeparator = " "; |
189 else if (configuration.tableStyle == Configuration::TableStyle::HorizontalOnly) verticalSeparator = " "; |
184 else if (configuration.tableStyle == Configuration::TableStyle::Ascii) verticalSeparator = "|"; |
190 else if (configuration.tableStyle == Configuration::TableStyle::Ascii) verticalSeparator = "|"; |
185 else throw RelpipeReaderException(L"Unsupported TableStyle: " + std::to_wstring((int) configuration.tableStyle)); |
191 else throw RelpipeReaderException(L"Unsupported TableStyle: " + std::to_wstring((int) configuration.tableStyle)); |
186 |
192 |
187 // Print column headers: |
193 // Print column headers: |
188 output << INDENT << ESC_BORDER << verticalSeparator << ESC_RESET; |
194 output << INDENT << cs.border << verticalSeparator << cs.reset; |
189 for (size_t i = 0; i < columnCount; i++) { |
195 for (size_t i = 0; i < columnCount; i++) { |
190 output << " " << convertor.to_bytes(formatValue(columnNames[i], ESC_HEADER)); |
196 output << " " << convertor.to_bytes(formatValue(columnNames[i], cs.header)); |
191 for (integer_t p = 0; p < paddings[i]; p++) { |
197 for (integer_t p = 0; p < paddings[i]; p++) { |
192 output << " "; |
198 output << " "; |
193 } |
199 } |
194 if (getConfiguration(writeTypes)) output << " (" << convertor.to_bytes(columnTypeCodes[i]) << ")"; |
200 if (getConfiguration(writeTypes)) output << " (" << convertor.to_bytes(columnTypeCodes[i]) << ")"; |
195 output << ESC_BORDER << " " << verticalSeparator << ESC_RESET; |
201 output << cs.border << " " << verticalSeparator << cs.reset; |
196 } |
202 } |
197 output << std::endl; |
203 output << std::endl; |
198 if (configuration.tableStyle == Configuration::TableStyle::Rounded) printHorizontalLine(L"├", L"┼", L"┤", L"─"); |
204 if (configuration.tableStyle == Configuration::TableStyle::Rounded) printHorizontalLine(L"├", L"┼", L"┤", L"─"); |
199 else if (configuration.tableStyle == Configuration::TableStyle::Sharp) printHorizontalLine(L"├", L"┼", L"┤", L"─"); |
205 else if (configuration.tableStyle == Configuration::TableStyle::Sharp) printHorizontalLine(L"├", L"┼", L"┤", L"─"); |
200 else if (configuration.tableStyle == Configuration::TableStyle::SharpDouble) printHorizontalLine(L"╠", L"╬", L"╣", L"═"); |
206 else if (configuration.tableStyle == Configuration::TableStyle::SharpDouble) printHorizontalLine(L"╠", L"╬", L"╣", L"═"); |
203 else throw RelpipeReaderException(L"Unsupported TableStyle: " + std::to_wstring((int) configuration.tableStyle)); |
209 else throw RelpipeReaderException(L"Unsupported TableStyle: " + std::to_wstring((int) configuration.tableStyle)); |
204 |
210 |
205 // Print particular rows: |
211 // Print particular rows: |
206 for (size_t i = 0; i < values.size(); i++) { |
212 for (size_t i = 0; i < values.size(); i++) { |
207 integer_t columnIndex = i % columnCount; |
213 integer_t columnIndex = i % columnCount; |
208 if (columnIndex == 0) output << INDENT << ESC_BORDER << verticalSeparator << ESC_RESET; |
214 if (columnIndex == 0) output << INDENT << cs.border << verticalSeparator << cs.reset; |
209 string_t stringValue = values[i]; |
215 string_t stringValue = values[i]; |
210 integer_t padding = columnWidths[columnIndex] - computeWidth(stringValue); |
216 integer_t padding = columnWidths[columnIndex] - computeWidth(stringValue); |
211 boolean_t alignRight = columnTypes[columnIndex] == TypeId::BOOLEAN || columnTypes[columnIndex] == TypeId::INTEGER; |
217 boolean_t alignRight = columnTypes[columnIndex] == TypeId::BOOLEAN || columnTypes[columnIndex] == TypeId::INTEGER; |
212 |
218 |
213 if (alignRight) for (integer_t p = 0; p < padding; p++) output << " "; |
219 if (alignRight) for (integer_t p = 0; p < padding; p++) output << " "; |
214 output << " " << convertor.to_bytes(formatValue(stringValue, ESC_VALUE)); |
220 output << " " << convertor.to_bytes(formatValue(stringValue, cs.value)); |
215 if (!alignRight) for (integer_t p = 0; p < padding; p++) output << " "; |
221 if (!alignRight) for (integer_t p = 0; p < padding; p++) output << " "; |
216 |
222 |
217 output << ESC_BORDER << " " << verticalSeparator << ESC_RESET; |
223 output << cs.border << " " << verticalSeparator << cs.reset; |
218 if (columnIndex == (columnCount - 1)) output << std::endl; |
224 if (columnIndex == (columnCount - 1)) output << std::endl; |
219 } |
225 } |
220 if (configuration.tableStyle == Configuration::TableStyle::Rounded) printHorizontalLine(L"╰", L"┴", L"╯", L"─"); |
226 if (configuration.tableStyle == Configuration::TableStyle::Rounded) printHorizontalLine(L"╰", L"┴", L"╯", L"─"); |
221 else if (configuration.tableStyle == Configuration::TableStyle::Sharp) printHorizontalLine(L"└", L"┴", L"┘", L"─"); |
227 else if (configuration.tableStyle == Configuration::TableStyle::Sharp) printHorizontalLine(L"└", L"┴", L"┘", L"─"); |
222 else if (configuration.tableStyle == Configuration::TableStyle::SharpDouble) printHorizontalLine(L"╚", L"╩", L"╝", L"═"); |
228 else if (configuration.tableStyle == Configuration::TableStyle::SharpDouble) printHorizontalLine(L"╚", L"╩", L"╝", L"═"); |
224 else if (configuration.tableStyle == Configuration::TableStyle::Ascii) printHorizontalLine(L"+", L"+", L"+", L"-"); |
230 else if (configuration.tableStyle == Configuration::TableStyle::Ascii) printHorizontalLine(L"+", L"+", L"+", L"-"); |
225 else throw RelpipeReaderException(L"Unsupported TableStyle: " + std::to_wstring((int) configuration.tableStyle)); |
231 else throw RelpipeReaderException(L"Unsupported TableStyle: " + std::to_wstring((int) configuration.tableStyle)); |
226 |
232 |
227 if (getConfiguration(writeRecordCount)) { |
233 if (getConfiguration(writeRecordCount)) { |
228 integer_t recordCount = values.size() / columnCount; |
234 integer_t recordCount = values.size() / columnCount; |
229 output << ESC_COUNT << "Record count: " << ESC_RESET << recordCount << std::endl; |
235 output << cs.count << "Record count: " << cs.reset << recordCount << std::endl; |
230 } |
236 } |
231 |
237 |
232 values.clear(); |
238 values.clear(); |
233 } |
239 } |
234 |
240 |
246 currentRelationConfiguration = &configuration.relationConfigurations[i]; |
252 currentRelationConfiguration = &configuration.relationConfigurations[i]; |
247 break; // it there are multiple matches, only the first configuration is used |
253 break; // it there are multiple matches, only the first configuration is used |
248 } |
254 } |
249 } |
255 } |
250 |
256 |
251 if (getConfiguration(writeRelationName)) output << ESC_RELATION << convertor.to_bytes(name) << ":" << ESC_RESET << endl; |
257 if (configuration.colorScheme == Configuration::ColorScheme::Greenish) { |
|
258 cs = ColorScheme(); |
|
259 } else if (configuration.colorScheme == Configuration::ColorScheme::Amberish) { |
|
260 cs = ColorScheme(); |
|
261 cs.border = cs.ESC_YELLOW; |
|
262 cs.value = cs.ESC_AMBER; |
|
263 } else if (configuration.colorScheme == Configuration::ColorScheme::Monochrome) { |
|
264 cs = ColorScheme(); |
|
265 cs.border = cs.ESC_EMPTY; |
|
266 cs.count = cs.ESC_EMPTY; |
|
267 cs.header = cs.ESC_EMPTY; |
|
268 cs.relation = cs.ESC_EMPTY; |
|
269 cs.replacement = cs.ESC_EMPTY; |
|
270 cs.reset = cs.ESC_EMPTY; |
|
271 cs.value = cs.ESC_EMPTY; |
|
272 } else if (configuration.colorScheme == Configuration::ColorScheme::Midnight) { |
|
273 cs = ColorScheme(); |
|
274 cs.relation = cs.ESC_CYAN; |
|
275 cs.count = cs.ESC_CYAN; |
|
276 cs.border = cs.ESC_BLUE; |
|
277 cs.value = cs.ESC_EMPTY; |
|
278 } else { |
|
279 throw RelpipeReaderException(L"Unsupported ColorScheme: " + std::to_wstring((int) configuration.colorScheme)); |
|
280 } |
|
281 |
|
282 if (getConfiguration(writeRelationName)) output << cs.relation << convertor.to_bytes(name) << ":" << cs.reset << endl; |
252 columnCount = attributes.size(); |
283 columnCount = attributes.size(); |
253 columnTypes.resize(columnCount); |
284 columnTypes.resize(columnCount); |
254 columnTypeCodes.resize(columnCount); |
285 columnTypeCodes.resize(columnCount); |
255 columnNames.resize(columnCount); |
286 columnNames.resize(columnCount); |
256 columnWidths.resize(columnCount, 0); |
287 columnWidths.resize(columnCount, 0); |