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/XhtmlFormatter.java Mon Mar 04 20:15:24 2019 +0100
1.3 @@ -0,0 +1,262 @@
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.Constants;
1.24 +import info.globalcode.sql.dk.NamedParameter;
1.25 +import info.globalcode.sql.dk.Parameter;
1.26 +import info.globalcode.sql.dk.Xmlns;
1.27 +import info.globalcode.sql.dk.configuration.DatabaseDefinition;
1.28 +import info.globalcode.sql.dk.configuration.Properties;
1.29 +import info.globalcode.sql.dk.configuration.Property;
1.30 +import static info.globalcode.sql.dk.formatting.AbstractXmlFormatter.qname;
1.31 +import java.sql.Array;
1.32 +import java.sql.SQLException;
1.33 +import java.util.Date;
1.34 +import java.util.List;
1.35 +import java.util.Map;
1.36 +import java.util.Scanner;
1.37 +import java.util.logging.Level;
1.38 +import java.util.logging.Logger;
1.39 +import javax.xml.namespace.QName;
1.40 +
1.41 +/**
1.42 + * Prints result sets and parameters as tables, SQL as preformatted and updates counts as
1.43 + * paragraphs. You can pick XHTML fragments (usually tabular data) and use it on your website or use
1.44 + * whole output as preview or report.
1.45 + *
1.46 + * @author Ing. František Kučera (frantovo.cz)
1.47 + */
1.48 +public class XhtmlFormatter extends AbstractXmlFormatter {
1.49 +
1.50 + private static final Logger log = Logger.getLogger(XhtmlFormatter.class.getName());
1.51 + public static final String NAME = "xhtml"; // bash-completion:formatter
1.52 + private static final String DOCTYPE = "html PUBLIC \"-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN\" \"http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd\"";
1.53 + private static final String CSS_FILE = "info/globalcode/sql/dk/formatter/XhtmlFormatter.css";
1.54 + private int statementCounter = 0;
1.55 + private int resultSetCounter = 0;
1.56 + private int updatesResultCounter = 0;
1.57 +
1.58 + public XhtmlFormatter(FormatterContext formatterContext) {
1.59 + super(addDefaults(formatterContext));
1.60 + }
1.61 +
1.62 + /**
1.63 + * Do not indent text – preserve whitespace for pre elements
1.64 + */
1.65 + private static FormatterContext addDefaults(FormatterContext formatterContext) {
1.66 + Properties defaults = new Properties(1);
1.67 + defaults.add(new Property(PROPERTY_INDENT_TEXT, "false"));
1.68 + formatterContext.getProperties().setLastDefaults(defaults);
1.69 + return formatterContext;
1.70 + }
1.71 +
1.72 + @Override
1.73 + public void writeStartBatch() {
1.74 + super.writeStartBatch();
1.75 + printStartDocument();
1.76 + printDoctype(DOCTYPE);
1.77 + printStartElement(qname("html"), singleAttribute(qname("xmlns"), Xmlns.XHTML));
1.78 +
1.79 + printStartElement(qname("head"));
1.80 + printTextElement(qname("title"), null, Constants.PROGRAM_NAME + ": batch results");
1.81 + printCss();
1.82 + printEndElement();
1.83 +
1.84 + printStartElement(qname("body"));
1.85 + }
1.86 +
1.87 + private void printCss() {
1.88 +
1.89 + try (Scanner css = new Scanner(getClass().getClassLoader().getResourceAsStream(CSS_FILE))) {
1.90 + printStartElement(qname("style"), singleAttribute(qname("type"), "text/css"));
1.91 + while (css.hasNext()) {
1.92 + printText(css.nextLine(), true);
1.93 + }
1.94 + printEndElement();
1.95 + }
1.96 + }
1.97 +
1.98 + @Override
1.99 + public void writeEndBatch() {
1.100 + super.writeEndBatch();
1.101 + printEndElement();
1.102 + printEndElement();
1.103 + printEndDocument();
1.104 + }
1.105 +
1.106 + @Override
1.107 + public void writeStartDatabase(DatabaseDefinition databaseDefinition) {
1.108 + super.writeStartDatabase(databaseDefinition);
1.109 + printTextElement(qname("h1"), null, "Database: " + databaseDefinition.getName());
1.110 +
1.111 + printStartElement(qname("p"));
1.112 + printText("This is XHTML output of batch executed at: ", true);
1.113 + printText(new Date().toString(), true);
1.114 + printEndElement();
1.115 + }
1.116 +
1.117 + @Override
1.118 + public void writeQuery(String sql) {
1.119 + super.writeQuery(sql);
1.120 + printTextElement(qname("pre"), null, sql);
1.121 + }
1.122 +
1.123 + @Override
1.124 + public void writeParameters(List<? extends Parameter> parameters) {
1.125 + super.writeParameters(parameters);
1.126 +
1.127 + if (parameters == null || parameters.isEmpty()) {
1.128 + printTextElement(qname("p"), null, "(this query has no parameters)");
1.129 + } else {
1.130 + printTextElement(qname("h3"), null, "Parameters:");
1.131 +
1.132 + printStartElement(qname("table"));
1.133 +
1.134 + printStartElement(qname("thead"));
1.135 + printStartElement(qname("tr"));
1.136 + printTextElement(qname("td"), null, "id");
1.137 + printTextElement(qname("td"), null, "type");
1.138 + printTextElement(qname("td"), null, "value");
1.139 + printEndElement();
1.140 + printEndElement();
1.141 +
1.142 + printStartElement(qname("tbody"));
1.143 + for (int i = 0; i < parameters.size(); i++) {
1.144 + Parameter p = parameters.get(i);
1.145 + printStartElement(qname("tr"));
1.146 + String numberOrName;
1.147 + if (p instanceof NamedParameter) {
1.148 + numberOrName = ((NamedParameter) p).getName();
1.149 + } else {
1.150 + numberOrName = String.valueOf(i + 1);
1.151 + }
1.152 + printTextElement(qname("td"), null, numberOrName);
1.153 + printTextElement(qname("td"), null, p.getType().name());
1.154 + printTableData(p.getValue());
1.155 + printEndElement();
1.156 + }
1.157 + printEndElement();
1.158 +
1.159 + printEndElement();
1.160 + }
1.161 + }
1.162 +
1.163 + private void printTableData(Object value) {
1.164 +
1.165 + if (value instanceof Array) {
1.166 + Array sqlArray = (Array) value;
1.167 + try {
1.168 + Object[] array = (Object[]) sqlArray.getArray();
1.169 + printStartElement(qname("td"));
1.170 + printArray(array);
1.171 + printEndElement();
1.172 + } catch (SQLException e) {
1.173 + log.log(Level.SEVERE, "Unable to format array", e);
1.174 + printTableData(String.valueOf(value));
1.175 + }
1.176 + } else {
1.177 + Map<QName, String> attributes = null;
1.178 + if (value instanceof Number) {
1.179 + attributes = singleAttribute(qname("class"), "number");
1.180 + } else if (value instanceof Boolean) {
1.181 + attributes = singleAttribute(qname("class"), "boolean");
1.182 + }
1.183 + printTextElement(qname("td"), attributes, String.valueOf(value));
1.184 + }
1.185 + }
1.186 +
1.187 + private void printArray(Object[] array) {
1.188 + printStartElement(qname("ul"));
1.189 + for (Object o : array) {
1.190 + if (o instanceof Object[]) {
1.191 + printStartElement(qname("li"));
1.192 + printTextElement(qname("p"), null, "nested array:");
1.193 + printArray((Object[]) o);
1.194 + printEndElement();
1.195 + } else {
1.196 + printTextElement(qname("li"), null, String.valueOf(o));
1.197 + }
1.198 + }
1.199 + printEndElement();
1.200 + }
1.201 +
1.202 + @Override
1.203 + public void writeStartResultSet(ColumnsHeader header) {
1.204 + super.writeStartResultSet(header);
1.205 + resultSetCounter++;
1.206 + printEmptyElement(qname("hr"), null);
1.207 + printTextElement(qname("h3"), null, "Result set #" + resultSetCounter);
1.208 + printStartElement(qname("table"));
1.209 + printStartElement(qname("thead"));
1.210 + printStartElement(qname("tr"));
1.211 + for (ColumnDescriptor cd : header.getColumnDescriptors()) {
1.212 + // TODO: type
1.213 + printTextElement(qname("td"), null, cd.getLabel());
1.214 + }
1.215 + printEndElement();
1.216 + printEndElement();
1.217 +
1.218 + printStartElement(qname("tbody"));
1.219 + }
1.220 +
1.221 + @Override
1.222 + public void writeEndResultSet() {
1.223 + super.writeEndResultSet();
1.224 + printEndElement();
1.225 + printEndElement();
1.226 + printTextElement(qname("p"), null, "Record count: " + getCurrentRowCount());
1.227 + }
1.228 +
1.229 + @Override
1.230 + public void writeStartRow() {
1.231 + super.writeStartRow();
1.232 + printStartElement(qname("tr"));
1.233 + }
1.234 +
1.235 + @Override
1.236 + public void writeColumnValue(Object value) {
1.237 + super.writeColumnValue(value);
1.238 + printTableData(value);
1.239 + }
1.240 +
1.241 + @Override
1.242 + public void writeEndRow() {
1.243 + super.writeEndRow();
1.244 + printEndElement();
1.245 + }
1.246 +
1.247 + @Override
1.248 + public void writeStartStatement() {
1.249 + super.writeStartStatement();
1.250 + statementCounter++;
1.251 + printEmptyElement(qname("hr"), null);
1.252 + printTextElement(qname("h2"), null, "SQL statement #" + statementCounter);
1.253 + resultSetCounter = 0;
1.254 + updatesResultCounter = 0;
1.255 + }
1.256 +
1.257 + @Override
1.258 + public void writeUpdatesResult(int updatedRowsCount) {
1.259 + super.writeUpdatesResult(updatedRowsCount);
1.260 + updatesResultCounter++;
1.261 + printEmptyElement(qname("hr"), null);
1.262 + printTextElement(qname("h3"), null, "Updates result #" + updatesResultCounter);
1.263 + printTextElement(qname("p"), null, "Updated rows: " + updatedRowsCount);
1.264 + }
1.265 +}