java/sql-dk/src/info/globalcode/sql/dk/formatting/XhtmlFormatter.java
author František Kučera <franta-hg@frantovo.cz>
Sun, 05 Jan 2014 00:17:27 +0100
branchv_0
changeset 138 b765713c60e9
parent 137 3a24be5d8dac
child 142 da1e38386d84
permissions -rw-r--r--
XHTML formatter: simple formatting of (multidimensional) arrays
franta-hg@128
     1
/**
franta-hg@128
     2
 * SQL-DK
franta-hg@128
     3
 * Copyright © 2014 František Kučera (frantovo.cz)
franta-hg@128
     4
 *
franta-hg@128
     5
 * This program is free software: you can redistribute it and/or modify
franta-hg@128
     6
 * it under the terms of the GNU General Public License as published by
franta-hg@128
     7
 * the Free Software Foundation, either version 3 of the License, or
franta-hg@128
     8
 * (at your option) any later version.
franta-hg@128
     9
 *
franta-hg@128
    10
 * This program is distributed in the hope that it will be useful,
franta-hg@128
    11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
franta-hg@128
    12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
franta-hg@128
    13
 * GNU General Public License for more details.
franta-hg@128
    14
 *
franta-hg@128
    15
 * You should have received a copy of the GNU General Public License
franta-hg@128
    16
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
franta-hg@128
    17
 */
franta-hg@128
    18
package info.globalcode.sql.dk.formatting;
franta-hg@128
    19
franta-hg@134
    20
import info.globalcode.sql.dk.Constants;
franta-hg@134
    21
import info.globalcode.sql.dk.NamedParameter;
franta-hg@134
    22
import info.globalcode.sql.dk.Parameter;
franta-hg@134
    23
import info.globalcode.sql.dk.Xmlns;
franta-hg@134
    24
import info.globalcode.sql.dk.configuration.DatabaseDefinition;
franta-hg@134
    25
import info.globalcode.sql.dk.configuration.Properties;
franta-hg@134
    26
import info.globalcode.sql.dk.configuration.Property;
franta-hg@134
    27
import static info.globalcode.sql.dk.formatting.AbstractXmlFormatter.qname;
franta-hg@138
    28
import java.sql.Array;
franta-hg@138
    29
import java.sql.SQLException;
franta-hg@135
    30
import java.util.Date;
franta-hg@134
    31
import java.util.HashMap;
franta-hg@134
    32
import java.util.List;
franta-hg@134
    33
import java.util.Map;
franta-hg@135
    34
import java.util.Scanner;
franta-hg@138
    35
import java.util.logging.Level;
franta-hg@138
    36
import java.util.logging.Logger;
franta-hg@134
    37
import javax.xml.namespace.QName;
franta-hg@134
    38
franta-hg@128
    39
/**
franta-hg@128
    40
 *
franta-hg@128
    41
 * @author Ing. František Kučera (frantovo.cz)
franta-hg@128
    42
 */
franta-hg@128
    43
