# HG changeset patch # User František Kučera # Date 1440894495 -7200 # Node ID 36db9fd27436eff323c405f887566e5d4b9a6830 # Parent 195d969a1fb1185bbc1f4dbac2aa59a44f4c7c23 BarChartFormatter: first version diff -r 195d969a1fb1 -r 36db9fd27436 java/sql-dk/src/info/globalcode/sql/dk/configuration/Configuration.java --- a/java/sql-dk/src/info/globalcode/sql/dk/configuration/Configuration.java Sun Aug 30 02:24:36 2015 +0200 +++ b/java/sql-dk/src/info/globalcode/sql/dk/configuration/Configuration.java Sun Aug 30 02:28:15 2015 +0200 @@ -19,7 +19,7 @@ import static info.globalcode.sql.dk.Xmlns.CONFIGURATION; import static info.globalcode.sql.dk.Functions.findByName; -import info.globalcode.sql.dk.formatting.DsvFormatter; +import info.globalcode.sql.dk.formatting.BarChartFormatter; import info.globalcode.sql.dk.formatting.SilentFormatter; import info.globalcode.sql.dk.formatting.SingleRecordFormatter; import info.globalcode.sql.dk.formatting.SingleValueFormatter; @@ -73,7 +73,9 @@ l.add(new FormatterDefinition(TabularPrefetchingFormatter.NAME, TabularPrefetchingFormatter.class.getName())); l.add(new FormatterDefinition(TabularWrappingFormatter.NAME, TabularWrappingFormatter.class.getName())); l.add(new FormatterDefinition(TeXFormatter.NAME, TeXFormatter.class.getName())); - l.add(new FormatterDefinition(DsvFormatter.NAME, DsvFormatter.class.getName())); + //l.add(new FormatterDefinition(DsvFormatter.NAME, DsvFormatter.class.getName())); + //l.add(new FormatterDefinition(SystemCommandExecutor.NAME, SystemCommandExecutor.class.getName())); + l.add(new FormatterDefinition(BarChartFormatter.NAME, BarChartFormatter.class.getName())); buildInFormatters = Collections.unmodifiableCollection(l); } @@ -88,7 +90,7 @@ /** * @param name - * @return + * @return * @throws ConfigurationException if no database with this name is configured */ public DatabaseDefinition getDatabase(String name) throws ConfigurationException { diff -r 195d969a1fb1 -r 36db9fd27436 java/sql-dk/src/info/globalcode/sql/dk/formatting/BarChartFormatter.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/java/sql-dk/src/info/globalcode/sql/dk/formatting/BarChartFormatter.java Sun Aug 30 02:28:15 2015 +0200 @@ -0,0 +1,96 @@ +/** + * SQL-DK + * Copyright © 2015 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.Functions; +import info.globalcode.sql.dk.configuration.PropertyDeclaration; +import info.globalcode.sql.dk.logging.LoggerProducer; +import java.math.BigDecimal; +import java.math.MathContext; +import java.math.RoundingMode; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * + * @author Ing. František Kučera (frantovo.cz) + */ +@PropertyDeclaration(name = BarChartFormatter.PROPERTY_PRECISION, type = Integer.class, defaultValue = BarChartFormatter.PROPERTY_PRECISION_DEFAULT, description = "number of characters representing 100 % in the bar chart") +public class BarChartFormatter extends TabularPrefetchingFormatter { + + public static final String NAME = "barchart"; // bash-completion:formatter + public static final String PROPERTY_PRECISION = "precision"; + protected static final String PROPERTY_PRECISION_DEFAULT = "100"; + private static final MathContext mathContext = MathContext.DECIMAL128; + public static final Logger log = LoggerProducer.getLogger(); + private final BigDecimal chartPrecision; + private final char chartFull; + private final char chartEmpty; + + public BarChartFormatter(FormatterContext formatterContext) { + super(formatterContext); + chartPrecision = BigDecimal.valueOf(formatterContext.getProperties().getInteger(PROPERTY_PRECISION, Integer.parseInt(PROPERTY_PRECISION_DEFAULT))); + chartFull = isAsciiNostalgia() ? '#' : '█'; + chartEmpty = isAsciiNostalgia() ? '~' : '░'; + // TODO: consider using partial blocks for more precision: https://en.wikipedia.org/wiki/Block_Elements + } + + @Override + protected void postprocessPrefetchedResultSet(ColumnsHeader currentHeader, List currentResultSet) { + super.postprocessPrefetchedResultSet(currentHeader, currentResultSet); + + updateColumnWidth(currentHeader.getColumnCount(), chartPrecision.intValue()); + + BigDecimal maximum = BigDecimal.ZERO; + BigDecimal minimum = BigDecimal.ZERO; + int lastIndex = currentHeader.getColumnCount() - 1; + + try { + for (Object[] row : currentResultSet) { + Object valueObject = row[lastIndex]; + if (valueObject != null) { + BigDecimal value = new BigDecimal(valueObject.toString()); + maximum = maximum.max(value); + minimum = minimum.min(value); + } + } + + BigDecimal range = maximum.subtract(minimum); + + for (Object[] row : currentResultSet) { + Object valueObject = row[lastIndex]; + if (valueObject != null) { + BigDecimal value = new BigDecimal(valueObject.toString()); + BigDecimal valueFromMinimum = value.subtract(minimum); + + BigDecimal points = chartPrecision.divide(range, mathContext).multiply(valueFromMinimum, mathContext); + int pointsRounded = points.setScale(0, RoundingMode.HALF_UP).intValue(); + row[lastIndex] = Functions.repeat(chartFull, pointsRounded) + Functions.repeat(chartEmpty, chartPrecision.intValue() - pointsRounded); + } + } + + } catch (NumberFormatException e) { + // https://en.wiktionary.org/wiki/parsable + log.log(Level.SEVERE, "Last column must be number or an object with toString() value parsable to a number."); + // FIXME: throw FormatterException + throw e; + } + } + +} diff -r 195d969a1fb1 -r 36db9fd27436 java/sql-dk/src/info/globalcode/sql/dk/formatting/TabularFormatter.java --- a/java/sql-dk/src/info/globalcode/sql/dk/formatting/TabularFormatter.java Sun Aug 30 02:24:36 2015 +0200 +++ b/java/sql-dk/src/info/globalcode/sql/dk/formatting/TabularFormatter.java Sun Aug 30 02:28:15 2015 +0200 @@ -277,4 +277,11 @@ protected void printTableIndent() { out.print(" "); } + + /** + * @return whether should print only ASCII characters instead of unlimited Unicode. + */ + protected boolean isAsciiNostalgia() { + return asciiNostalgia; + } } diff -r 195d969a1fb1 -r 36db9fd27436 java/sql-dk/src/info/globalcode/sql/dk/formatting/TabularPrefetchingFormatter.java --- a/java/sql-dk/src/info/globalcode/sql/dk/formatting/TabularPrefetchingFormatter.java Sun Aug 30 02:24:36 2015 +0200 +++ b/java/sql-dk/src/info/globalcode/sql/dk/formatting/TabularPrefetchingFormatter.java Sun Aug 30 02:28:15 2015 +0200 @@ -86,6 +86,8 @@ public void writeEndResultSet() { prefetchDone = true; + postprocessPrefetchedResultSet(currentHeader, currentResultSet); + super.writeStartResultSet(currentHeader); for (Object[] row : currentResultSet) { @@ -103,4 +105,15 @@ super.writeEndResultSet(); prefetchDone = false; } + + /** + * Optional post-processing – override in sub-classes if needed. + * Don't forget to {@linkplain #updateColumnWidth(int, int)} + * + * @param currentHeader + * @param currentResultSet + */ + protected void postprocessPrefetchedResultSet(ColumnsHeader currentHeader, List currentResultSet) { + } + }