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: }