|
1 /** |
|
2 * SQL-DK |
|
3 * Copyright © 2014 František Kučera (frantovo.cz) |
|
4 * |
|
5 * This program is free software: you can redistribute it and/or modify |
|
6 * it under the terms of the GNU General Public License as published by |
|
7 * the Free Software Foundation, either version 3 of the License, or |
|
8 * (at your option) any later version. |
|
9 * |
|
10 * This program is distributed in the hope that it will be useful, |
|
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
13 * GNU General Public License for more details. |
|
14 * |
|
15 * You should have received a copy of the GNU General Public License |
|
16 * along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
17 */ |
|
18 package info.globalcode.sql.dk.formatting; |
|
19 |
|
20 import info.globalcode.sql.dk.ColorfulPrintWriter; |
|
21 import info.globalcode.sql.dk.Constants; |
|
22 import info.globalcode.sql.dk.configuration.PropertyDeclaration; |
|
23 import static info.globalcode.sql.dk.formatting.CommonProperties.COLORFUL; |
|
24 import static info.globalcode.sql.dk.formatting.CommonProperties.COLORFUL_DESCRIPTION; |
|
25 import java.util.Collections; |
|
26 import java.util.HashMap; |
|
27 import java.util.List; |
|
28 import java.util.Map; |
|
29 |
|
30 /** |
|
31 * Outputs result sets in (La)TeX format. |
|
32 * |
|
33 * @author Ing. František Kučera (frantovo.cz) |
|
34 */ |
|
35 @PropertyDeclaration(name = COLORFUL, defaultValue = "false", type = Boolean.class, description = COLORFUL_DESCRIPTION) |
|
36 public class TeXFormatter extends AbstractFormatter { |
|
37 |
|
38 public static final String NAME = "tex"; // bash-completion:formatter |
|
39 private static final ColorfulPrintWriter.TerminalColor COMMAND_COLOR = ColorfulPrintWriter.TerminalColor.Magenta; |
|
40 private static final ColorfulPrintWriter.TerminalColor OPTIONS_COLOR = ColorfulPrintWriter.TerminalColor.Yellow; |
|
41 private static final Map<Character, String> TEX_ESCAPE_MAP; |
|
42 private final ColorfulPrintWriter out; |
|
43 |
|
44 static { |
|
45 Map<Character, String> replacements = new HashMap<>(); |
|
46 |
|
47 replacements.put('\\', "\\textbackslash{}"); |
|
48 replacements.put('{', "\\{{}"); |
|
49 replacements.put('}', "\\}{}"); |
|
50 replacements.put('_', "\\_{}"); |
|
51 replacements.put('^', "\\textasciicircum{}"); |
|
52 replacements.put('#', "\\#{}"); |
|
53 replacements.put('&', "\\&{}"); |
|
54 replacements.put('$', "\\${}"); |
|
55 replacements.put('%', "\\%{}"); |
|
56 replacements.put('~', "\\textasciitilde{}"); |
|
57 replacements.put('-', "{-}"); |
|
58 |
|
59 TEX_ESCAPE_MAP = Collections.unmodifiableMap(replacements); |
|
60 } |
|
61 |
|
62 public TeXFormatter(FormatterContext formatterContext) { |
|
63 super(formatterContext); |
|
64 boolean colorful = formatterContext.getProperties().getBoolean(COLORFUL, false); |
|
65 out = new ColorfulPrintWriter(formatterContext.getOutputStream(), false, colorful); |
|
66 } |
|
67 |
|
68 @Override |
|
69 public void writeStartBatch() { |
|
70 super.writeStartBatch(); |
|
71 |
|
72 printCommand("documentclass", "a4paper,twoside", "article", true); |
|
73 printCommand("usepackage", "T1", "fontenc", true); |
|
74 printCommand("usepackage", "utf8x", "inputenc", true); |
|
75 printCommand("usepackage", "pdfauthor={" + Constants.WEBSITE + "}, bookmarks=true,unicode,colorlinks=true,linkcolor=black,urlcolor=blue,citecolor=blue", "hyperref", true); |
|
76 printBegin("document"); |
|
77 } |
|
78 |
|
79 @Override |
|
80 public void writeEndBatch() { |
|
81 super.writeEndBatch(); |
|
82 printEnd("document"); |
|
83 } |
|
84 |
|
85 @Override |
|
86 public void writeColumnValue(Object value) { |
|
87 super.writeColumnValue(value); |
|
88 // TODO: arrays, numbers, booleans, nulls etc.: |
|
89 out.print(escapeTex(toString(value))); |
|
90 |
|
91 if (!isCurrentColumnLast()) { |
|
92 printColumnSeparator(); |
|
93 } |
|
94 } |
|
95 |
|
96 @Override |
|
97 public void writeEndRow() { |
|
98 super.writeEndRow(); |
|
99 printEndRow(); |
|
100 } |
|
101 |
|
102 @Override |
|
103 public void writeStartResultSet(ColumnsHeader header) { |
|
104 super.writeStartResultSet(header); |
|
105 printCommand("begin", null, "tabular", false); |
|
106 |
|
107 List<ColumnDescriptor> columnDescriptors = header.getColumnDescriptors(); |
|
108 |
|
109 StringBuilder columnAlignments = new StringBuilder(); |
|
110 for (ColumnDescriptor cd : columnDescriptors) { |
|
111 if (cd.isNumeric() || cd.isBoolean()) { |
|
112 columnAlignments.append('r'); |
|
113 } else { |
|
114 columnAlignments.append('l'); |
|
115 } |
|
116 } |
|
117 |
|
118 printCommand(null, null, columnAlignments.toString(), true); |
|
119 printCommand("hline", null, null, true); |
|
120 |
|
121 for (ColumnDescriptor cd : columnDescriptors) { |
|
122 printCommand("textbf", null, cd.getLabel(), false); |
|
123 if (cd.isLastColumn()) { |
|
124 printEndRow(); |
|
125 } else { |
|
126 printColumnSeparator(); |
|
127 } |
|
128 } |
|
129 |
|
130 printCommand("hline", null, null, true); |
|
131 } |
|
132 |
|
133 @Override |
|
134 public void writeEndResultSet() { |
|
135 super.writeEndResultSet(); |
|
136 printCommand("hline", null, null, true); |
|
137 printEnd("tabular"); |
|
138 } |
|
139 |
|
140 private String escapeTex(String text) { |
|
141 if (text == null) { |
|
142 return null; |
|
143 } else { |
|
144 StringBuilder result = new StringBuilder(text.length() * 2); |
|
145 |
|
146 for (char ch : text.toCharArray()) { |
|
147 String replacement = TEX_ESCAPE_MAP.get(ch); |
|
148 result.append(replacement == null ? ch : replacement); |
|
149 } |
|
150 |
|
151 return result.toString(); |
|
152 } |
|
153 } |
|
154 |
|
155 protected String toString(Object value) { |
|
156 return String.valueOf(value); |
|
157 } |
|
158 |
|
159 private void printColumnSeparator() { |
|
160 out.print(COMMAND_COLOR, " & "); |
|
161 } |
|
162 |
|
163 private void printEndRow() { |
|
164 out.println(COMMAND_COLOR, " \\\\"); |
|
165 out.flush(); |
|
166 } |
|
167 |
|
168 /** |
|
169 * |
|
170 * @param command will not be escaped – should contain just a valid TeX command name |
|
171 * @param options will not be escaped – should be properly formatted to be printed inside [ |
|
172 * and ] |
|
173 * @param value will be escaped |
|
174 * @param println whether to print line end and flush |
|
175 */ |
|
176 private void printCommand(String command, String options, String value, boolean println) { |
|
177 |
|
178 if (command != null) { |
|
179 out.print(COMMAND_COLOR, "\\" + command); |
|
180 } |
|
181 |
|
182 if (options != null) { |
|
183 out.print(COMMAND_COLOR, "["); |
|
184 out.print(OPTIONS_COLOR, options); |
|
185 out.print(COMMAND_COLOR, "]"); |
|
186 } |
|
187 |
|
188 if (value != null) { |
|
189 out.print(COMMAND_COLOR, "{"); |
|
190 out.print(escapeTex(value)); |
|
191 out.print(COMMAND_COLOR, "}"); |
|
192 } |
|
193 |
|
194 if (println) { |
|
195 out.println(); |
|
196 out.flush(); |
|
197 } |
|
198 } |
|
199 |
|
200 private void printBegin(String environment) { |
|
201 printCommand("begin", null, environment, true); |
|
202 } |
|
203 |
|
204 private void printEnd(String environment) { |
|
205 printCommand("end", null, environment, true); |
|
206 } |
|
207 |
|
208 } |