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