franta-hg@128: /** franta-hg@128: * SQL-DK franta-hg@128: * Copyright © 2014 František Kučera (frantovo.cz) franta-hg@128: * franta-hg@128: * This program is free software: you can redistribute it and/or modify franta-hg@128: * it under the terms of the GNU General Public License as published by franta-hg@128: * the Free Software Foundation, either version 3 of the License, or franta-hg@128: * (at your option) any later version. franta-hg@128: * franta-hg@128: * This program is distributed in the hope that it will be useful, franta-hg@128: * but WITHOUT ANY WARRANTY; without even the implied warranty of franta-hg@128: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the franta-hg@128: * GNU General Public License for more details. franta-hg@128: * franta-hg@128: * You should have received a copy of the GNU General Public License franta-hg@128: * along with this program. If not, see . franta-hg@128: */ franta-hg@128: package info.globalcode.sql.dk.formatting; franta-hg@128: franta-hg@134: import info.globalcode.sql.dk.Constants; franta-hg@134: import info.globalcode.sql.dk.NamedParameter; franta-hg@134: import info.globalcode.sql.dk.Parameter; franta-hg@134: import info.globalcode.sql.dk.Xmlns; franta-hg@134: import info.globalcode.sql.dk.configuration.DatabaseDefinition; franta-hg@134: import info.globalcode.sql.dk.configuration.Properties; franta-hg@134: import info.globalcode.sql.dk.configuration.Property; franta-hg@134: import static info.globalcode.sql.dk.formatting.AbstractXmlFormatter.qname; franta-hg@138: import java.sql.Array; franta-hg@138: import java.sql.SQLException; franta-hg@135: import java.util.Date; franta-hg@134: import java.util.List; franta-hg@134: import java.util.Map; franta-hg@135: import java.util.Scanner; franta-hg@138: import java.util.logging.Level; franta-hg@138: import java.util.logging.Logger; franta-hg@134: import javax.xml.namespace.QName; franta-hg@134: franta-hg@128: /** franta-hg@128: * franta-hg@128: * @author Ing. František Kučera (frantovo.cz) franta-hg@128: */ franta-hg@128: public class XhtmlFormatter extends AbstractXmlFormatter { franta-hg@128: franta-hg@138: private static final Logger log = Logger.getLogger(XhtmlFormatter.class.getName()); franta-hg@128: public static final String NAME = "xhtml"; // bash-completion:formatter franta-hg@136: 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\""; franta-hg@135: private static final String CSS_FILE = "info/globalcode/sql/dk/formatter/XhtmlFormatter.css"; franta-hg@142: private int statementCounter = 0; franta-hg@134: private int resultSetCounter = 0; franta-hg@134: private int updatesResultCounter = 0; franta-hg@128: franta-hg@128: public XhtmlFormatter(FormatterContext formatterContext) { franta-hg@134: super(addDefaults(formatterContext)); franta-hg@134: } franta-hg@134: franta-hg@134: /** franta-hg@134: * Do not indent text – preserve whitespace for pre elements franta-hg@134: */ franta-hg@134: private static FormatterContext addDefaults(FormatterContext formatterContext) { franta-hg@134: Properties defaults = new Properties(1); franta-hg@134: defaults.add(new Property(PROPERTY_INDENT_TEXT, "false")); franta-hg@134: formatterContext.getProperties().setLastDefaults(defaults); franta-hg@134: return formatterContext; franta-hg@134: } franta-hg@134: franta-hg@134: @Override franta-hg@134: public void writeStartBatch() { franta-hg@134: super.writeStartBatch(); franta-hg@134: printStartDocument(); franta-hg@136: printDoctype(DOCTYPE); franta-hg@152: printStartElement(qname("html"), singleAttribute(qname("xmlns"), Xmlns.XHTML)); franta-hg@134: franta-hg@134: printStartElement(qname("head")); franta-hg@134: printTextElement(qname("title"), null, Constants.PROGRAM_NAME + ": batch results"); franta-hg@135: printCss(); franta-hg@134: printEndElement(); franta-hg@134: franta-hg@134: printStartElement(qname("body")); franta-hg@134: } franta-hg@134: franta-hg@135: private void printCss() { franta-hg@135: franta-hg@135: try (Scanner css = new Scanner(getClass().getClassLoader().getResourceAsStream(CSS_FILE))) { franta-hg@152: printStartElement(qname("style"), singleAttribute(qname("type"), "text/css")); franta-hg@135: while (css.hasNext()) { franta-hg@135: printText(css.nextLine(), true); franta-hg@135: } franta-hg@135: printEndElement(); franta-hg@135: } franta-hg@135: } franta-hg@135: franta-hg@134: @Override franta-hg@134: public void writeEndBatch() { franta-hg@134: super.writeEndBatch(); franta-hg@134: printEndElement(); franta-hg@134: printEndElement(); franta-hg@134: printEndDocument(); franta-hg@134: } franta-hg@134: franta-hg@134: @Override franta-hg@134: public void writeStartDatabase(DatabaseDefinition databaseDefinition) { franta-hg@134: super.writeStartDatabase(databaseDefinition); franta-hg@134: printTextElement(qname("h1"), null, "Database: " + databaseDefinition.getName()); franta-hg@135: franta-hg@135: printStartElement(qname("p")); franta-hg@135: printText("This is XHTML output of batch executed at: ", true); franta-hg@135: printText(new Date().toString(), true); franta-hg@135: printEndElement(); franta-hg@134: } franta-hg@134: franta-hg@134: @Override franta-hg@134: public void writeQuery(String sql) { franta-hg@134: super.writeQuery(sql); franta-hg@134: printTextElement(qname("pre"), null, sql); franta-hg@134: } franta-hg@134: franta-hg@134: @Override franta-hg@134: public void writeParameters(List parameters) { franta-hg@134: super.writeParameters(parameters); franta-hg@134: franta-hg@134: if (parameters == null || parameters.isEmpty()) { franta-hg@134: printTextElement(qname("p"), null, "(this query has no parameters)"); franta-hg@134: } else { franta-hg@134: printTextElement(qname("h3"), null, "Parameters:"); franta-hg@134: franta-hg@134: printStartElement(qname("table")); franta-hg@134: franta-hg@135: printStartElement(qname("thead")); franta-hg@134: printStartElement(qname("tr")); franta-hg@135: printTextElement(qname("td"), null, "id"); franta-hg@134: printTextElement(qname("td"), null, "type"); franta-hg@134: printTextElement(qname("td"), null, "value"); franta-hg@134: printEndElement(); franta-hg@134: printEndElement(); franta-hg@134: franta-hg@134: printStartElement(qname("tbody")); franta-hg@134: for (int i = 0; i < parameters.size(); i++) { franta-hg@134: Parameter p = parameters.get(i); franta-hg@134: printStartElement(qname("tr")); franta-hg@134: String numberOrName; franta-hg@134: if (p instanceof NamedParameter) { franta-hg@134: numberOrName = ((NamedParameter) p).getName(); franta-hg@134: } else { franta-hg@134: numberOrName = String.valueOf(i + 1); franta-hg@134: } franta-hg@134: printTextElement(qname("td"), null, numberOrName); franta-hg@134: printTextElement(qname("td"), null, p.getType().name()); franta-hg@135: printTableData(p.getValue()); franta-hg@134: printEndElement(); franta-hg@134: } franta-hg@134: printEndElement(); franta-hg@134: franta-hg@134: printEndElement(); franta-hg@134: } franta-hg@134: } franta-hg@134: franta-hg@135: private void printTableData(Object value) { franta-hg@138: franta-hg@138: if (value instanceof Array) { franta-hg@138: Array sqlArray = (Array) value; franta-hg@138: try { franta-hg@138: Object[] array = (Object[]) sqlArray.getArray(); franta-hg@138: printStartElement(qname("td")); franta-hg@138: printArray(array); franta-hg@138: printEndElement(); franta-hg@138: } catch (SQLException e) { franta-hg@138: log.log(Level.SEVERE, "Unable to format array", e); franta-hg@138: printTableData(String.valueOf(value)); franta-hg@138: } franta-hg@138: } else { franta-hg@152: Map attributes = null; franta-hg@138: if (value instanceof Number) { franta-hg@152: attributes = singleAttribute(qname("class"), "number"); franta-hg@138: } else if (value instanceof Boolean) { franta-hg@152: attributes = singleAttribute(qname("class"), "boolean"); franta-hg@138: } franta-hg@138: printTextElement(qname("td"), attributes, String.valueOf(value)); franta-hg@135: } franta-hg@138: } franta-hg@138: franta-hg@138: private void printArray(Object[] array) { franta-hg@138: printStartElement(qname("ul")); franta-hg@138: for (Object o : array) { franta-hg@138: if (o instanceof Object[]) { franta-hg@138: printStartElement(qname("li")); franta-hg@138: printTextElement(qname("p"), null, "nested array:"); franta-hg@138: printArray((Object[]) o); franta-hg@138: printEndElement(); franta-hg@138: } else { franta-hg@138: printTextElement(qname("li"), null, String.valueOf(o)); franta-hg@138: } franta-hg@138: } franta-hg@138: printEndElement(); franta-hg@135: } franta-hg@135: franta-hg@134: @Override franta-hg@142: public void writeStartResultSet(ColumnsHeader header) { franta-hg@142: super.writeStartResultSet(header); franta-hg@142: resultSetCounter++; franta-hg@142: printEmptyElement(qname("hr"), null); franta-hg@142: printTextElement(qname("h3"), null, "Result set #" + resultSetCounter); franta-hg@134: printStartElement(qname("table")); franta-hg@135: printStartElement(qname("thead")); franta-hg@134: printStartElement(qname("tr")); franta-hg@134: for (ColumnDescriptor cd : header.getColumnDescriptors()) { franta-hg@134: // TODO: type franta-hg@134: printTextElement(qname("td"), null, cd.getLabel()); franta-hg@134: } franta-hg@134: printEndElement(); franta-hg@134: printEndElement(); franta-hg@134: franta-hg@134: printStartElement(qname("tbody")); franta-hg@134: } franta-hg@134: franta-hg@134: @Override franta-hg@142: public void writeEndResultSet() { franta-hg@142: super.writeEndResultSet(); franta-hg@142: printEndElement(); franta-hg@142: printEndElement(); franta-hg@142: printTextElement(qname("p"), null, "Record count: " + getCurrentRowCount()); franta-hg@142: } franta-hg@142: franta-hg@142: @Override franta-hg@134: public void writeStartRow() { franta-hg@134: super.writeStartRow(); franta-hg@134: printStartElement(qname("tr")); franta-hg@134: } franta-hg@134: franta-hg@134: @Override franta-hg@134: public void writeColumnValue(Object value) { franta-hg@134: super.writeColumnValue(value); franta-hg@135: printTableData(value); franta-hg@134: } franta-hg@134: franta-hg@134: @Override franta-hg@134: public void writeEndRow() { franta-hg@134: super.writeEndRow(); franta-hg@134: printEndElement(); franta-hg@134: } franta-hg@134: franta-hg@134: @Override franta-hg@142: public void writeStartStatement() { franta-hg@142: super.writeStartStatement(); franta-hg@142: statementCounter++; franta-hg@135: printEmptyElement(qname("hr"), null); franta-hg@142: printTextElement(qname("h2"), null, "SQL statement #" + statementCounter); franta-hg@142: resultSetCounter = 0; franta-hg@142: updatesResultCounter = 0; franta-hg@134: } franta-hg@134: franta-hg@134: @Override franta-hg@142: public void writeUpdatesResult(int updatedRowsCount) { franta-hg@142: super.writeUpdatesResult(updatedRowsCount); franta-hg@134: updatesResultCounter++; franta-hg@135: printEmptyElement(qname("hr"), null); franta-hg@142: printTextElement(qname("h3"), null, "Updates result #" + updatesResultCounter); franta-hg@134: printTextElement(qname("p"), null, "Updated rows: " + updatedRowsCount); franta-hg@134: } franta-hg@128: }