DelimitedWriter.java
1/*******************************************************************************
2 * Copyright (c) 2009, 2010 Mountainminds GmbH & Co. KG and Contributors
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("header1", "header2", ...);
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}