# HG changeset patch # User František Kučera # Date 1396819974 -7200 # Node ID 3c6d560a1d146011b2a95f542470aeb263ba588a # Parent b48a82a64a02a2821c5579585cf2ba2d5a6b709d TeXFormatter: first version diff -r b48a82a64a02 -r 3c6d560a1d14 java/sql-dk/src/info/globalcode/sql/dk/configuration/Configuration.java --- a/java/sql-dk/src/info/globalcode/sql/dk/configuration/Configuration.java Sun Apr 06 17:53:36 2014 +0200 +++ b/java/sql-dk/src/info/globalcode/sql/dk/configuration/Configuration.java Sun Apr 06 23:32:54 2014 +0200 @@ -24,6 +24,7 @@ import info.globalcode.sql.dk.formatting.TabularFormatter; import info.globalcode.sql.dk.formatting.TabularPrefetchingFormatter; import info.globalcode.sql.dk.formatting.TabularWrappingFormatter; +import info.globalcode.sql.dk.formatting.TeXFormatter; import info.globalcode.sql.dk.formatting.XhtmlFormatter; import info.globalcode.sql.dk.formatting.XmlFormatter; import java.util.ArrayList; @@ -68,6 +69,7 @@ l.add(new FormatterDefinition(TabularFormatter.NAME, TabularFormatter.class.getName())); 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())); buildInFormatters = Collections.unmodifiableCollection(l); } diff -r b48a82a64a02 -r 3c6d560a1d14 java/sql-dk/src/info/globalcode/sql/dk/formatting/ColumnDescriptor.java --- a/java/sql-dk/src/info/globalcode/sql/dk/formatting/ColumnDescriptor.java Sun Apr 06 17:53:36 2014 +0200 +++ b/java/sql-dk/src/info/globalcode/sql/dk/formatting/ColumnDescriptor.java Sun Apr 06 23:32:54 2014 +0200 @@ -17,6 +17,8 @@ */ package info.globalcode.sql.dk.formatting; +import java.sql.Types; + /** * * @author Ing. František Kučera (frantovo.cz) @@ -96,4 +98,25 @@ public void setColumnNumber(int columnNumber) { this.columnNumber = columnNumber; } + + public boolean isBoolean() { + return type == Types.BOOLEAN; + } + + public boolean isNumeric() { + switch (type) { + case Types.BIGINT: + case Types.DECIMAL: + case Types.DOUBLE: + case Types.FLOAT: + case Types.INTEGER: + case Types.NUMERIC: + case Types.REAL: + case Types.SMALLINT: + case Types.TINYINT: + return true; + default: + return false; + } + } } diff -r b48a82a64a02 -r 3c6d560a1d14 java/sql-dk/src/info/globalcode/sql/dk/formatting/TeXFormatter.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/java/sql-dk/src/info/globalcode/sql/dk/formatting/TeXFormatter.java Sun Apr 06 23:32:54 2014 +0200 @@ -0,0 +1,205 @@ +/** + * SQL-DK + * Copyright © 2014 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 info.globalcode.sql.dk.Constants; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Outputs result sets in (La)TeX format. + * + * @author Ing. František Kučera (frantovo.cz) + */ +public class TeXFormatter extends AbstractFormatter { + + public static final String NAME = "tex"; // bash-completion:formatter + public static final String PROPERTY_COLORFUL = "color"; + private static final ColorfulPrintWriter.TerminalColor COMMAND_COLOR = ColorfulPrintWriter.TerminalColor.Magenta; + private static final ColorfulPrintWriter.TerminalColor OPTIONS_COLOR = ColorfulPrintWriter.TerminalColor.Yellow; + private static final Map TEX_ESCAPE_MAP; + private final ColorfulPrintWriter out; + + static { + Map replacements = new HashMap<>(); + + replacements.put('\\', "\\textbackslash{}"); + replacements.put('{', "\\{{}"); + replacements.put('}', "\\}{}"); + replacements.put('_', "\\_{}"); + replacements.put('^', "\\textasciicircum{}"); + replacements.put('#', "\\#{}"); + replacements.put('&', "\\&{}"); + replacements.put('$', "\\${}"); + replacements.put('%', "\\%{}"); + replacements.put('~', "\\textasciitilde{}"); + replacements.put('-', "{-}"); + + TEX_ESCAPE_MAP = Collections.unmodifiableMap(replacements); + } + + public TeXFormatter(FormatterContext formatterContext) { + super(formatterContext); + boolean colorful = formatterContext.getProperties().getBoolean(PROPERTY_COLORFUL, false); + out = new ColorfulPrintWriter(formatterContext.getOutputStream(), false, colorful); + } + + @Override + public void writeStartBatch() { + super.writeStartBatch(); + + printCommand("documentclass", "a4paper,twoside", "article", true); + printCommand("usepackage", "T1", "fontenc", true); + printCommand("usepackage", "utf8x", "inputenc", true); + printCommand("usepackage", "pdfauthor={" + Constants.WEBSITE + "}, bookmarks=true,unicode,colorlinks=true,linkcolor=black,urlcolor=blue,citecolor=blue", "hyperref", true); + printBegin("document"); + } + + @Override + public void writeEndBatch() { + super.writeEndBatch(); + printEnd("document"); + } + + @Override + public void writeColumnValue(Object value) { + super.writeColumnValue(value); + // TODO: arrays, numbers, booleans, nulls etc.: + out.print(escapeTex(toString(value))); + + if (!isCurrentColumnLast()) { + printColumnSeparator(); + } + } + + @Override + public void writeEndRow() { + super.writeEndRow(); + printEndRow(); + } + + @Override + public void writeStartResultSet(ColumnsHeader header) { + super.writeStartResultSet(header); + printCommand("begin", null, "tabular", false); + + List columnDescriptors = header.getColumnDescriptors(); + + StringBuilder columnAlignments = new StringBuilder(); + for (ColumnDescriptor cd : columnDescriptors) { + if (cd.isNumeric() || cd.isBoolean()) { + columnAlignments.append('r'); + } else { + columnAlignments.append('l'); + } + } + + printCommand(null, null, columnAlignments.toString(), true); + printCommand("hline", null, null, true); + + for (ColumnDescriptor cd : columnDescriptors) { + printCommand("textbf", null, cd.getLabel(), false); + if (cd.isLastColumn()) { + printEndRow(); + } else { + printColumnSeparator(); + } + } + + printCommand("hline", null, null, true); + } + + @Override + public void writeEndResultSet() { + super.writeEndResultSet(); + printCommand("hline", null, null, true); + printEnd("tabular"); + } + + private String escapeTex(String text) { + if (text == null) { + return null; + } else { + StringBuilder result = new StringBuilder(text.length() * 2); + + for (char ch : text.toCharArray()) { + String replacement = TEX_ESCAPE_MAP.get(ch); + result.append(replacement == null ? ch : replacement); + } + + return result.toString(); + } + } + + protected String toString(Object value) { + return String.valueOf(value); + } + + private void printColumnSeparator() { + out.print(COMMAND_COLOR, " & "); + } + + private void printEndRow() { + out.println(COMMAND_COLOR, " \\\\"); + out.flush(); + } + + /** + * + * @param command will not be escaped – should contain just a valid TeX command name + * @param options will not be escaped – should be properly formatted to be printed inside [ + * and ] + * @param value will be escaped + * @param println whether to print line end and flush + */ + private void printCommand(String command, String options, String value, boolean println) { + + if (command != null) { + out.print(COMMAND_COLOR, "\\" + command); + } + + if (options != null) { + out.print(COMMAND_COLOR, "["); + out.print(OPTIONS_COLOR, options); + out.print(COMMAND_COLOR, "]"); + } + + if (value != null) { + out.print(COMMAND_COLOR, "{"); + out.print(escapeTex(value)); + out.print(COMMAND_COLOR, "}"); + } + + if (println) { + out.println(); + out.flush(); + } + } + + private void printBegin(String environment) { + printCommand("begin", null, environment, true); + } + + private void printEnd(String environment) { + printCommand("end", null, environment, true); + } + +}