aboutsummaryrefslogtreecommitdiff
path: root/org.jacoco.report/src/org/jacoco/report/csv/DelimitedWriter.java
blob: 3d3152a43126ce4009f1e86783a50964930a77d1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
/*******************************************************************************
 * Copyright (c) 2009, 2021 Mountainminds GmbH & Co. KG and Contributors
 * This program and the accompanying materials are made available under
 * the terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *    Brock Janiczak - initial API and implementation
 *
 *******************************************************************************/
package org.jacoco.report.csv;

import java.io.IOException;
import java.io.Writer;

/**
 * Helper class for writing out CSV or tab delimited files.
 * <p>
 * <strong>Example Usage:</strong>
 *
 * <pre>
 * delimitedWriter.writeFields(&quot;header1&quot;, &quot;header2&quot;, ...);
 * for each line to be written {
 *   delimitedWriter.writeField(value1);
 *   delimitedWriter.writeField(value2);
 *   delimitedWriter.nextLine();
 * }
 * delimitedWriter.close();
 * </pre>
 *
 * </p>
 */
class DelimitedWriter {
	private static final String QUOTE = "\"";
	private static final String ESCAPED_QUOTE = "\"\"";

	private static final char DEFAULT_DELIMITER = ',';
	private static final String NEW_LINE = System.getProperty("line.separator");
	private final char delimiter;
	private final Writer delegate;
	private int fieldPosition = 0;

	/**
	 * Creates a new Delimited writer using the default delimiter
	 *
	 * @param delegate
	 *            Writer to delegate all writes to
	 */
	public DelimitedWriter(final Writer delegate) {
		this(delegate, DEFAULT_DELIMITER);
	}

	/**
	 * Creates a new Delimited writer using the default delimiter
	 *
	 * @param delegate
	 *            Writer to delegate all writes to
	 * @param delimiter
	 *            delimiter to use (usually a comma, tab or space)
	 */
	public DelimitedWriter(final Writer delegate, final char delimiter) {
		this.delegate = delegate;
		this.delimiter = delimiter;
	}

	/**
	 * Write multiple fields at once. Values will be auto escaped and quoted as
	 * needed. Each value will be separated using the current delimiter
	 *
	 * @param fields
	 *            Values to write
	 * @throws IOException
	 *             Error writing to the underlying writer object
	 */
	public void write(final String... fields) throws IOException {
		for (final String field : fields) {
			write(field);
		}
	}

	/**
	 * Write a single value. Values will be auto escaped and quoted as needed.
	 * If this is not the first field of the current line the value will be
	 * prepended with the current delimiter
	 *
	 * @param field
	 *            Value to write
	 * @throws IOException
	 *             Error writing to the underlying writer object
	 */
	public void write(final String field) throws IOException {
		if (fieldPosition != 0) {
			delegate.write(delimiter);
		}
		delegate.write(escape(field));
		fieldPosition++;
	}

	/**
	 * Write a single integer value.
	 *
	 * @param value
	 *            Value to write
	 * @throws IOException
	 *             Error writing to the underlying writer object
	 */
	public void write(final int value) throws IOException {
		write(Integer.toString(value));
	}

	/**
	 * Write muliple integer values
	 *
	 * @param values
	 *            values to write
	 * @throws IOException
	 *             Error writing to the underlying writer object
	 */
	public void write(final int... values) throws IOException {
		for (final int value : values) {
			write(Integer.toString(value));
		}
	}

	/**
	 * Output a new line and advance the writer to the next line. The line
	 * delimiter is the default for the platform.
	 *
	 * @throws IOException
	 *             Error writing to the underlying writer object
	 */
	public void nextLine() throws IOException {
		delegate.write(NEW_LINE);
		fieldPosition = 0;
	}

	/**
	 * Close the underlying writer object. Once closed all write operations will
	 * fail
	 *
	 * @throws IOException
	 *             Error closing the underlying writer object
	 */
	public void close() throws IOException {
		delegate.close();
	}

	/**
	 * Escapes any occurrences of the quote character in value by replacing it
	 * with a double quote. Also Quotes the value if a quote or delimiter value
	 * is found.
	 *
	 * @param value
	 *            String that needs escaping
	 * @return New string with all values escaped
	 */
	private String escape(final String value) {
		String escapedValue = value;

		// Escape and quote if the source value contains the delimiter
		// or the quote character
		if (value.indexOf(QUOTE) != -1 || value.indexOf(delimiter) != -1) {
			escapedValue = value.replace(QUOTE, ESCAPED_QUOTE);
			escapedValue = QUOTE + escapedValue + QUOTE;
		}

		return escapedValue;
	}
}