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();
43 // Returns the number of (data) rows that were output
44 public int printTable(PrintWriter pw) throws SQLException {
46 Column[] cols = getCols();
49 // Initialize colWidths to the widths of the column names
50 int[] colWidths = initColWidthsFromColNames(cols);
52 // Examine and buffer up to ROW_BUF_SIZE rows, adjusting (widening) colWidths where needed
53 rowBuf = bufferRows(colWidths);
55 int pending = rowBuf.getPending();
57 writeHeader(pw, cols, colWidths);
58 writeRowBuffer(pw, rowBuf, colWidths);
59 rowCount = rowBuf.getPending();
63 rowBuf = bufferRows(colWidths);
64 writeRowBuffer(pw, rowBuf, colWidths);
65 pending = rowBuf.getPending();
69 // TODO: Implement a String table for i18n
70 pw.println("" + rowCount + " row(s) returned.");
76 // Returns the number of (data) rows that were output
77 public int printCsv(PrintWriter pw) throws SQLException {
79 Column[] cols = getCols();
82 StringBuilder sb = new StringBuilder();
83 boolean firstCol = true;
84 for (int idx = 1; idx <= cols.length; ++idx) {
91 sb.append("" + cols[idx - 1].getLabel());
94 String header = sb.toString();
96 while (null != (row = getNext())) {
103 for (int idx = 1; idx <= cols.length; ++idx) {
110 pw.print("" + row.getValue(idx));
120 // Examine and buffer up to rowBuf.length rows.
121 // Returns the number of actual rows that were buffered (zero if no more rows are available).
122 RowBuffer bufferRows(int[] colWidths)
124 RowBuffer rowBuf = new RowBuffer();
126 while (!rowBuf.isFull()) {
129 // No more rows available
134 // Check whether all values in this row will fit in the current column widths
135 for (int colIdx = 0; colIdx < colWidths.length; ++colIdx) {
136 int width = ("" + row.getValue(colIdx + 1)).length();
137 if (width > colWidths[colIdx]) {
138 // Widen the column to fit this value
139 colWidths[colIdx] = width;
147 String centrePad(String value, int desiredWidth)
150 return centrePad("NULL", desiredWidth);
152 if (value.length() >= desiredWidth) {
155 int left = (desiredWidth - value.length()) / 2;
156 int right = desiredWidth - value.length() - left;
157 return repChar(' ', left) + value + repChar(' ', right);
160 int[] initColWidthsFromColNames(Column[] cols)
162 int[] widths = new int[cols.length];
164 for (int idx = 0; idx < cols.length; ++idx) {
165 widths[idx] = cols[idx].getLabel().length();
171 Class<?> classForSqlType(int sqlType)
176 return Boolean.class;
178 return Character.class;
181 case Types.TIMESTAMP:
182 return java.util.Date.class;
193 return Integer.class;
196 case Types.LONGNVARCHAR:
197 case Types.LONGVARCHAR:
206 String repChar(char chr, int times)
208 StringBuffer sb = new StringBuffer();
209 for (int idx = 0; idx < times; ++idx) {
212 return sb.toString();
215 void writeDivider(PrintWriter pw, int[] colWidths) {
216 for (int idx = 0; idx < colWidths.length; ++idx) {
217 pw.print("+" + repChar('-', colWidths[idx]));
222 void writeHeader(PrintWriter pw, Column[] cols, int[] colWidths) {
223 writeDivider(pw, colWidths);
225 for (int idx = 1; idx <= cols.length; ++idx) {
226 Column col = cols[idx];
227 pw.print("|" + centrePad(col.getLabel(), colWidths[idx]));
231 writeDivider(pw, colWidths);
234 void writeRowBuffer(PrintWriter pw, RowBuffer rowBuf, int[] colWidths) {
236 for (int rowIdx = 0; rowIdx < rowBuf.getPending(); ++rowIdx) {
237 Row row = rowBuf.getRow(rowIdx);
238 for (int colIdx = 0; colIdx < colWidths.length; ++colIdx) {
239 String value = "" + row.getValue(colIdx);
240 String padding = repChar(' ', colWidths[colIdx] - value.length());
241 pw.print("|" + value + padding);