Package winterwell.utils.io

Source Code of winterwell.utils.io.CSVWriter

package winterwell.utils.io;

import java.io.BufferedWriter;
import java.io.Closeable;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.Writer;
import java.util.List;

import winterwell.utils.IORException;
import winterwell.utils.StrUtils;
import winterwell.utils.Utils;

/**
* Support for creating .csv files.
*
* Implements "standard" CSV behaviour as per
* http://en.wikipedia.org/wiki/Comma-separated_values
*
* TODO is this thread safe?? depends on whether {@link BufferedWriter} is. if
* needed maybe use a lock-free queue instead of a lock for high concurrency??
*
* TODO: Add method for writing a comment, quote fields containing comment chars
* ?
*
* @author daniel
* @testedby {@link CSVWriterTest}
*/
public class CSVWriter implements Closeable {

  private static BufferedWriter CSVWriter2_fileWriter(File file,
      boolean append) {
    try {
      return FileUtils.getWriter(new FileOutputStream(file, append));
    } catch (FileNotFoundException e) {
      throw Utils.runtime(e);
    }
  }

  char commentMarker;
  private final char delimiter;
  private File file;
  private CharSequence LINEEND = StrUtils.LINEEND;
  int linesWritten = 0;

  private BufferedWriter out;

  private final String quote; // This should be ' or "

  private final String quotedQuote;

  /**
   * Create a CSV file with the standard double-quote quote character.
   *
   * @param file
   * @param delimiter
   * @throws FileNotFoundException
   */
  public CSVWriter(File file, char delimiter) {
    this(file, delimiter, '"', false);
  }

  /**
   * Work with CSV file with the standard double-quote quote character.
   *
   * @param file
   * @param delimiter
   * @throws FileNotFoundException
   */
  public CSVWriter(File file, char delimiter, boolean append) {
    this(file, delimiter, '"', append);
  }

  public CSVWriter(File file, char delimiter, char quote)
      throws FileNotFoundException {
    this(file, delimiter, quote, false);
  }

  public CSVWriter(File file, char delimiter, char quote, boolean append) {
    this(CSVWriter2_fileWriter(file, append), delimiter, quote);
    this.file = file;
  }

  public CSVWriter(Writer out, char delimiter, char quote) {
    assert out != null;
    file = null;
    this.out = out instanceof BufferedWriter ? (BufferedWriter) out
        : new BufferedWriter(out);
    this.delimiter = delimiter;
    // Possibly this is too restrictive, but actually other values don't
    // really make sense
    assert quote == '\'' || quote == '"';
    this.quote = "" + quote;
    this.quotedQuote = "" + quote + quote;
  }

  /**
   * Flush & close the underlying file writer
   */
  @Override
  public void close() {
    try { // is this needed?
      flush();
    } catch (Exception e) {
      // oh well;
    }
    FileUtils.close(out);
  }

  public void flush() {
    try {
      out.flush();
    } catch (IOException e) {
      throw new IORException(e);
    }
  }

  /**
   * @return file (if created using the file constructor) or null. null does
   *         not imply that this is not a file-based writer.
   */
  public File getFile() {
    return file;
  }

  /**
   * Set this writer to append to the end of an existing file. Must be called
   * before any lines are written
   *
   * @param append
   */
  public void setAppend(boolean append) {
    assert linesWritten == 0;
    if (!append)
      return;
    try {
      out = FileUtils.getWriter(new FileOutputStream(file, true));
    } catch (FileNotFoundException e) {
      throw new IORException(e);
    }
  }

  /**
   * @param commentMarker
   *            If set (eg to '#'), then items beginning with this character
   *            will be quoted to avoid them being interpreted as comments at
   *            the other end. 0 by default. Comment markers are not standard
   *            csv to the extent that there is such a thing.
   */
  public void setCommentMarker(char commentMarker) {
    this.commentMarker = commentMarker;
  }

  /**
   * Change the default line-end. E.g. if you want to force M$ style \r\n
   * output
   *
   * @param lineEnd
   */
  public void setLineEnd(CharSequence lineEnd) {
    LINEEND = lineEnd;
  }

  @Override
  public String toString() {
    return file == null ? getClass().getSimpleName() : getClass()
        .getSimpleName() + "[" + file + "]";
  }

  /**
   * Convenience for {@link #write(Object[])}
   *
   * @param line
   */
  public void write(List line) {
    write(line.toArray());
  }

  /**
   * Write out a row.
   *
   * @param objects
   *            These will be converted by {@link String#valueOf(Object)},
   *            with escaping of the delimiter and the escape char. Quotes:
   *            added if set, otherwise line-breaks are converted into spaces.
   */
  public void write(Object... strings) {
    // defend against accidentally routing to the wrong method
    if (strings.length == 1 && strings[0] instanceof String[]) {
      write((String[]) strings[0]);
      return;
    }
    String[] ss = new String[strings.length];
    for (int i = 0; i < strings.length; i++) {
      ss[i] = strings[i] == null ? null : String.valueOf(strings[i]);
    }
    write(ss);
  }

  /**
   * Write out a row.
   *
   * @param objects
   *            These will be escaping for the delimiter and the escape char.
   *            Quotes: added if set, otherwise line-breaks are converted into
   *            spaces.
   */
  public void write(String... strings) {
    linesWritten++;
    try {
      if (strings.length == 0) {
        out.append(LINEEND);
        return;
      }
      StringBuilder sb = new StringBuilder();
      for (int i = 0, n = strings.length; i < n; i++) {
        String si = strings[i] == null ? "" : strings[i];

        // TODO: Add an option to suppress in-field line breaks
        // NB: Line breaking within a quote is okay per the standard

        // If field contains the delimiter, quote-char, newline, or
        // comment-char it must be quoted
        if (si.indexOf(delimiter) != -1
            || si.indexOf(quote) != -1
            || si.indexOf('\n') != -1
            || (commentMarker != 0 && si.indexOf(commentMarker) != -1)) {
          // Quote character must be replaced by double quote
          si = si.replace(quote, quotedQuote);
          si = quote + si + quote;
        }

        sb.append(si);
        sb.append(delimiter);
      }
      // remove final delimiter
      StrUtils.pop(sb, 1);
      sb.append(LINEEND);
      // write
      out.append(sb);
    } catch (IOException ex) {
      throw new IORException(ex);
    }
  }

  public void writeComment(String comment) {
    if (commentMarker == 0)
      throw new IllegalStateException(
          "You must specify a comment marker before writing comments");
    // ??
    if (comment.charAt(0) == commentMarker) {
      comment = comment.substring(1);
    }
    try {
      out.append(commentMarker);
      out.append(' ');
      out.append(comment);
      out.append(LINEEND);
    } catch (IOException e) {
      throw Utils.runtime(e);
    }
  }

}
TOP

Related Classes of winterwell.utils.io.CSVWriter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.