/* * * SchemaCrawler * http://sourceforge.net/projects/schemacrawler * Copyright (c) 2000-2009, Sualeh Fatehi. * * This library is free software; you can redistribute it and/or modify it under the terms * of the GNU Lesser General Public License as published by the Free Software Foundation; * either version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with this * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * */ package schemacrawler.tools.datatext; import java.io.BufferedInputStream; import java.io.IOException; import java.io.PrintWriter; import java.sql.Blob; import java.sql.Clob; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import schemacrawler.execute.DataHandler; import schemacrawler.execute.QueryExecutorException; import schemacrawler.schemacrawler.SchemaCrawlerException; import schemacrawler.tools.OutputFormat; import schemacrawler.tools.OutputOptions; import schemacrawler.tools.util.HtmlFormattingHelper; import schemacrawler.tools.util.PlainTextFormattingHelper; import schemacrawler.tools.util.TextFormattingHelper; import sf.util.Utilities; /** * Text formatting of data. * * @author Sualeh Fatehi */ public final class DataTextFormatter implements DataHandler { private static final Logger LOGGER = Logger.getLogger(DataTextFormatter.class .getName()); private static final String BINARY = "<binary>"; private final DataTextFormatOptions options; private final PrintWriter out; private final TextFormattingHelper formattingHelper; /** * Text formatting of data. * * @param options * Options for text formatting of data */ public DataTextFormatter(final DataTextFormatOptions options) throws SchemaCrawlerException { if (options == null) { throw new IllegalArgumentException("Options not provided"); } this.options = options; final OutputOptions outputOptions = options.getOutputOptions(); final OutputFormat outputFormat = outputOptions.getOutputFormat(); if (outputFormat == OutputFormat.html) { formattingHelper = new HtmlFormattingHelper(outputFormat); } else { formattingHelper = new PlainTextFormattingHelper(OutputFormat.csv); } try { out = options.getOutputOptions().openOutputWriter(); } catch (final IOException e) { throw new SchemaCrawlerException("Could not obtain output writer", e); } } /** * {@inheritDoc} * * @see schemacrawler.execute.DataHandler#begin() */ public void begin() { if (!options.getOutputOptions().isNoHeader()) { out.println(formattingHelper.createDocumentStart()); } } /** * {@inheritDoc} * * @see schemacrawler.execute.DataHandler#end() */ public void end() { if (!options.getOutputOptions().isNoFooter()) { out.println(formattingHelper.createDocumentEnd()); } out.flush(); // options.getOutputOptions().closeOutputWriter(out); } /** * {@inheritDoc} * * @see schemacrawler.execute.DataHandler#getPrintWriter() */ public PrintWriter getPrintWriter() { return out; } /** * {@inheritDoc} * * @see schemacrawler.execute.DataHandler#handleData(java.sql.ResultSet) */ public void handleData(final String title, final ResultSet rows) throws QueryExecutorException { out.println(formattingHelper.createObjectStart(title)); try { final ResultSetMetaData rsm = rows.getMetaData(); final int columnCount = rsm.getColumnCount(); final String[] columnNames = new String[columnCount]; for (int i = 0; i < columnCount; i++) { columnNames[i] = rsm.getColumnName(i + 1); } out.println(formattingHelper.createRowHeader(columnNames)); if (options.isMergeRows() && columnCount > 1) { iterateRowsAndMerge(rows, columnNames); } else { iterateRows(rows, columnNames.length); } } catch (final SQLException e) { throw new QueryExecutorException(e.getMessage(), e); } out.println(formattingHelper.createObjectEnd()); } private String convertColumnDataToString(final Object columnData) { String columnDataString; if (columnData == null) { columnDataString = "<null>"; } else if (columnData instanceof Clob || columnData instanceof Blob) { columnDataString = BINARY; if (options.isShowLobs()) { columnDataString = readLob(columnData); } } else { columnDataString = columnData.toString(); } return columnDataString; } private void doHandleOneRow(final List<String> row, final String lastColumnData) throws QueryExecutorException { if (row.isEmpty()) { return; } final List<String> outputRow = new ArrayList<String>(); // output outputRow.addAll(row); outputRow.add(lastColumnData); final String[] columnData = outputRow.toArray(new String[outputRow.size()]); out.println(formattingHelper.createRow(columnData)); } private void iterateRows(final ResultSet rows, final int columnCount) throws SQLException, QueryExecutorException { List<String> currentRow; while (rows.next()) { currentRow = new ArrayList<String>(columnCount); for (int i = 0; i < columnCount; i++) { final int columnIndex = i + 1; final Object columnData = rows.getObject(columnIndex); final String columnDataString = convertColumnDataToString(columnData); currentRow.add(columnDataString); } final String[] columnData = currentRow.toArray(new String[currentRow .size()]); out.println(formattingHelper.createRow(columnData)); } } private void iterateRowsAndMerge(final ResultSet rows, final String[] columnNames) throws SQLException, QueryExecutorException { final int columnCount = columnNames.length; List<String> previousRow = new ArrayList<String>(); List<String> currentRow; StringBuilder currentRowLastColumn = new StringBuilder(); // write out the data while (rows.next()) { currentRow = new ArrayList<String>(columnCount - 1); for (int i = 0; i < columnCount - 1; i++) { final Object columnData = rows.getObject(i + 1); final String columnDataString = convertColumnDataToString(columnData); currentRow.add(columnDataString); } final Object lastColumnData = rows.getObject(columnCount); final String lastColumnDataString = convertColumnDataToString(lastColumnData); if (currentRow.equals(previousRow)) { currentRowLastColumn.append(lastColumnDataString); } else { // At this point, we have a new row coming in, so dump the // previous merged row out doHandleOneRow(previousRow, currentRowLastColumn.toString()); // reset currentRowLastColumn = new StringBuilder(); // save the last column currentRowLastColumn.append(lastColumnDataString); } previousRow = currentRow; } // Dump the last row out doHandleOneRow(previousRow, currentRowLastColumn.toString()); } /** * Reads data from a LOB into a string. Default system encoding is * assumed. * * @param columnData * Column data object returned by JDBC * @return A string with the contents of the LOB */ private String readLob(final Object columnData) { BufferedInputStream in = null; String lobData = BINARY; try { if (columnData instanceof Blob) { final Blob blob = (Blob) columnData; in = new BufferedInputStream(blob.getBinaryStream()); } else if (columnData instanceof Clob) { final Clob clob = (Clob) columnData; in = new BufferedInputStream(clob.getAsciiStream()); } lobData = new String(Utilities.readFully(in)); return lobData; } catch (final SQLException e) { LOGGER.log(Level.FINE, "Could not read binary data", e); return lobData; } } }