public class XhtmlFormatter extends AbstractXmlFormatter {
franta-hg@128
    44
franta-hg@138
    45
	private static final Logger log = Logger.getLogger(XhtmlFormatter.class.getName());
franta-hg@128
    46
	public static final String NAME = "xhtml"; // bash-completion:formatter
franta-hg@136
    47
	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
    48
	private static final String CSS_FILE = "info/globalcode/sql/dk/formatter/XhtmlFormatter.css";
franta-hg@134
    49
	private int resultSetCounter = 0;
franta-hg@134
    50
	private int updatesResultCounter = 0;
franta-hg@128
    51
franta-hg@128
    52
	public XhtmlFormatter(FormatterContext formatterContext) {
franta-hg@134
    53
		super(addDefaults(formatterContext));
franta-hg@134
    54
	}
franta-hg@134
    55
franta-hg@134
    56
	/**
franta-hg@134
    57
	 * Do not indent text – preserve whitespace for pre elements
franta-hg@134
    58
	 */
franta-hg@134
    59
	private static FormatterContext addDefaults(FormatterContext formatterContext) {
franta-hg@134
    60
		Properties defaults = new Properties(1);
franta-hg@134
    61
		defaults.add(new Property(PROPERTY_INDENT_TEXT, "false"));
franta-hg@134
    62
		formatterContext.getProperties().setLastDefaults(defaults);
franta-hg@134
    63
		return formatterContext;
franta-hg@134
    64
	}
franta-hg@134
    65
franta-hg@134
    66
	@Override
franta-hg@134
    67
	public void writeStartBatch() {
franta-hg@134
    68
		super.writeStartBatch();
franta-hg@134
    69
		printStartDocument();
franta-hg@136
    70
		printDoctype(DOCTYPE);
franta-hg@134
    71
		Map<QName, String> attributes = new HashMap<>(1);
franta-hg@134
    72
		attributes.put(qname("xmlns"), Xmlns.XHTML);
franta-hg@134
    73
		printStartElement(qname("html"), attributes);
franta-hg@134
    74
franta-hg@134
    75
		printStartElement(qname("head"));
franta-hg@134
    76
		printTextElement(qname("title"), null, Constants.PROGRAM_NAME + ": batch results");
franta-hg@135
    77
		printCss();
franta-hg@134
    78
		printEndElement();
franta-hg@134
    79
franta-hg@134
    80
		printStartElement(qname("body"));
franta-hg@134
    81
	}
franta-hg@134
    82
franta-hg@135
    83
	private void printCss() {
franta-hg@135
    84
franta-hg@135
    85
		try (Scanner css = new Scanner(getClass().getClassLoader().getResourceAsStream(CSS_FILE))) {
franta-hg@135
    86
			Map<QName, String> attributes = new HashMap<>(1);
franta-hg@135
    87
			attributes.put(qname("type"), "text/css");
franta-hg@135
    88
			printStartElement(qname("style"), attributes);
franta-hg@135
    89
			while (css.hasNext()) {
franta-hg@135
    90
				printText(css.nextLine(), true);
franta-hg@135
    91
			}
franta-hg@135
    92
			printEndElement();
franta-hg@135
    93
		}
franta-hg@135
    94
	}
franta-hg@135
    95
franta-hg@134
    96
	@Override
franta-hg@134
    97
	public void writeEndBatch() {
franta-hg@134
    98
		super.writeEndBatch();
franta-hg@134
    99
		printEndElement();
franta-hg@134
   100
		printEndElement();
franta-hg@134
   101
		printEndDocument();
franta-hg@134
   102
	}
franta-hg@134
   103
franta-hg@134
   104
	@Override
franta-hg@134
   105
	public void writeStartDatabase(DatabaseDefinition databaseDefinition) {
franta-hg@134
   106
		super.writeStartDatabase(databaseDefinition);
franta-hg@134
   107
		printTextElement(qname("h1"), null, "Database: " + databaseDefinition.getName());
franta-hg@135
   108
franta-hg@135
   109
		printStartElement(qname("p"));
franta-hg@135
   110
		printText("This is XHTML output of batch executed at: ", true);
franta-hg@135
   111
		printText(new Date().toString(), true);
franta-hg@135
   112
		printEndElement();
franta-hg@134
   113
	}
franta-hg@134
   114
franta-hg@134
   115
	@Override
franta-hg@134
   116
	public void writeQuery(String sql) {
franta-hg@134
   117
		super.writeQuery(sql);
franta-hg@134
   118
		printTextElement(qname("h3"), null, "SQL:");
franta-hg@134
   119
		printTextElement(qname("pre"), null, sql);
franta-hg@134
   120
	}
franta-hg@134
   121
franta-hg@134
   122
	@Override
franta-hg@134
   123
	public void writeParameters(List<? extends Parameter> parameters) {
franta-hg@134
   124
		super.writeParameters(parameters);
franta-hg@134
   125
franta-hg@134
   126
		if (parameters == null || parameters.isEmpty()) {
franta-hg@134
   127
			printTextElement(qname("p"), null, "(this query has no parameters)");
franta-hg@134
   128
		} else {
franta-hg@134
   129
			printTextElement(qname("h3"), null, "Parameters:");
franta-hg@134
   130
franta-hg@134
   131
			printStartElement(qname("table"));
franta-hg@134
   132
franta-hg@135
   133
			printStartElement(qname("thead"));
franta-hg@134
   134
			printStartElement(qname("tr"));
franta-hg@135
   135
			printTextElement(qname("td"), null, "id");
franta-hg@134
   136
			printTextElement(qname("td"), null, "type");
franta-hg@134
   137
			printTextElement(qname("td"), null, "value");
franta-hg@134
   138
			printEndElement();
franta-hg@134
   139
			printEndElement();
franta-hg@134
   140
franta-hg@134
   141
			printStartElement(qname("tbody"));
franta-hg@134
   142
			for (int i = 0; i < parameters.size(); i++) {
franta-hg@134
   143
				Parameter p = parameters.get(i);
franta-hg@134
   144
				printStartElement(qname("tr"));
franta-hg@134
   145
				String numberOrName;
franta-hg@134
   146
				if (p instanceof NamedParameter) {
franta-hg@134
   147
					numberOrName = ((NamedParameter) p).getName();
franta-hg@134
   148
				} else {
franta-hg@134
   149
					numberOrName = String.valueOf(i + 1);
franta-hg@134
   150
				}
franta-hg@134
   151
				printTextElement(qname("td"), null, numberOrName);
franta-hg@134
   152
				printTextElement(qname("td"), null, p.getType().name());
franta-hg@135
   153
				printTableData(p.getValue());
franta-hg@134
   154
				printEndElement();
franta-hg@134
   155
			}
franta-hg@134
   156
			printEndElement();
franta-hg@134
   157
franta-hg@134
   158
			printEndElement();
franta-hg@134
   159
		}
franta-hg@134
   160
	}
franta-hg@134
   161
franta-hg@135
   162
	private void printTableData(Object value) {
franta-hg@138
   163
franta-hg@138
   164
		if (value instanceof Array) {
franta-hg@138
   165
			Array sqlArray = (Array) value;
franta-hg@138
   166
			try {
franta-hg@138
   167
				Object[] array = (Object[]) sqlArray.getArray();
franta-hg@138
   168
				printStartElement(qname("td"));
franta-hg@138
   169
				printArray(array);
franta-hg@138
   170
				printEndElement();
franta-hg@138
   171
			} catch (SQLException e) {
franta-hg@138
   172
				log.log(Level.SEVERE, "Unable to format array", e);
franta-hg@138
   173
				printTableData(String.valueOf(value));
franta-hg@138
   174
			}
franta-hg@138
   175
		} else {
franta-hg@138
   176
			Map<QName, String> attributes = new HashMap<>(1);
franta-hg@138
   177
			if (value instanceof Number) {
franta-hg@138
   178
				attributes.put(qname("class"), "number");
franta-hg@138
   179
			} else if (value instanceof Boolean) {
franta-hg@138
   180
				attributes.put(qname("class"), "boolean");
franta-hg@138
   181
			}
franta-hg@138
   182
			printTextElement(qname("td"), attributes, String.valueOf(value));
franta-hg@135
   183
		}
franta-hg@138
   184
	}
franta-hg@138
   185
franta-hg@138
   186
	private void printArray(Object[] array) {
franta-hg@138
   187
		printStartElement(qname("ul"));
franta-hg@138
   188
		for (Object o : array) {
franta-hg@138
   189
			if (o instanceof Object[]) {
franta-hg@138
   190
				printStartElement(qname("li"));
franta-hg@138
   191
				printTextElement(qname("p"), null, "nested array:");
franta-hg@138
   192
				printArray((Object[]) o);
franta-hg@138
   193
				printEndElement();
franta-hg@138
   194
			} else {
franta-hg@138
   195
				printTextElement(qname("li"), null, String.valueOf(o));
franta-hg@138
   196
			}
franta-hg@138
   197
		}
franta-hg@138
   198
		printEndElement();
franta-hg@135
   199
	}
franta-hg@135
   200
franta-hg@134
   201
	@Override
franta-hg@134
   202
	public void writeColumnsHeader(ColumnsHeader header) {
franta-hg@134
   203
		super.writeColumnsHeader(header);
franta-hg@134
   204
		printTextElement(qname("h3"), null, "Data:");
franta-hg@134
   205
		printStartElement(qname("table"));
franta-hg@135
   206
		printStartElement(qname("thead"));
franta-hg@134
   207
		printStartElement(qname("tr"));
franta-hg@134
   208
		for (ColumnDescriptor cd : header.getColumnDescriptors()) {
franta-hg@134
   209
			// TODO: type
franta-hg@134
   210
			printTextElement(qname("td"), null, cd.getLabel());
franta-hg@134
   211
		}
franta-hg@134
   212
		printEndElement();
franta-hg@134
   213
		printEndElement();
franta-hg@134
   214
franta-hg@134
   215
		printStartElement(qname("tbody"));
franta-hg@134
   216
	}
franta-hg@134
   217
franta-hg@134
   218
	@Override
franta-hg@134
   219
	public void writeStartRow() {
franta-hg@134
   220
		super.writeStartRow();
franta-hg@134
   221
		printStartElement(qname("tr"));
franta-hg@134
   222
	}
franta-hg@134
   223
franta-hg@134
   224
	@Override
franta-hg@134
   225
	public void writeColumnValue(Object value) {
franta-hg@134
   226
		super.writeColumnValue(value);
franta-hg@135
   227
		printTableData(value);
franta-hg@134
   228
	}
franta-hg@134
   229
franta-hg@134
   230
	@Override
franta-hg@134
   231
	public void writeEndRow() {
franta-hg@134
   232
		super.writeEndRow();
franta-hg@134
   233
		printEndElement();
franta-hg@134
   234
	}
franta-hg@134
   235
franta-hg@134
   236
	@Override
franta-hg@134
   237
	public void writeStartResultSet() {
franta-hg@134
   238
		super.writeStartResultSet();
franta-hg@134
   239
		resultSetCounter++;
franta-hg@135
   240
		printEmptyElement(qname("hr"), null);
franta-hg@135
   241
		printTextElement(qname("h2"), null, "Result set #" + resultSetCounter);
franta-hg@134
   242
	}
franta-hg@134
   243
franta-hg@134
   244
	@Override
franta-hg@134
   245
	public void writeEndResultSet() {
franta-hg@134
   246
		super.writeEndResultSet();
franta-hg@134
   247
		printEndElement();
franta-hg@134
   248
		printEndElement();
franta-hg@135
   249
franta-hg@135
   250
		printTextElement(qname("p"), null, "Record count: " + getCurrentRowCount());
franta-hg@134
   251
	}
franta-hg@134
   252
franta-hg@134
   253
	@Override
franta-hg@134
   254
	public void writeStartUpdatesResult() {
franta-hg@134
   255
		super.writeStartUpdatesResult();
franta-hg@134
   256
		updatesResultCounter++;
franta-hg@135
   257
		printEmptyElement(qname("hr"), null);
franta-hg@135
   258
		printTextElement(qname("h2"), null, "Updates result #" + updatesResultCounter);
franta-hg@134
   259
	}
franta-hg@134
   260
franta-hg@134
   261
	@Override
franta-hg@134
   262
	public void writeUpdatedRowsCount(int updatedRowsCount) {
franta-hg@134
   263
		super.writeUpdatedRowsCount(updatedRowsCount);
franta-hg@134
   264
		printTextElement(qname("p"), null, "Updated rows: " + updatedRowsCount);
franta-hg@134
   265
	}
franta-hg@128
   266
}