DelimitedWriter.java

    1/*******************************************************************************
    2 * Copyright (c) 2009 Mountainminds GmbH & Co. KG and others
    3 * All rights reserved. This program and the accompanying materials
    4 * are made available under the terms of the Eclipse Public License v1.0
    5 * which accompanies this distribution, and is available at
    6 * http://www.eclipse.org/legal/epl-v10.html
    7 *
    8 * Contributors:
    9 *    Brock Janiczak - initial API and implementation
   10 *    
   11 * $Id: $
   12 *******************************************************************************/
   13package org.jacoco.report.csv;
   14
   15import java.io.IOException;
   16import java.io.Writer;
   17
   18/**
   19 * Helper class for writing out CSV or tab delimited files.
   20 * <p>
   21 * <strong>Example Usage:</strong>
   22 * 
   23 * <pre>
   24 * delimitedWriter.writeFields(&quot;header1&quot;, &quot;header2&quot;, ...);
   25 * for each line to be written {
   26 *   delimitedWriter.writeField(value1);
   27 *   delimitedWriter.writeField(value2);
   28 *   delimitedWriter.nextLine();
   29 * }
   30 * delimitedWriter.close();
   31 * </pre>
   32 * 
   33 * </p>
   34 * 
   35 * @author Brock Janiczak
   36 * @version $Revision: $
   37 */
   38public class DelimitedWriter {
   39    private static final String QUOTE = "\"";
   40    private static final String ESCAPED_QUOTE = "\"\"";
   41
   42    private static final char DEFAULT_DELIMITER = ',';
   43    private static final String NEW_LINE = System.getProperty("line.separator");
   44    private final char delimiter;
   45    private final Writer delegate;
   46    private int fieldPosition = 0;
   47
   48    /**
   49     * Creates a new Delimited writer using the default delimiter
   50     * 
   51     * @param delegate
   52     *            Writer to delegate all writes to
   53     */
   54    public DelimitedWriter(final Writer delegate) {
   55        this(delegate, DEFAULT_DELIMITER);
   56    }
   57
   58    /**
   59     * Creates a new Delimited writer using the default delimiter
   60     * 
   61     * @param delegate
   62     *            Writer to delegate all writes to
   63     * @param delimiter
   64     *            delimiter to use (usually a comma, tab or space)
   65     */
   66    public DelimitedWriter(final Writer delegate, final char delimiter) {
   67        this.delegate = delegate;
   68        this.delimiter = delimiter;
   69    }
   70
   71    /**
   72     * Write multiple fields at once. Values will be auto escaped and quoted as
   73     * needed. Each value will be separated using the current delimiter
   74     * 
   75     * @param fields
   76     *            Values to write
   77     * @throws IOException
   78     *             Error writing to the underlying writer object
   79     */
   80    public void write(final String... fields) throws IOException {
   81        for (final String field : fields) {
   82            write(field);
   83        }
   84    }
   85
   86    /**
   87     * Write a single value. Values will be auto escaped and quoted as needed.
   88     * If this is not the first field of the current line the value will be
   89     * prepended with the current delimiter
   90     * 
   91     * @param field
   92     *            Value to write
   93     * @throws IOException
   94     *             Error writing to the underlying writer object
   95     */
   96    public void write(final String field) throws IOException {
   97        if (fieldPosition != 0) {
   98            delegate.write(delimiter);
   99        }
  100        delegate.write(escape(field));
  101        fieldPosition++;
  102    }
  103
  104    /**
  105     * Write a single integer value.
  106     * 
  107     * @param value
  108     *            Value to write
  109     * @throws IOException
  110     *             Error writing to the underlying writer object
  111     */
  112    public void write(final int value) throws IOException {
  113        write(Integer.toString(value));
  114    }
  115
  116    /**
  117     * Write muliple integer values
  118     * 
  119     * @param values
  120     *            values to write
  121     * @throws IOException
  122     *             Error writing to the underlying writer object
  123     */
  124    public void write(final int... values) throws IOException {
  125        for (final int value : values) {
  126            write(Integer.toString(value));
  127        }
  128    }
  129
  130    /**
  131     * Output a new line and advance the writer to the next line. The line
  132     * delimiter is the default for the platform.
  133     * 
  134     * @throws IOException
  135     *             Error writing to the underlying writer object
  136     */
  137    public void nextLine() throws IOException {
  138        delegate.write(NEW_LINE);
  139        fieldPosition = 0;
  140    }
  141
  142    /**
  143     * Close the underlying writer object. Once closed all write operations will
  144     * fail
  145     * 
  146     * @throws IOException
  147     *             Error closing the underlying writer object
  148     */
  149    public void close() throws IOException {
  150        delegate.close();
  151    }
  152
  153    /**
  154     * Escapes any occurrences of the quote character in value by replacing it
  155     * with a double quote. Also Quotes the value if a quote or delimiter value
  156     * is found.
  157     * 
  158     * @param value
  159     *            String that needs escaping
  160     * @return New string with all values escaped
  161     */
  162    private String escape(final String value) {
  163        String escapedValue = value;
  164
  165        // Escape and quote if the source value contains the delimiter
  166        // or the quote character
  167        if (value.indexOf(QUOTE) != -1 || value.indexOf(delimiter) != -1) {
  168            escapedValue = value.replace(QUOTE, ESCAPED_QUOTE);
  169            escapedValue = QUOTE + escapedValue + QUOTE;
  170        }
  171
  172        return escapedValue;
  173    }
  174}