1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/formatting/TeXFormatter.java Mon Mar 04 20:15:24 2019 +0100
1.3 @@ -0,0 +1,208 @@
1.4 +/**
1.5 + * SQL-DK
1.6 + * Copyright © 2014 František Kučera (frantovo.cz)
1.7 + *
1.8 + * This program is free software: you can redistribute it and/or modify
1.9 + * it under the terms of the GNU General Public License as published by
1.10 + * the Free Software Foundation, either version 3 of the License, or
1.11 + * (at your option) any later version.
1.12 + *
1.13 + * This program is distributed in the hope that it will be useful,
1.14 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1.15 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.16 + * GNU General Public License for more details.
1.17 + *
1.18 + * You should have received a copy of the GNU General Public License
1.19 + * along with this program. If not, see <http://www.gnu.org/licenses/>.
1.20 + */
1.21 +package info.globalcode.sql.dk.formatting;
1.22 +
1.23 +import info.globalcode.sql.dk.ColorfulPrintWriter;
1.24 +import info.globalcode.sql.dk.Constants;
1.25 +import info.globalcode.sql.dk.configuration.PropertyDeclaration;
1.26 +import static info.globalcode.sql.dk.formatting.CommonProperties.COLORFUL;
1.27 +import static info.globalcode.sql.dk.formatting.CommonProperties.COLORFUL_DESCRIPTION;
1.28 +import java.util.Collections;
1.29 +import java.util.HashMap;
1.30 +import java.util.List;
1.31 +import java.util.Map;
1.32 +
1.33 +/**
1.34 + * Outputs result sets in (La)TeX format.
1.35 + *
1.36 + * @author Ing. František Kučera (frantovo.cz)
1.37 + */
1.38 +@PropertyDeclaration(name = COLORFUL, defaultValue = "false", type = Boolean.class, description = COLORFUL_DESCRIPTION)
1.39 +public class TeXFormatter extends AbstractFormatter {
1.40 +
1.41 + public static final String NAME = "tex"; // bash-completion:formatter
1.42 + private static final ColorfulPrintWriter.TerminalColor COMMAND_COLOR = ColorfulPrintWriter.TerminalColor.Magenta;
1.43 + private static final ColorfulPrintWriter.TerminalColor OPTIONS_COLOR = ColorfulPrintWriter.TerminalColor.Yellow;
1.44 + private static final Map<Character, String> TEX_ESCAPE_MAP;
1.45 + private final ColorfulPrintWriter out;
1.46 +
1.47 + static {
1.48 + Map<Character, String> replacements = new HashMap<>();
1.49 +
1.50 + replacements.put('\\', "\\textbackslash{}");
1.51 + replacements.put('{', "\\{{}");
1.52 + replacements.put('}', "\\}{}");
1.53 + replacements.put('_', "\\_{}");
1.54 + replacements.put('^', "\\textasciicircum{}");
1.55 + replacements.put('#', "\\#{}");
1.56 + replacements.put('&', "\\&{}");
1.57 + replacements.put('$', "\\${}");
1.58 + replacements.put('%', "\\%{}");
1.59 + replacements.put('~', "\\textasciitilde{}");
1.60 + replacements.put('-', "{-}");
1.61 +
1.62 + TEX_ESCAPE_MAP = Collections.unmodifiableMap(replacements);
1.63 + }
1.64 +
1.65 + public TeXFormatter(FormatterContext formatterContext) {
1.66 + super(formatterContext);
1.67 + boolean colorful = formatterContext.getProperties().getBoolean(COLORFUL, false);
1.68 + out = new ColorfulPrintWriter(formatterContext.getOutputStream(), false, colorful);
1.69 + }
1.70 +
1.71 + @Override
1.72 + public void writeStartBatch() {
1.73 + super.writeStartBatch();
1.74 +
1.75 + printCommand("documentclass", "a4paper,twoside", "article", true);
1.76 + printCommand("usepackage", "T1", "fontenc", true);
1.77 + printCommand("usepackage", "utf8x", "inputenc", true);
1.78 + printCommand("usepackage", "pdfauthor={" + Constants.WEBSITE + "}, bookmarks=true,unicode,colorlinks=true,linkcolor=black,urlcolor=blue,citecolor=blue", "hyperref", true);
1.79 + printBegin("document");
1.80 + }
1.81 +
1.82 + @Override
1.83 + public void writeEndBatch() {
1.84 + super.writeEndBatch();
1.85 + printEnd("document");
1.86 + }
1.87 +
1.88 + @Override
1.89 + public void writeColumnValue(Object value) {
1.90 + super.writeColumnValue(value);
1.91 + // TODO: arrays, numbers, booleans, nulls etc.:
1.92 + out.print(escapeTex(toString(value)));
1.93 +
1.94 + if (!isCurrentColumnLast()) {
1.95 + printColumnSeparator();
1.96 + }
1.97 + }
1.98 +
1.99 + @Override
1.100 + public void writeEndRow() {
1.101 + super.writeEndRow();
1.102 + printEndRow();
1.103 + }
1.104 +
1.105 + @Override
1.106 + public void writeStartResultSet(ColumnsHeader header) {
1.107 + super.writeStartResultSet(header);
1.108 + printCommand("begin", null, "tabular", false);
1.109 +
1.110 + List<ColumnDescriptor> columnDescriptors = header.getColumnDescriptors();
1.111 +
1.112 + StringBuilder columnAlignments = new StringBuilder();
1.113 + for (ColumnDescriptor cd : columnDescriptors) {
1.114 + if (cd.isNumeric() || cd.isBoolean()) {
1.115 + columnAlignments.append('r');
1.116 + } else {
1.117 + columnAlignments.append('l');
1.118 + }
1.119 + }
1.120 +
1.121 + printCommand(null, null, columnAlignments.toString(), true);
1.122 + printCommand("hline", null, null, true);
1.123 +
1.124 + for (ColumnDescriptor cd : columnDescriptors) {
1.125 + printCommand("textbf", null, cd.getLabel(), false);
1.126 + if (cd.isLastColumn()) {
1.127 + printEndRow();
1.128 + } else {
1.129 + printColumnSeparator();
1.130 + }
1.131 + }
1.132 +
1.133 + printCommand("hline", null, null, true);
1.134 + }
1.135 +
1.136 + @Override
1.137 + public void writeEndResultSet() {
1.138 + super.writeEndResultSet();
1.139 + printCommand("hline", null, null, true);
1.140 + printEnd("tabular");
1.141 + }
1.142 +
1.143 + private String escapeTex(String text) {
1.144 + if (text == null) {
1.145 + return null;
1.146 + } else {
1.147 + StringBuilder result = new StringBuilder(text.length() * 2);
1.148 +
1.149 + for (char ch : text.toCharArray()) {
1.150 + String replacement = TEX_ESCAPE_MAP.get(ch);
1.151 + result.append(replacement == null ? ch : replacement);
1.152 + }
1.153 +
1.154 + return result.toString();
1.155 + }
1.156 + }
1.157 +
1.158 + protected String toString(Object value) {
1.159 + return String.valueOf(value);
1.160 + }
1.161 +
1.162 + private void printColumnSeparator() {
1.163 + out.print(COMMAND_COLOR, " & ");
1.164 + }
1.165 +
1.166 + private void printEndRow() {
1.167 + out.println(COMMAND_COLOR, " \\\\");
1.168 + out.flush();
1.169 + }
1.170 +
1.171 + /**
1.172 + *
1.173 + * @param command will not be escaped – should contain just a valid TeX command name
1.174 + * @param options will not be escaped – should be properly formatted to be printed inside [
1.175 + * and ]
1.176 + * @param value will be escaped
1.177 + * @param println whether to print line end and flush
1.178 + */
1.179 + private void printCommand(String command, String options, String value, boolean println) {
1.180 +
1.181 + if (command != null) {
1.182 + out.print(COMMAND_COLOR, "\\" + command);
1.183 + }
1.184 +
1.185 + if (options != null) {
1.186 + out.print(COMMAND_COLOR, "[");
1.187 + out.print(OPTIONS_COLOR, options);
1.188 + out.print(COMMAND_COLOR, "]");
1.189 + }
1.190 +
1.191 + if (value != null) {
1.192 + out.print(COMMAND_COLOR, "{");
1.193 + out.print(escapeTex(value));
1.194 + out.print(COMMAND_COLOR, "}");
1.195 + }
1.196 +
1.197 + if (println) {
1.198 + out.println();
1.199 + out.flush();
1.200 + }
1.201 + }
1.202 +
1.203 + private void printBegin(String environment) {
1.204 + printCommand("begin", null, environment, true);
1.205 + }
1.206 +
1.207 + private void printEnd(String environment) {
1.208 + printCommand("end", null, environment, true);
1.209 + }
1.210 +
1.211 +}