/* * Copyright (c) 2013 Patrick Meyer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.itemanalysis.jmetrik.utils; import au.com.bytecode.opencsv.ResultSetHelper; import au.com.bytecode.opencsv.ResultSetHelperService; import com.itemanalysis.psychometrics.data.DataType; import com.itemanalysis.psychometrics.data.VariableAttributes; import java.io.Closeable; import java.io.IOException; import java.io.PrintWriter; import java.io.Writer; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; /** * This class is based on CSVWriter in OpenCSV library. * It only writes the quotechar for variables that are strings. * This change is needed for the bulk import procedure in Apache * Derby. Apache Derby bulk import only allows fields that are * strings to have quotes. Numeric values must not have quotes. * */ public class DerbyCSVWriter implements Closeable { public static final int INITIAL_STRING_SIZE = 128; private Writer rawWriter; private PrintWriter pw; private char separator; private char quotechar; private char escapechar; private String lineEnd; /** The character used for escaping quotes. */ public static final char DEFAULT_ESCAPE_CHARACTER = '"'; /** The default separator to use if none is supplied to the constructor. */ public static final char DEFAULT_SEPARATOR = ','; /** * The default quote character to use if none is supplied to the * constructor. */ public static final char DEFAULT_QUOTE_CHARACTER = '"'; /** The quote constant to use when you wish to suppress all quoting. */ public static final char NO_QUOTE_CHARACTER = '\u0000'; /** The escape constant to use when you wish to suppress all escaping. */ public static final char NO_ESCAPE_CHARACTER = '\u0000'; /** Default line terminator uses platform encoding. */ public static final String DEFAULT_LINE_END = "\n"; private ResultSetHelper resultService = new ResultSetHelperService(); private ArrayList<VariableAttributes> variables = null; /** * Constructs CSVWriter using a comma for the separator. * * @param writer * the writer to an underlying CSV source. */ public DerbyCSVWriter(Writer writer, ArrayList<VariableAttributes> variables) { this(writer, DEFAULT_SEPARATOR, variables); } /** * Constructs CSVWriter with supplied separator. * * @param writer * the writer to an underlying CSV source. * @param separator * the delimiter to use for separating entries. */ public DerbyCSVWriter(Writer writer, char separator, ArrayList<VariableAttributes> variables) { this(writer, separator, DEFAULT_QUOTE_CHARACTER, variables); } /** * Constructs CSVWriter with supplied separator and quote char. * * @param writer * the writer to an underlying CSV source. * @param separator * the delimiter to use for separating entries * @param quotechar * the character to use for quoted elements */ public DerbyCSVWriter(Writer writer, char separator, char quotechar, ArrayList<VariableAttributes> variables) { this(writer, separator, quotechar, DEFAULT_ESCAPE_CHARACTER, variables); } /** * Constructs CSVWriter with supplied separator and quote char. * * @param writer * the writer to an underlying CSV source. * @param separator * the delimiter to use for separating entries * @param quotechar * the character to use for quoted elements * @param escapechar * the character to use for escaping quotechars or escapechars */ public DerbyCSVWriter(Writer writer, char separator, char quotechar, char escapechar, ArrayList<VariableAttributes> variables) { this(writer, separator, quotechar, escapechar, DEFAULT_LINE_END, variables); } /** * Constructs CSVWriter with supplied separator and quote char. * * @param writer * the writer to an underlying CSV source. * @param separator * the delimiter to use for separating entries * @param quotechar * the character to use for quoted elements * @param lineEnd * the line feed terminator to use */ public DerbyCSVWriter(Writer writer, char separator, char quotechar, String lineEnd, ArrayList<VariableAttributes> variables) { this(writer, separator, quotechar, DEFAULT_ESCAPE_CHARACTER, lineEnd, variables); } /** * Constructs CSVWriter with supplied separator, quote char, escape char and line ending. * * @param writer * the writer to an underlying CSV source. * @param separator * the delimiter to use for separating entries * @param quotechar * the character to use for quoted elements * @param escapechar * the character to use for escaping quotechars or escapechars * @param lineEnd * the line feed terminator to use */ public DerbyCSVWriter(Writer writer, char separator, char quotechar, char escapechar, String lineEnd, ArrayList<VariableAttributes> variables) { this.rawWriter = writer; this.pw = new PrintWriter(writer); this.separator = separator; this.quotechar = quotechar; this.escapechar = escapechar; this.lineEnd = lineEnd; this.variables = variables; } /** * Writes the entire list to a CSV file. The list is assumed to be a * String[] * * @param allLines * a List of String[], with each String[] representing a line of * the file. */ public void writeAll(List<String[]> allLines) { for (String[] line : allLines) { writeNext(line); } } protected void writeColumnNames(ResultSet rs) throws SQLException { writeNext(resultService.getColumnNames(rs)); } /** * Writes the entire ResultSet to a CSV file. * * The caller is responsible for closing the ResultSet. * * @param rs the recordset to write * @param includeColumnNames true if you want column names in the output, false otherwise * * @throws java.io.IOException thrown by getColumnValue * @throws java.sql.SQLException thrown by getColumnValue */ public void writeAll(java.sql.ResultSet rs, boolean includeColumnNames) throws SQLException, IOException { if (includeColumnNames) { writeColumnNames(rs); } while (rs.next()) { writeNext(resultService.getColumnValues(rs)); } } /** * Writes the next line to the file. * * @param nextLine * a string array with each comma-separated element as a separate * entry. */ public void writeNext(String[] nextLine) { if (nextLine == null) return; boolean stringValue = false; StringBuilder sb = new StringBuilder(INITIAL_STRING_SIZE); for (int i = 0; i < nextLine.length; i++) { stringValue = variables.get(i).getType().getDataType()== DataType.STRING; if (i != 0) { sb.append(separator); } String nextElement = nextLine[i]; if (nextElement == null) continue; if (quotechar != NO_QUOTE_CHARACTER && stringValue) sb.append(quotechar); sb.append(stringContainsSpecialCharacters(nextElement) ? processLine(nextElement) : nextElement); if (quotechar != NO_QUOTE_CHARACTER && stringValue) sb.append(quotechar); } sb.append(lineEnd); pw.write(sb.toString()); } private boolean stringContainsSpecialCharacters(String line) { return line.indexOf(quotechar) != -1 || line.indexOf(escapechar) != -1; } protected StringBuilder processLine(String nextElement) { StringBuilder sb = new StringBuilder(INITIAL_STRING_SIZE); for (int j = 0; j < nextElement.length(); j++) { char nextChar = nextElement.charAt(j); if (escapechar != NO_ESCAPE_CHARACTER && nextChar == quotechar) { sb.append(escapechar).append(nextChar); } else if (escapechar != NO_ESCAPE_CHARACTER && nextChar == escapechar) { sb.append(escapechar).append(nextChar); } else { sb.append(nextChar); } } return sb; } /** * Flush underlying stream to writer. * * @throws IOException if bad things happen */ public void flush() throws IOException { pw.flush(); } /** * Close the underlying stream writer flushing any buffered content. * * @throws IOException if bad things happen * */ public void close() throws IOException { flush(); pw.close(); rawWriter.close(); } /** * Checks to see if the there has been an error in the printstream. */ public boolean checkError() { return pw.checkError(); } public void setResultService(ResultSetHelper resultService) { this.resultService = resultService; } }