diff -r 7e08730da258 -r 4a1864c3e867 java/sql-dk/src/info/globalcode/sql/dk/formatting/TabularFormatter.java --- a/java/sql-dk/src/info/globalcode/sql/dk/formatting/TabularFormatter.java Mon Mar 04 17:06:42 2019 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,308 +0,0 @@ -/** - * SQL-DK - * Copyright © 2013 František Kučera (frantovo.cz) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package info.globalcode.sql.dk.formatting; - -import info.globalcode.sql.dk.ColorfulPrintWriter; -import static info.globalcode.sql.dk.ColorfulPrintWriter.*; -import info.globalcode.sql.dk.Functions; -import static info.globalcode.sql.dk.Functions.lpad; -import static info.globalcode.sql.dk.Functions.rpad; -import static info.globalcode.sql.dk.Functions.repeat; -import info.globalcode.sql.dk.configuration.PropertyDeclaration; -import static info.globalcode.sql.dk.formatting.CommonProperties.COLORFUL; -import static info.globalcode.sql.dk.formatting.CommonProperties.COLORFUL_DESCRIPTION; -import java.sql.SQLException; -import java.sql.SQLXML; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - *

- * Prints human-readable output – tables of result sets and text messages with update counts. - *

- * - *

- * Longer values might break the table – overflow the cells – see alternative tabular formatters and - * the {@linkplain #PROPERTY_TRIM} property. - *

- * - * @author Ing. František Kučera (frantovo.cz) - * @see TabularPrefetchingFormatter - * @see TabularWrappingFormatter - */ -@PropertyDeclaration(name = COLORFUL, defaultValue = "true", type = Boolean.class, description = COLORFUL_DESCRIPTION) -@PropertyDeclaration(name = TabularFormatter.PROPERTY_ASCII, defaultValue = "false", type = Boolean.class, description = "whether to use ASCII table borders instead of unicode ones") -@PropertyDeclaration(name = TabularFormatter.PROPERTY_TRIM, defaultValue = "false", type = Boolean.class, description = "whether to trim the values to fit the column width") -@PropertyDeclaration(name = TabularFormatter.PROPERTY_HEADER_TYPE, defaultValue = "true", type = Boolean.class, description = "whether to print data types in column headers") -public class TabularFormatter extends AbstractFormatter { - - private static final Logger log = Logger.getLogger(TabularFormatter.class.getName()); - public static final String NAME = "tabular"; // bash-completion:formatter - private static final String HEADER_TYPE_PREFIX = " ("; - private static final String HEADER_TYPE_SUFFIX = ")"; - public static final String PROPERTY_ASCII = "ascii"; - public static final String PROPERTY_TRIM = "trim"; - public static final String PROPERTY_HEADER_TYPE = "headerTypes"; - protected ColorfulPrintWriter out; - private boolean firstResult = true; - private int[] columnWidth; - /** - * use ASCII borders instead of unicode ones - */ - private final boolean asciiNostalgia; - /** - * Trim values if they are longer than cell size - */ - private final boolean trimValues; - /** - * Print data type of each column in the header - */ - private final boolean printHeaderTypes; - - public TabularFormatter(FormatterContext formatterContext) { - super(formatterContext); - out = new ColorfulPrintWriter(formatterContext.getOutputStream()); - asciiNostalgia = formatterContext.getProperties().getBoolean(PROPERTY_ASCII, false); - trimValues = formatterContext.getProperties().getBoolean(PROPERTY_TRIM, false); - printHeaderTypes = formatterContext.getProperties().getBoolean(PROPERTY_HEADER_TYPE, true); - out.setColorful(formatterContext.getProperties().getBoolean(COLORFUL, true)); - } - - @Override - public void writeStartResultSet(ColumnsHeader header) { - super.writeStartResultSet(header); - printResultSeparator(); - - initColumnWidths(header.getColumnCount()); - - printTableIndent(); - printTableBorder("╭"); - - List columnDescriptors = header.getColumnDescriptors(); - - for (ColumnDescriptor cd : columnDescriptors) { - // padding: make header cell at least same width as data cells in this column - int typeWidth = printHeaderTypes ? cd.getTypeName().length() + HEADER_TYPE_PREFIX.length() + HEADER_TYPE_SUFFIX.length() : 0; - cd.setLabel(rpad(cd.getLabel(), getColumnWidth(cd.getColumnNumber()) - typeWidth)); - updateColumnWidth(cd.getColumnNumber(), cd.getLabel().length() + typeWidth); - - if (!cd.isFirstColumn()) { - printTableBorder("┬"); - } - printTableBorder(repeat('─', getColumnWidth(cd.getColumnNumber()) + 2)); - } - printTableBorder("╮"); - out.println(); - - for (ColumnDescriptor cd : columnDescriptors) { - if (cd.isFirstColumn()) { - printTableIndent(); - printTableBorder("│ "); - } else { - printTableBorder(" │ "); - } - out.print(TerminalStyle.Bright, cd.getLabel()); - if (printHeaderTypes) { - out.print(HEADER_TYPE_PREFIX); - out.print(cd.getTypeName()); - out.print(HEADER_TYPE_SUFFIX); - } - if (cd.isLastColumn()) { - printTableBorder(" │"); - } - } - out.println(); - - printTableIndent(); - printTableBorder("├"); - for (int i = 1; i <= header.getColumnCount(); i++) { - if (i > 1) { - printTableBorder("┼"); - } - printTableBorder(repeat('─', getColumnWidth(i) + 2)); - } - printTableBorder("┤"); - out.println(); - - out.flush(); - } - - /** - * Must be called before {@linkplain #updateColumnWidth(int, int)} and - * {@linkplain #getColumnWidth(int)} for each result set. - * - * @param columnCount number of columns in current result set - */ - protected void initColumnWidths(int columnCount) { - if (columnWidth == null) { - columnWidth = new int[columnCount]; - } - } - - protected void cleanColumnWidths() { - columnWidth = null; - } - - @Override - public void writeColumnValue(Object value) { - super.writeColumnValue(value); - writeColumnValueInternal(value); - } - - protected void writeColumnValueInternal(Object value) { - - if (isCurrentColumnFirst()) { - printTableIndent(); - printTableBorder("│ "); - } else { - printTableBorder(" │ "); - } - - printValueWithWhitespaceReplaced(toString(value)); - - if (isCurrentColumnLast()) { - printTableBorder(" │"); - } - - } - - protected void printValueWithWhitespaceReplaced(String text) { - Functions.printValueWithWhitespaceReplaced(out, text, TerminalColor.Cyan, TerminalColor.Red); - } - - protected int getColumnWidth(int columnNumber) { - return columnWidth[columnNumber - 1]; - } - - private void setColumnWidth(int columnNumber, int width) { - columnWidth[columnNumber - 1] = width; - } - - protected void updateColumnWidth(int columnNumber, int width) { - int oldWidth = getColumnWidth(columnNumber); - setColumnWidth(columnNumber, Math.max(width, oldWidth)); - - } - - protected String toString(Object value) { - final int width = getColumnWidth(getCurrentColumnsCount()); - String result; - if (value instanceof Number || value instanceof Boolean) { - result = lpad(String.valueOf(value), width); - } else { - if (value instanceof SQLXML) { - // TODO: move to a common method, share with other formatters - try { - value = ((SQLXML) value).getString(); - } catch (SQLException e) { - log.log(Level.SEVERE, "Unable to format XML", e); - } - } - - result = rpad(String.valueOf(value), width); - } - // ? value = (boolean) value ? "✔" : "✗"; - - if (trimValues && result.length() > width) { - result = result.substring(0, width - 1) + "…"; - } - - return result; - } - - @Override - public void writeEndRow() { - super.writeEndRow(); - writeEndRowInternal(); - } - - public void writeEndRowInternal() { - out.println(); - out.flush(); - } - - @Override - public void writeEndResultSet() { - int columnCount = getCurrentColumnsHeader().getColumnCount(); - super.writeEndResultSet(); - - printTableIndent(); - printTableBorder("╰"); - for (int i = 1; i <= columnCount; i++) { - if (i > 1) { - printTableBorder("┴"); - } - printTableBorder(repeat('─', getColumnWidth(i) + 2)); - } - printTableBorder("╯"); - out.println(); - - cleanColumnWidths(); - - out.print(TerminalColor.Yellow, "Record count: "); - out.println(getCurrentRowCount()); - out.bell(); - out.flush(); - } - - @Override - public void writeUpdatesResult(int updatedRowsCount) { - super.writeUpdatesResult(updatedRowsCount); - printResultSeparator(); - out.print(TerminalColor.Red, "Updated records: "); - out.println(updatedRowsCount); - out.bell(); - out.flush(); - } - - @Override - public void writeEndDatabase() { - super.writeEndDatabase(); - out.flush(); - } - - private void printResultSeparator() { - if (firstResult) { - firstResult = false; - } else { - out.println(); - } - } - - protected void printTableBorder(String border) { - if (asciiNostalgia) { - border = border.replaceAll("─", "-"); - border = border.replaceAll("│", "|"); - border = border.replaceAll("[╭┬╮├┼┤╰┴╯]", "+"); - } - - out.print(TerminalColor.Green, border); - } - - protected void printTableIndent() { - out.print(" "); - } - - /** - * @return whether should print only ASCII characters instead of unlimited Unicode. - */ - protected boolean isAsciiNostalgia() { - return asciiNostalgia; - } -}