transform the record formatter into the recfile formatter v_0
authorFrantišek Kučera <franta-hg@frantovo.cz>
Tue, 30 Apr 2019 19:49:17 +0200
branchv_0
changeset 2487f81cfa150d0
parent 247 3380ae5275be
child 249 7655df0622ee
transform the record formatter into the recfile formatter
still a human-readable format and very similar
but also machine-readable – can be processed in GNU Recutils and Relational pipes
java/sql-dk/src/main/java/info/globalcode/sql/dk/configuration/Configuration.java
java/sql-dk/src/main/java/info/globalcode/sql/dk/formatting/AbstractFormatter.java
java/sql-dk/src/main/java/info/globalcode/sql/dk/formatting/RecfileFormatter.java
java/sql-dk/src/main/java/info/globalcode/sql/dk/formatting/SingleRecordFormatter.java
java/sql-dk/src/main/java/info/globalcode/sql/dk/formatting/XmlFormatter.java
     1.1 --- a/java/sql-dk/src/main/java/info/globalcode/sql/dk/configuration/Configuration.java	Mon Apr 29 01:27:26 2019 +0200
     1.2 +++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/configuration/Configuration.java	Tue Apr 30 19:49:17 2019 +0200
     1.3 @@ -20,8 +20,8 @@
     1.4  import static info.globalcode.sql.dk.Xmlns.CONFIGURATION;
     1.5  import static info.globalcode.sql.dk.Functions.findByName;
     1.6  import info.globalcode.sql.dk.formatting.BarChartFormatter;
     1.7 +import info.globalcode.sql.dk.formatting.RecfileFormatter;
     1.8  import info.globalcode.sql.dk.formatting.SilentFormatter;
     1.9 -import info.globalcode.sql.dk.formatting.SingleRecordFormatter;
    1.10  import info.globalcode.sql.dk.formatting.SingleValueFormatter;
    1.11  import info.globalcode.sql.dk.formatting.TabularFormatter;
    1.12  import info.globalcode.sql.dk.formatting.TabularPrefetchingFormatter;
    1.13 @@ -66,7 +66,7 @@
    1.14  		Collection<FormatterDefinition> l = new ArrayList<>();
    1.15  		l.add(new FormatterDefinition(SilentFormatter.NAME, SilentFormatter.class.getName()));
    1.16  		l.add(new FormatterDefinition(SingleValueFormatter.NAME, SingleValueFormatter.class.getName()));
    1.17 -		l.add(new FormatterDefinition(SingleRecordFormatter.NAME, SingleRecordFormatter.class.getName()));
    1.18 +		l.add(new FormatterDefinition(RecfileFormatter.NAME, RecfileFormatter.class.getName()));
    1.19  		l.add(new FormatterDefinition(XmlFormatter.NAME, XmlFormatter.class.getName()));
    1.20  		l.add(new FormatterDefinition(XhtmlFormatter.NAME, XhtmlFormatter.class.getName()));
    1.21  		l.add(new FormatterDefinition(TabularFormatter.NAME, TabularFormatter.class.getName()));
     2.1 --- a/java/sql-dk/src/main/java/info/globalcode/sql/dk/formatting/AbstractFormatter.java	Mon Apr 29 01:27:26 2019 +0200
     2.2 +++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/formatting/AbstractFormatter.java	Tue Apr 30 19:49:17 2019 +0200
     2.3 @@ -41,12 +41,21 @@
     2.4  	private String currentQuery;
     2.5  	private int currentColumnsCount;
     2.6  	private int currentRowCount;
     2.7 +	private int resultSetCount;
     2.8  
     2.9  	public AbstractFormatter(FormatterContext formatterContext) {
    2.10  		this.formatterContext = formatterContext;
    2.11  		state.push(State.ROOT);
    2.12  	}
    2.13  
    2.14 +	protected String getCurrentRelationName() {
    2.15 +		if (getFormatterContext().getRelationNames() == null || getFormatterContext().getRelationNames().size() < resultSetCount) {
    2.16 +			return "r" + resultSetCount;
    2.17 +		} else {
    2.18 +			return getFormatterContext().getRelationNames().get(resultSetCount - 1);
    2.19 +		}
    2.20 +	}
    2.21 +
    2.22  	/*
    2.23  	 * root
    2.24  	 * .batch
    2.25 @@ -126,6 +135,7 @@
    2.26  	@Override
    2.27  	public void writeStartBatch() {
    2.28  		pushState(State.BATCH, EnumSet.of(State.ROOT));
    2.29 +		resultSetCount = 0;
    2.30  	}
    2.31  
    2.32  	@Override
    2.33 @@ -156,6 +166,7 @@
    2.34  	@Override
    2.35  	public void writeStartResultSet(ColumnsHeader header) {
    2.36  		pushState(State.RESULT_SET, EnumSet.of(State.STATEMENT));
    2.37 +		resultSetCount++;
    2.38  		currentRowCount = 0;
    2.39  		currentColumnsHeader = header;
    2.40  	}
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/formatting/RecfileFormatter.java	Tue Apr 30 19:49:17 2019 +0200
     3.3 @@ -0,0 +1,110 @@
     3.4 +/**
     3.5 + * SQL-DK
     3.6 + * Copyright © 2015 František Kučera (frantovo.cz)
     3.7 + *
     3.8 + * This program is free software: you can redistribute it and/or modify
     3.9 + * it under the terms of the GNU General Public License as published by
    3.10 + * the Free Software Foundation, either version 3 of the License, or
    3.11 + * (at your option) any later version.
    3.12 + *
    3.13 + * This program is distributed in the hope that it will be useful,
    3.14 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    3.15 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    3.16 + * GNU General Public License for more details.
    3.17 + *
    3.18 + * You should have received a copy of the GNU General Public License
    3.19 + * along with this program. If not, see <http://www.gnu.org/licenses/>.
    3.20 + */
    3.21 +package info.globalcode.sql.dk.formatting;
    3.22 +
    3.23 +import info.globalcode.sql.dk.ColorfulPrintWriter;
    3.24 +import info.globalcode.sql.dk.configuration.PropertyDeclaration;
    3.25 +import static info.globalcode.sql.dk.formatting.CommonProperties.COLORFUL;
    3.26 +import static info.globalcode.sql.dk.formatting.CommonProperties.COLORFUL_DESCRIPTION;
    3.27 +import java.util.logging.Level;
    3.28 +import java.util.logging.Logger;
    3.29 +
    3.30 +/**
    3.31 + * Prints results in the recfile format. This format is both human- and machine- readable. Can be
    3.32 + * processed by the <a href="https://www.gnu.org/software/recutils/">GNU Recutils</a>
    3.33 + * or <a href="https://relational-pipes.globalcode.info/">Relational pipes</a>.
    3.34 + *
    3.35 + * @author Ing. František Kučera (frantovo.cz)
    3.36 + */
    3.37 +@PropertyDeclaration(name = COLORFUL, defaultValue = "false", type = Boolean.class, description = COLORFUL_DESCRIPTION)
    3.38 +public class RecfileFormatter extends AbstractFormatter {
    3.39 +
    3.40 +	private static final Logger log = Logger.getLogger(RecfileFormatter.class.getName());
    3.41 +	public static final String NAME = "recfile"; // bash-completion:formatter
    3.42 +	private final ColorfulPrintWriter out;
    3.43 +	private boolean firstResult = true;
    3.44 +
    3.45 +	public RecfileFormatter(FormatterContext formatterContext) {
    3.46 +		super(formatterContext);
    3.47 +		out = new ColorfulPrintWriter(formatterContext.getOutputStream());
    3.48 +		out.setColorful(formatterContext.getProperties().getBoolean(COLORFUL, false));
    3.49 +	}
    3.50 +
    3.51 +	@Override
    3.52 +	public void writeStartResultSet(ColumnsHeader header) {
    3.53 +		super.writeStartResultSet(header);
    3.54 +		printResultSeparator();
    3.55 +		out.print(ColorfulPrintWriter.TerminalColor.Red, "%rec: ");
    3.56 +		printRecfileValue(getCurrentRelationName());
    3.57 +		// TODO: declare attribute data types where jdbc→recfile mapping is possible
    3.58 +	}
    3.59 +
    3.60 +	@Override
    3.61 +	public void writeStartRow() {
    3.62 +		super.writeStartRow();
    3.63 +		println();
    3.64 +	}
    3.65 +
    3.66 +	@Override
    3.67 +	public void writeColumnValue(Object value) {
    3.68 +		super.writeColumnValue(value);
    3.69 +		String columnName = getCurrentColumnsHeader().getColumnDescriptors().get(getCurrentColumnsCount() - 1).getLabel();
    3.70 +		out.print(ColorfulPrintWriter.TerminalColor.Green, columnName + ": ");
    3.71 +		printRecfileValue(value);
    3.72 +	}
    3.73 +
    3.74 +	@Override
    3.75 +	public void writeUpdatesResult(int updatedRowsCount) {
    3.76 +		super.writeUpdatesResult(updatedRowsCount);
    3.77 +		printResultSeparator();
    3.78 +		log.log(Level.INFO, "Updated records: {0}", updatedRowsCount);
    3.79 +	}
    3.80 +
    3.81 +	@Override
    3.82 +	public void writeEndBatch() {
    3.83 +		super.writeEndBatch();
    3.84 +		println();
    3.85 +	}
    3.86 +
    3.87 +	private void println() {
    3.88 +		out.println();
    3.89 +		out.flush();
    3.90 +	}
    3.91 +
    3.92 +	private void printRecfileValue(Object value) {
    3.93 +		if (value == null) {
    3.94 +			// TODO: null values in recfiles?
    3.95 +		} else {
    3.96 +			for (char ch : value.toString().toCharArray()) {
    3.97 +				out.print(ch);
    3.98 +				if (ch == '\n') {
    3.99 +					out.print(ColorfulPrintWriter.TerminalColor.Magenta, "+ ");
   3.100 +				}
   3.101 +			}
   3.102 +		}
   3.103 +		println();
   3.104 +	}
   3.105 +
   3.106 +	private void printResultSeparator() {
   3.107 +		if (firstResult) {
   3.108 +			firstResult = false;
   3.109 +		} else {
   3.110 +			println();
   3.111 +		}
   3.112 +	}
   3.113 +}
     4.1 --- a/java/sql-dk/src/main/java/info/globalcode/sql/dk/formatting/SingleRecordFormatter.java	Mon Apr 29 01:27:26 2019 +0200
     4.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.3 @@ -1,105 +0,0 @@
     4.4 -/**
     4.5 - * SQL-DK
     4.6 - * Copyright © 2015 František Kučera (frantovo.cz)
     4.7 - *
     4.8 - * This program is free software: you can redistribute it and/or modify
     4.9 - * it under the terms of the GNU General Public License as published by
    4.10 - * the Free Software Foundation, either version 3 of the License, or
    4.11 - * (at your option) any later version.
    4.12 - *
    4.13 - * This program is distributed in the hope that it will be useful,
    4.14 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    4.15 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    4.16 - * GNU General Public License for more details.
    4.17 - *
    4.18 - * You should have received a copy of the GNU General Public License
    4.19 - * along with this program. If not, see <http://www.gnu.org/licenses/>.
    4.20 - */
    4.21 -package info.globalcode.sql.dk.formatting;
    4.22 -
    4.23 -import info.globalcode.sql.dk.ColorfulPrintWriter;
    4.24 -import info.globalcode.sql.dk.Functions;
    4.25 -import info.globalcode.sql.dk.configuration.PropertyDeclaration;
    4.26 -import static info.globalcode.sql.dk.formatting.CommonProperties.COLORFUL;
    4.27 -import static info.globalcode.sql.dk.formatting.CommonProperties.COLORFUL_DESCRIPTION;
    4.28 -
    4.29 -/**
    4.30 - * Formatter intended for printing one record (or few records) with many columns.
    4.31 - * Prints each colum name and its value on separate line.
    4.32 - *
    4.33 - * @author Ing. František Kučera (frantovo.cz)
    4.34 - */
    4.35 -@PropertyDeclaration(name = COLORFUL, defaultValue = "true", type = Boolean.class, description = COLORFUL_DESCRIPTION)
    4.36 -public class SingleRecordFormatter extends AbstractFormatter {
    4.37 -
    4.38 -	public static final String NAME = "record"; // bash-completion:formatter
    4.39 -	private final ColorfulPrintWriter out;
    4.40 -	private boolean firstResult = true;
    4.41 -
    4.42 -	public SingleRecordFormatter(FormatterContext formatterContext) {
    4.43 -		super(formatterContext);
    4.44 -		out = new ColorfulPrintWriter(formatterContext.getOutputStream());
    4.45 -		out.setColorful(formatterContext.getProperties().getBoolean(COLORFUL, true));
    4.46 -	}
    4.47 -
    4.48 -	@Override
    4.49 -	public void writeStartResultSet(ColumnsHeader header) {
    4.50 -		super.writeStartResultSet(header);
    4.51 -		printResultSeparator();
    4.52 -	}
    4.53 -
    4.54 -	@Override
    4.55 -	public void writeStartRow() {
    4.56 -		super.writeStartRow();
    4.57 -		printRecordSeparator();
    4.58 -		out.print(ColorfulPrintWriter.TerminalColor.Red, "Record: ");
    4.59 -		out.print(getCurrentRowCount());
    4.60 -		println();
    4.61 -	}
    4.62 -
    4.63 -	@Override
    4.64 -	public void writeColumnValue(Object value) {
    4.65 -		super.writeColumnValue(value);
    4.66 -		String columnName = getCurrentColumnsHeader().getColumnDescriptors().get(getCurrentColumnsCount() - 1).getLabel();
    4.67 -		out.print(ColorfulPrintWriter.TerminalColor.Green, columnName + ": ");
    4.68 -		Functions.printValueWithWhitespaceReplaced(out, toString(value), null, ColorfulPrintWriter.TerminalColor.Red);
    4.69 -		println();
    4.70 -	}
    4.71 -
    4.72 -	private static String toString(Object value) {
    4.73 -		return String.valueOf(value);
    4.74 -	}
    4.75 -
    4.76 -	@Override
    4.77 -	public void writeUpdatesResult(int updatedRowsCount) {
    4.78 -		super.writeUpdatesResult(updatedRowsCount);
    4.79 -		printResultSeparator();
    4.80 -		out.print(ColorfulPrintWriter.TerminalColor.Red, "Updated records: ");
    4.81 -		out.println(updatedRowsCount);
    4.82 -		printBellAndFlush();
    4.83 -	}
    4.84 -
    4.85 -	private void printBellAndFlush() {
    4.86 -		out.bell();
    4.87 -		out.flush();
    4.88 -	}
    4.89 -
    4.90 -	private void println() {
    4.91 -		out.println();
    4.92 -		printBellAndFlush();
    4.93 -	}
    4.94 -
    4.95 -	private void printRecordSeparator() {
    4.96 -		if (getCurrentRowCount() > 1) {
    4.97 -			println();
    4.98 -		}
    4.99 -	}
   4.100 -
   4.101 -	private void printResultSeparator() {
   4.102 -		if (firstResult) {
   4.103 -			firstResult = false;
   4.104 -		} else {
   4.105 -			println();
   4.106 -		}
   4.107 -	}
   4.108 -}
     5.1 --- a/java/sql-dk/src/main/java/info/globalcode/sql/dk/formatting/XmlFormatter.java	Mon Apr 29 01:27:26 2019 +0200
     5.2 +++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/formatting/XmlFormatter.java	Tue Apr 30 19:49:17 2019 +0200
     5.3 @@ -85,7 +85,7 @@
     5.4  
     5.5  	private String currentDatabaseName;
     5.6  	private int statementCounter;
     5.7 -	private int resultSetCounter;
     5.8 +	
     5.9  
    5.10  	public XmlFormatter(FormatterContext formatterContext) {
    5.11  		super(formatterContext);
    5.12 @@ -109,7 +109,6 @@
    5.13  		attributes.put(new QName(null, XML_NS_PREFIX_SQLDK, "xmlns"), Xmlns.SQLDK);
    5.14  		printStartElement(qname("relpipe"), attributes);
    5.15  		statementCounter = 0;
    5.16 -		resultSetCounter = 0;
    5.17  
    5.18  	}
    5.19  
    5.20 @@ -183,18 +182,9 @@
    5.21  
    5.22  	}
    5.23  
    5.24 -	private String getCurrentRelationName() {
    5.25 -		if (getFormatterContext().getRelationNames() == null || getFormatterContext().getRelationNames().size() < resultSetCounter) {
    5.26 -			return "r" + resultSetCounter;
    5.27 -		} else {
    5.28 -			return getFormatterContext().getRelationNames().get(resultSetCounter - 1);
    5.29 -		}
    5.30 -	}
    5.31 -
    5.32  	@Override
    5.33  	public void writeStartResultSet(ColumnsHeader header) {
    5.34  		super.writeStartResultSet(header);
    5.35 -		resultSetCounter++;
    5.36  		printStartElement(qname(XML_ELEMENT_RELATION), singleAttribute(qnameDK(XML_ATTRIBUTE_STATEMENT), getCurrentStatementName()));
    5.37  		printTextElement(qname(XML_ELEMENT_NAME), null, getCurrentRelationName());
    5.38