franta-hg@174: /** franta-hg@174: * SQL-DK franta-hg@174: * Copyright © 2014 František Kučera (frantovo.cz) franta-hg@174: * franta-hg@174: * This program is free software: you can redistribute it and/or modify franta-hg@174: * it under the terms of the GNU General Public License as published by franta-hg@174: * the Free Software Foundation, either version 3 of the License, or franta-hg@174: * (at your option) any later version. franta-hg@174: * franta-hg@174: * This program is distributed in the hope that it will be useful, franta-hg@174: * but WITHOUT ANY WARRANTY; without even the implied warranty of franta-hg@174: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the franta-hg@174: * GNU General Public License for more details. franta-hg@174: * franta-hg@174: * You should have received a copy of the GNU General Public License franta-hg@174: * along with this program. If not, see . franta-hg@174: */ franta-hg@174: package info.globalcode.sql.dk.formatting; franta-hg@174: franta-hg@174: import info.globalcode.sql.dk.ColorfulPrintWriter; franta-hg@174: import info.globalcode.sql.dk.Constants; franta-hg@174: import java.util.Collections; franta-hg@174: import java.util.HashMap; franta-hg@174: import java.util.List; franta-hg@174: import java.util.Map; franta-hg@174: franta-hg@174: /** franta-hg@174: * Outputs result sets in (La)TeX format. franta-hg@174: * franta-hg@174: * @author Ing. František Kučera (frantovo.cz) franta-hg@174: */ franta-hg@174: public class TeXFormatter extends AbstractFormatter { franta-hg@174: franta-hg@174: public static final String NAME = "tex"; // bash-completion:formatter franta-hg@174: public static final String PROPERTY_COLORFUL = "color"; franta-hg@174: private static final ColorfulPrintWriter.TerminalColor COMMAND_COLOR = ColorfulPrintWriter.TerminalColor.Magenta; franta-hg@174: private static final ColorfulPrintWriter.TerminalColor OPTIONS_COLOR = ColorfulPrintWriter.TerminalColor.Yellow; franta-hg@174: private static final Map TEX_ESCAPE_MAP; franta-hg@174: private final ColorfulPrintWriter out; franta-hg@174: franta-hg@174: static { franta-hg@174: Map replacements = new HashMap<>(); franta-hg@174: franta-hg@174: replacements.put('\\', "\\textbackslash{}"); franta-hg@174: replacements.put('{', "\\{{}"); franta-hg@174: replacements.put('}', "\\}{}"); franta-hg@174: replacements.put('_', "\\_{}"); franta-hg@174: replacements.put('^', "\\textasciicircum{}"); franta-hg@174: replacements.put('#', "\\#{}"); franta-hg@174: replacements.put('&', "\\&{}"); franta-hg@174: replacements.put('$', "\\${}"); franta-hg@174: replacements.put('%', "\\%{}"); franta-hg@174: replacements.put('~', "\\textasciitilde{}"); franta-hg@174: replacements.put('-', "{-}"); franta-hg@174: franta-hg@174: TEX_ESCAPE_MAP = Collections.unmodifiableMap(replacements); franta-hg@174: } franta-hg@174: franta-hg@174: public TeXFormatter(FormatterContext formatterContext) { franta-hg@174: super(formatterContext); franta-hg@174: boolean colorful = formatterContext.getProperties().getBoolean(PROPERTY_COLORFUL, false); franta-hg@174: out = new ColorfulPrintWriter(formatterContext.getOutputStream(), false, colorful); franta-hg@174: } franta-hg@174: franta-hg@174: @Override franta-hg@174: public void writeStartBatch() { franta-hg@174: super.writeStartBatch(); franta-hg@174: franta-hg@174: printCommand("documentclass", "a4paper,twoside", "article", true); franta-hg@174: printCommand("usepackage", "T1", "fontenc", true); franta-hg@174: printCommand("usepackage", "utf8x", "inputenc", true); franta-hg@174: printCommand("usepackage", "pdfauthor={" + Constants.WEBSITE + "}, bookmarks=true,unicode,colorlinks=true,linkcolor=black,urlcolor=blue,citecolor=blue", "hyperref", true); franta-hg@174: printBegin("document"); franta-hg@174: } franta-hg@174: franta-hg@174: @Override franta-hg@174: public void writeEndBatch() { franta-hg@174: super.writeEndBatch(); franta-hg@174: printEnd("document"); franta-hg@174: } franta-hg@174: franta-hg@174: @Override franta-hg@174: public void writeColumnValue(Object value) { franta-hg@174: super.writeColumnValue(value); franta-hg@174: // TODO: arrays, numbers, booleans, nulls etc.: franta-hg@174: out.print(escapeTex(toString(value))); franta-hg@174: franta-hg@174: if (!isCurrentColumnLast()) { franta-hg@174: printColumnSeparator(); franta-hg@174: } franta-hg@174: } franta-hg@174: franta-hg@174: @Override franta-hg@174: public void writeEndRow() { franta-hg@174: super.writeEndRow(); franta-hg@174: printEndRow(); franta-hg@174: } franta-hg@174: franta-hg@174: @Override franta-hg@174: public void writeStartResultSet(ColumnsHeader header) { franta-hg@174: super.writeStartResultSet(header); franta-hg@174: printCommand("begin", null, "tabular", false); franta-hg@174: franta-hg@174: List columnDescriptors = header.getColumnDescriptors(); franta-hg@174: franta-hg@174: StringBuilder columnAlignments = new StringBuilder(); franta-hg@174: for (ColumnDescriptor cd : columnDescriptors) { franta-hg@174: if (cd.isNumeric() || cd.isBoolean()) { franta-hg@174: columnAlignments.append('r'); franta-hg@174: } else { franta-hg@174: columnAlignments.append('l'); franta-hg@174: } franta-hg@174: } franta-hg@174: franta-hg@174: printCommand(null, null, columnAlignments.toString(), true); franta-hg@174: printCommand("hline", null, null, true); franta-hg@174: franta-hg@174: for (ColumnDescriptor cd : columnDescriptors) { franta-hg@174: printCommand("textbf", null, cd.getLabel(), false); franta-hg@174: if (cd.isLastColumn()) { franta-hg@174: printEndRow(); franta-hg@174: } else { franta-hg@174: printColumnSeparator(); franta-hg@174: } franta-hg@174: } franta-hg@174: franta-hg@174: printCommand("hline", null, null, true); franta-hg@174: } franta-hg@174: franta-hg@174: @Override franta-hg@174: public void writeEndResultSet() { franta-hg@174: super.writeEndResultSet(); franta-hg@174: printCommand("hline", null, null, true); franta-hg@174: printEnd("tabular"); franta-hg@174: } franta-hg@174: franta-hg@174: private String escapeTex(String text) { franta-hg@174: if (text == null) { franta-hg@174: return null; franta-hg@174: } else { franta-hg@174: StringBuilder result = new StringBuilder(text.length() * 2); franta-hg@174: franta-hg@174: for (char ch : text.toCharArray()) { franta-hg@174: String replacement = TEX_ESCAPE_MAP.get(ch); franta-hg@174: result.append(replacement == null ? ch : replacement); franta-hg@174: } franta-hg@174: franta-hg@174: return result.toString(); franta-hg@174: } franta-hg@174: } franta-hg@174: franta-hg@174: protected String toString(Object value) { franta-hg@174: return String.valueOf(value); franta-hg@174: } franta-hg@174: franta-hg@174: private void printColumnSeparator() { franta-hg@174: out.print(COMMAND_COLOR, " & "); franta-hg@174: } franta-hg@174: franta-hg@174: private void printEndRow() { franta-hg@174: out.println(COMMAND_COLOR, " \\\\"); franta-hg@174: out.flush(); franta-hg@174: } franta-hg@174: franta-hg@174: /** franta-hg@174: * franta-hg@174: * @param command will not be escaped – should contain just a valid TeX command name franta-hg@174: * @param options will not be escaped – should be properly formatted to be printed inside [ franta-hg@174: * and ] franta-hg@174: * @param value will be escaped franta-hg@174: * @param println whether to print line end and flush franta-hg@174: */ franta-hg@174: private void printCommand(String command, String options, String value, boolean println) { franta-hg@174: franta-hg@174: if (command != null) { franta-hg@174: out.print(COMMAND_COLOR, "\\" + command); franta-hg@174: } franta-hg@174: franta-hg@174: if (options != null) { franta-hg@174: out.print(COMMAND_COLOR, "["); franta-hg@174: out.print(OPTIONS_COLOR, options); franta-hg@174: out.print(COMMAND_COLOR, "]"); franta-hg@174: } franta-hg@174: franta-hg@174: if (value != null) { franta-hg@174: out.print(COMMAND_COLOR, "{"); franta-hg@174: out.print(escapeTex(value)); franta-hg@174: out.print(COMMAND_COLOR, "}"); franta-hg@174: } franta-hg@174: franta-hg@174: if (println) { franta-hg@174: out.println(); franta-hg@174: out.flush(); franta-hg@174: } franta-hg@174: } franta-hg@174: franta-hg@174: private void printBegin(String environment) { franta-hg@174: printCommand("begin", null, environment, true); franta-hg@174: } franta-hg@174: franta-hg@174: private void printEnd(String environment) { franta-hg@174: printCommand("end", null, environment, true); franta-hg@174: } franta-hg@174: franta-hg@174: }