package net.jaekl.squelch.stmt;
import java.io.PrintWriter;
+import java.nio.ByteBuffer;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.CodingErrorAction;
+import java.nio.charset.StandardCharsets;
import java.sql.SQLException;
+import java.sql.SQLFeatureNotSupportedException;
import java.sql.Types;
+import javax.xml.bind.DatatypeConverter;
+
import net.jaekl.squelch.sql.Column;
import net.jaekl.squelch.sql.Row;
// Includes routines to output the data as a human-friendly table, or as CSV.
abstract public class Tabular {
+ private static final long BLOB_MAX = 65536;
+
private static class RowBuffer {
private final int ROW_BUF_SIZE = 50;
abstract Row getNext() throws SQLException;
// Returns the number of (data) rows that were output
- public int printTable(PrintWriter pw) throws SQLException {
+ public int printTable(PrintWriter pw, String noRowsMessage)
+ throws SQLException
+ {
int rowCount = 0;
Column[] cols = getCols();
RowBuffer rowBuf;
if (rowCount > 0) {
writeDivider(pw, colWidths);
+ // TODO: Implement a String table for i18n
+ pw.println("" + rowCount + " row(s) returned.");
+ }
+ else {
+ pw.println(noRowsMessage);
}
- // TODO: Implement a String table for i18n
- pw.println("" + rowCount + " row(s) returned.");
pw.flush();
return rowCount;
}
return Object.class;
}
-
+
String repChar(char chr, int times)
{
StringBuffer sb = new StringBuffer();
return sb.toString();
}
+ String stringify(Object obj) throws SQLException
+ {
+ if (obj instanceof java.sql.Blob) {
+ java.sql.Blob blob = null;
+
+ try {
+ blob = (java.sql.Blob)obj;
+ long length = blob.length();
+ if (length > BLOB_MAX) {
+ length = BLOB_MAX;
+ }
+ byte[] content = blob.getBytes(1, (int)length);
+
+ try {
+ // Assume that the BLOB is actually UTF-8 text, and attempt to return it that way.
+ // This happens to work well for the databases that I deal with regularly.
+ // Other users may want to change this assumption here...
+ return StandardCharsets.UTF_8.newDecoder().onMalformedInput(CodingErrorAction.REPORT)
+ .onUnmappableCharacter(CodingErrorAction.REPORT)
+ .decode(ByteBuffer.wrap(content))
+ .toString();
+ }
+ catch (CharacterCodingException exc) {
+ // If we get here, then the BLOB's content is not valid UTF-8.
+ // This may be because it's in a different character set, or because it's non-text
+ // (e.g., a bitmap image).
+ // We'll hex-dump it instead.
+ return DatatypeConverter.printHexBinary(content);
+ }
+ }
+ finally {
+ if (null != blob) {
+ try {
+ blob.free();
+ }
+ catch (UnsupportedOperationException | SQLFeatureNotSupportedException exc) {
+ // free() was only a hint; if the hint is not welcome, then that's OK.
+ }
+ }
+ }
+ }
+
+ return "" + obj;
+ }
+
+ int stringWidth(Object obj)
+ {
+ if (obj instanceof java.sql.Blob) {
+ return 1;
+ }
+ return ("" + obj).length();
+ }
+
void writeDivider(PrintWriter pw, int[] colWidths) {
for (int idx = 0; idx < colWidths.length; ++idx) {
- pw.print("+" + repChar('-', colWidths[idx]));
+ pw.print("+" + repChar('-', colWidths[idx] + 2));
}
pw.println("+");
}
for (int idx = 0; idx < cols.length; ++idx) {
Column col = cols[idx];
- pw.print("|" + centrePad(col.getLabel(), colWidths[idx]));
+ pw.print("| " + centrePad(col.getLabel(), colWidths[idx]) + " ");
}
pw.println("|");
writeDivider(pw, colWidths);
}
- void writeRowBuffer(PrintWriter pw, RowBuffer rowBuf, int[] colWidths) {
+ void writeRowBuffer(PrintWriter pw, RowBuffer rowBuf, int[] colWidths) throws SQLException {
for (int rowIdx = 0; rowIdx < rowBuf.getPending(); ++rowIdx) {
Row row = rowBuf.getRow(rowIdx);
for (int colIdx = 0; colIdx < colWidths.length; ++colIdx) {
- String value = "" + row.getValue(colIdx + 1);
- String padding = repChar(' ', colWidths[colIdx] - value.length());
- pw.print("|" + value + padding);
+ Object obj = row.getValue(colIdx + 1);
+ String value = stringify(obj);
+ int width = stringWidth(obj);
+ String padding = repChar(' ', colWidths[colIdx] - width);
+ pw.print("| " + value + padding + " ");
}
pw.println("|");
}