1 package net.jaekl.squelch.stmt;
3 import java.io.PrintWriter;
4 import java.sql.SQLException;
7 import net.jaekl.squelch.sql.Column;
8 import net.jaekl.squelch.sql.Row;
10 // Copyright (C) 2016 by Chris Jaekl
12 // Tabular: wrapper around a set of tabular data (such as a ResultSet or table's MetaData).
13 // Includes routines to output the data as a human-friendly table, or as CSV.
15 abstract public class Tabular {
16 private static class RowBuffer {
17 private final int ROW_BUF_SIZE = 50;
19 private Row[] m_rowBuf;
20 private int m_pending;
23 m_rowBuf = new Row[ROW_BUF_SIZE];
27 public void addRow(Row row) {
28 assert (m_pending < m_rowBuf.length);
29 m_rowBuf[m_pending] = row;
32 public int getPending() { return m_pending; }
33 public Row getRow(int idx) {
34 assert (idx >= 0 && idx < m_pending);
37 public boolean isFull() { return m_pending >= m_rowBuf.length; }
40 abstract Column[] getCols() throws SQLException;
41 abstract Row getNext() throws SQLException;
43 // Returns the number of (data) rows that were output
44 public int printTable(PrintWriter pw, String noRowsMessage)
48 Column[] cols = getCols();
51 // Initialize colWidths to the widths of the column names
52 int[] colWidths = initColWidthsFromColNames(cols);
54 // Examine and buffer up to ROW_BUF_SIZE rows, adjusting (widening) colWidths where needed
55 rowBuf = bufferRows(colWidths);
57 int pending = rowBuf.getPending();
59 writeHeader(pw, cols, colWidths);
60 writeRowBuffer(pw, rowBuf, colWidths);
61 rowCount = rowBuf.getPending();
65 rowBuf = bufferRows(colWidths);
66 writeRowBuffer(pw, rowBuf, colWidths);
67 pending = rowBuf.getPending();
72 writeDivider(pw, colWidths);
73 // TODO: Implement a String table for i18n
74 pw.println("" + rowCount + " row(s) returned.");
77 pw.println(noRowsMessage);
85 // Returns the number of (data) rows that were output
86 public int printCsv(PrintWriter pw) throws SQLException {
88 Column[] cols = getCols();
91 StringBuilder sb = new StringBuilder();
92 boolean firstCol = true;
93 for (int idx = 1; idx <= cols.length; ++idx) {
100 sb.append("" + cols[idx - 1].getLabel());
103 String header = sb.toString();
105 while (null != (row = getNext())) {
106 if (null != header) {
112 for (int idx = 1; idx <= cols.length; ++idx) {
119 pw.print("" + row.getValue(idx));
129 // Examine and buffer up to rowBuf.length rows.
130 // Returns the number of actual rows that were buffered (zero if no more rows are available).
131 RowBuffer bufferRows(int[] colWidths) throws SQLException
133 RowBuffer rowBuf = new RowBuffer();
135 while (!rowBuf.isFull()) {
138 // No more rows available
143 // Check whether all values in this row will fit in the current column widths
144 for (int colIdx = 0; colIdx < colWidths.length; ++colIdx) {
145 int width = ("" + row.getValue(colIdx + 1)).length();
146 if (width > colWidths[colIdx]) {
147 // Widen the column to fit this value
148 colWidths[colIdx] = width;
156 String centrePad(String value, int desiredWidth)
159 return centrePad("NULL", desiredWidth);
161 if (value.length() >= desiredWidth) {
164 int left = (desiredWidth - value.length()) / 2;
165 int right = desiredWidth - value.length() - left;
166 return repChar(' ', left) + value + repChar(' ', right);
169 int[] initColWidthsFromColNames(Column[] cols)
171 int[] widths = new int[cols.length];
173 for (int idx = 0; idx < cols.length; ++idx) {
174 widths[idx] = cols[idx].getLabel().length();
180 Class<?> classForSqlType(int sqlType)
185 return Boolean.class;
187 return Character.class;
190 case Types.TIMESTAMP:
191 return java.util.Date.class;
202 return Integer.class;
205 case Types.LONGNVARCHAR:
206 case Types.LONGVARCHAR:
215 String repChar(char chr, int times)
217 StringBuffer sb = new StringBuffer();
218 for (int idx = 0; idx < times; ++idx) {
221 return sb.toString();
224 void writeDivider(PrintWriter pw, int[] colWidths) {
225 for (int idx = 0; idx < colWidths.length; ++idx) {
226 pw.print("+" + repChar('-', colWidths[idx]));
231 void writeHeader(PrintWriter pw, Column[] cols, int[] colWidths) {
232 writeDivider(pw, colWidths);
234 for (int idx = 0; idx < cols.length; ++idx) {
235 Column col = cols[idx];
236 pw.print("|" + centrePad(col.getLabel(), colWidths[idx]));
240 writeDivider(pw, colWidths);
243 void writeRowBuffer(PrintWriter pw, RowBuffer rowBuf, int[] colWidths) {
245 for (int rowIdx = 0; rowIdx < rowBuf.getPending(); ++rowIdx) {
246 Row row = rowBuf.getRow(rowIdx);
247 for (int colIdx = 0; colIdx < colWidths.length; ++colIdx) {
248 String value = "" + row.getValue(colIdx + 1);
249 String padding = repChar(' ', colWidths[colIdx] - value.length());
250 pw.print("|" + value + padding);