|
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 java.util.Collections; |
|
23 import java.util.HashMap; |
|
24 import java.util.List; |
|
25 import java.util.Map; |
|
26 |
|
27 /** |
|
28 * Outputs result sets in (La)TeX format. |
|
29 * |
|
30 * @author Ing. František Kučera (frantovo.cz) |
|
31 */ |
|
32 public class TeXFormatter extends AbstractFormatter { |
|
33 |
|
34 public static final String NAME = "tex"; // bash-completion:formatter |
|
35 public static final String PROPERTY_COLORFUL = "color"; |
|
36 private static final ColorfulPrintWriter.TerminalColor COMMAND_COLOR = ColorfulPrintWriter.TerminalColor.Magenta; |
|
37 private static final ColorfulPrintWriter.TerminalColor OPTIONS_COLOR = ColorfulPrintWriter.TerminalColor.Yellow; |
|
38 private static final Map<Character, String> TEX_ESCAPE_MAP; |
|
39 private final ColorfulPrintWriter out; |
|
40 |
|
41 static { |
|
42 Map<Character, String> replacements = new HashMap<>(); |
|
43 |
|
44 replacements.put('\\', "\\textbackslash{}"); |
|
45 replacements.put('{', "\\{{}"); |
|
46 replacements.put('}', "\\}{}"); |
|
47 replacements.put('_', "\\_{}"); |
|
48 replacements.put('^', "\\textasciicircum{}"); |
|
49 replacements.put('#', "\\#{}"); |
|
50 replacements.put('&', "\\&{}"); |
|
51 replacements.put('$', "\\${}"); |
|
52 replacements.put('%', "\\%{}"); |
|
53 replacements.put('~', "\\textasciitilde{}"); |
|
54 replacements.put('-', "{-}"); |
|
55 |
|
56 TEX_ESCAPE_MAP = Collections.unmodifiableMap(replacements); |
|
57 } |
|
58 |
|
59 public TeXFormatter(FormatterContext formatterContext) { |
|
60 super(formatterContext); |
|
61 boolean colorful = formatterContext.getProperties().getBoolean(PROPERTY_COLORFUL, false); |
|
62 out = new ColorfulPrintWriter(formatterContext.getOutputStream(), false, colorful); |
|
63 } |
|
64 |
|
65 @Override |
|
66 public void writeStartBatch() { |
|
67 super.writeStartBatch(); |
|
68 |
|
69 printCommand("documentclass", "a4paper,twoside", "article", true); |
|
70 printCommand("usepackage", "T1", "fontenc", true); |
|
71 printCommand("usepackage", "utf8x", "inputenc", true); |
|
72 printCommand("usepackage", "pdfauthor={" + Constants.WEBSITE + "}, bookmarks=true,unicode,colorlinks=true,linkcolor=black,urlcolor=blue,citecolor=blue", "hyperref", true); |
|
73 printBegin("document"); |
|
74 } |
|
75 |
|
76 @Override |
|
77 public void writeEndBatch() { |
|
78 super.writeEndBatch(); |
|
79 printEnd("document"); |
|
80 } |
|
81 |
|
82 @Override |
|
83 public void writeColumnValue(Object value) { |
|
84 super.writeColumnValue(value); |
|
85 // TODO: arrays, numbers, booleans, nulls etc.: |
|
86 out.print(escapeTex(toString(value))); |
|
87 |
|
88 if (!isCurrentColumnLast()) { |
|
89 printColumnSeparator(); |
|
90 } |
|
91 } |
|
92 |
|
93 @Override |
|
94 public void writeEndRow() { |
|
95 super.writeEndRow(); |
|
96 printEndRow(); |
|
97 } |
|
98 |
|
99 @Override |
|
100 public void writeStartResultSet(ColumnsHeader header) { |
|
101 super.writeStartResultSet(header); |
|
102 printCommand("begin", null, "tabular", false); |
|
103 |
|
104 List<ColumnDescriptor> columnDescriptors = header.getColumnDescriptors(); |
|
105 |
|
106 StringBuilder columnAlignments = new StringBuilder(); |
|
107 for (ColumnDescriptor cd : columnDescriptors) { |
|
108 if (cd.isNumeric() || cd.isBoolean()) { |
|
109 columnAlignments.append('r'); |
|
110 } else { |
|
111 columnAlignments.append('l'); |
|
112 } |
|
113 } |
|
114 |
|
115 printCommand(null, null, columnAlignments.toString(), true); |
|
116 printCommand("hline", null, null, true); |
|
117 |
|
118 for (ColumnDescriptor cd : columnDescriptors) { |
|
119 printCommand("textbf", null, cd.getLabel(), false); |
|
120 if (cd.isLastColumn()) { |
|
121 printEndRow(); |
|
122 } else { |
|
123 printColumnSeparator(); |
|
124 } |
|
125 } |
|
126 |
|
127 printCommand("hline", null, null, true); |
|
128 } |
|
129 |
|
130 @Override |
|
131 public void writeEndResultSet() { |
|
132 super.writeEndResultSet(); |
|
133 printCommand("hline", null, null, true); |
|
134 printEnd("tabular"); |
|
135 } |
|
136 |
|
137 private String escapeTex(String text) { |
|
138 if (text == null) { |
|
139 return null; |
|
140 } else { |
|
141 StringBuilder result = new StringBuilder(text.length() * 2); |
|
142 |
|
143 for (char ch : text.toCharArray()) { |
|
144 String replacement = TEX_ESCAPE_MAP.get(ch); |
|
145 result.append(replacement == null ? ch : replacement); |
|
146 } |
|
147 |
|
148 return result.toString(); |
|
149 } |
|
150 } |
|
151 |
|
152 protected String toString(Object value) { |
|
153 return String.valueOf(value); |
|
154 } |
|
155 |
|
156 private void printColumnSeparator() { |
|
157 out.print(COMMAND_COLOR, " & "); |
|
158 } |
|
159 |
|
160 private void printEndRow() { |
|
161 out.println(COMMAND_COLOR, " \\\\"); |
|
162 out.flush(); |
|
163 } |
|
164 |
|
165 /** |
|
166 * |
|
167 * @param command will not be escaped – should contain just a valid TeX command name |
|
168 * @param options will not be escaped – should be properly formatted to be printed inside [ |
|
169 * and ] |
|
170 * @param value will be escaped |
|
171 * @param println whether to print line end and flush |
|
172 */ |
|
173 private void printCommand(String command, String options, String value, boolean println) { |
|
174 |
|
175 if (command != null) { |
|
176 out.print(COMMAND_COLOR, "\\" + command); |
|
177 } |
|
178 |
|
179 if (options != null) { |
|
180 out.print(COMMAND_COLOR, "["); |
|
181 out.print(OPTIONS_COLOR, options); |
|
182 out.print(COMMAND_COLOR, "]"); |
|
183 } |
|
184 |
|
185 if (value != null) { |
|
186 out.print(COMMAND_COLOR, "{"); |
|
187 out.print(escapeTex(value)); |
|
188 out.print(COMMAND_COLOR, "}"); |
|
189 } |
|
190 |
|
191 if (println) { |
|
192 out.println(); |
|
193 out.flush(); |
|
194 } |
|
195 } |
|
196 |
|
197 private void printBegin(String environment) { |
|
198 printCommand("begin", null, environment, true); |
|
199 } |
|
200 |
|
201 private void printEnd(String environment) { |
|
202 printCommand("end", null, environment, true); |
|
203 } |
|
204 |
|
205 } |