1 package au.com.bytecode.opencsv;
2
3 /**
4 Copyright 2005 Bytecode Pty Ltd.
5
6 Licensed under the Apache License, Version 2.0 (the "License");
7 you may not use this file except in compliance with the License.
8 You may obtain a copy of the License at
9
10 http://www.apache.org/licenses/LICENSE-2.0
11
12 Unless required by applicable law or agreed to in writing, software
13 distributed under the License is distributed on an "AS IS" BASIS,
14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 See the License for the specific language governing permissions and
16 limitations under the License.
17 */
18
19 import java.io.*;
20 import java.sql.ResultSet;
21 import java.sql.SQLException;
22 import java.util.List;
23
24 /**
25 * A very simple CSV writer released under a commercial-friendly license.
26 *
27 * @author Glen Smith
28 */
29 public class CSVWriter implements Closeable, Flushable {
30
31 public static final int INITIAL_STRING_SIZE = 128;
32
33 private Writer rawWriter;
34
35 private PrintWriter pw;
36
37 private char separator;
38
39 private char quotechar;
40
41 private char escapechar;
42
43 private String lineEnd;
44
45 /**
46 * The character used for escaping quotes.
47 */
48 public static final char DEFAULT_ESCAPE_CHARACTER = '"';
49
50 /**
51 * The default separator to use if none is supplied to the constructor.
52 */
53 public static final char DEFAULT_SEPARATOR = ',';
54
55 /**
56 * The default quote character to use if none is supplied to the
57 * constructor.
58 */
59 public static final char DEFAULT_QUOTE_CHARACTER = '"';
60
61 /**
62 * The quote constant to use when you wish to suppress all quoting.
63 */
64 public static final char NO_QUOTE_CHARACTER = '\u0000';
65
66 /**
67 * The escape constant to use when you wish to suppress all escaping.
68 */
69 public static final char NO_ESCAPE_CHARACTER = '\u0000';
70
71 /**
72 * Default line terminator uses platform encoding.
73 */
74 public static final String DEFAULT_LINE_END = "\n";
75
76 private ResultSetHelper resultService = new ResultSetHelperService();
77
78 /**
79 * Constructs CSVWriter using a comma for the separator.
80 *
81 * @param writer the writer to an underlying CSV source.
82 */
83 public CSVWriter(Writer writer) {
84 this(writer, DEFAULT_SEPARATOR);
85 }
86
87 /**
88 * Constructs CSVWriter with supplied separator.
89 *
90 * @param writer the writer to an underlying CSV source.
91 * @param separator the delimiter to use for separating entries.
92 */
93 public CSVWriter(Writer writer, char separator) {
94 this(writer, separator, DEFAULT_QUOTE_CHARACTER);
95 }
96
97 /**
98 * Constructs CSVWriter with supplied separator and quote char.
99 *
100 * @param writer the writer to an underlying CSV source.
101 * @param separator the delimiter to use for separating entries
102 * @param quotechar the character to use for quoted elements
103 */
104 public CSVWriter(Writer writer, char separator, char quotechar) {
105 this(writer, separator, quotechar, DEFAULT_ESCAPE_CHARACTER);
106 }
107
108 /**
109 * Constructs CSVWriter with supplied separator and quote char.
110 *
111 * @param writer the writer to an underlying CSV source.
112 * @param separator the delimiter to use for separating entries
113 * @param quotechar the character to use for quoted elements
114 * @param escapechar the character to use for escaping quotechars or escapechars
115 */
116 public CSVWriter(Writer writer, char separator, char quotechar, char escapechar) {
117 this(writer, separator, quotechar, escapechar, DEFAULT_LINE_END);
118 }
119
120
121 /**
122 * Constructs CSVWriter with supplied separator and quote char.
123 *
124 * @param writer the writer to an underlying CSV source.
125 * @param separator the delimiter to use for separating entries
126 * @param quotechar the character to use for quoted elements
127 * @param lineEnd the line feed terminator to use
128 */
129 public CSVWriter(Writer writer, char separator, char quotechar, String lineEnd) {
130 this(writer, separator, quotechar, DEFAULT_ESCAPE_CHARACTER, lineEnd);
131 }
132
133
134 /**
135 * Constructs CSVWriter with supplied separator, quote char, escape char and line ending.
136 *
137 * @param writer the writer to an underlying CSV source.
138 * @param separator the delimiter to use for separating entries
139 * @param quotechar the character to use for quoted elements
140 * @param escapechar the character to use for escaping quotechars or escapechars
141 * @param lineEnd the line feed terminator to use
142 */
143 public CSVWriter(Writer writer, char separator, char quotechar, char escapechar, String lineEnd) {
144 this.rawWriter = writer;
145 this.pw = new PrintWriter(writer);
146 this.separator = separator;
147 this.quotechar = quotechar;
148 this.escapechar = escapechar;
149 this.lineEnd = lineEnd;
150 }
151
152 /**
153 * Writes the entire list to a CSV file. The list is assumed to be a
154 * String[]
155 *
156 * @param allLines a List of String[], with each String[] representing a line of
157 * the file.
158 * @param applyQuotesToAll true if all values are to be quoted. false if quotes only
159 * to be applied to values which contain the separator, escape,
160 * quote or new line characters.
161 */
162 public void writeAll(List<String[]> allLines, boolean applyQuotesToAll) {
163 for (String[] line : allLines) {
164 writeNext(line, applyQuotesToAll);
165 }
166 }
167
168 /**
169 * Writes the entire list to a CSV file. The list is assumed to be a
170 * String[]
171 *
172 * @param allLines a List of String[], with each String[] representing a line of
173 * the file.
174 */
175 public void writeAll(List<String[]> allLines) {
176 for (String[] line : allLines) {
177 writeNext(line);
178 }
179 }
180
181 protected void writeColumnNames(ResultSet rs)
182 throws SQLException {
183
184 writeNext(resultService.getColumnNames(rs));
185 }
186
187 /**
188 * Writes the entire ResultSet to a CSV file.
189 * <p/>
190 * The caller is responsible for closing the ResultSet.
191 *
192 * @param rs the recordset to write
193 * @param includeColumnNames true if you want column names in the output, false otherwise
194 * @throws java.io.IOException thrown by getColumnValue
195 * @throws java.sql.SQLException thrown by getColumnValue
196 */
197 public void writeAll(java.sql.ResultSet rs, boolean includeColumnNames) throws SQLException, IOException {
198 writeAll(rs, includeColumnNames, false);
199 }
200
201 /**
202 * Writes the entire ResultSet to a CSV file.
203 * <p/>
204 * The caller is responsible for closing the ResultSet.
205 *
206 * @throws java.io.IOException thrown by getColumnValue
207 * @throws java.sql.SQLException thrown by getColumnValue
208 */
209 public void writeAll(java.sql.ResultSet rs, boolean includeColumnNames, boolean trim) throws SQLException, IOException {
210
211
212 if (includeColumnNames) {
213 writeColumnNames(rs);
214 }
215
216 while (rs.next()) {
217 writeNext(resultService.getColumnValues(rs, trim));
218 }
219 }
220
221 /**
222 * Writes the next line to the file.
223 *
224 * @param nextLine a string array with each comma-separated element as a separate
225 * entry.
226 * @param applyQuotesToAll true if all values are to be quoted. false applies quotes only
227 * to values which contain the separator, escape, quote or new line characters.
228 */
229 public void writeNext(String[] nextLine, boolean applyQuotesToAll) {
230
231 if (nextLine == null)
232 return;
233
234 StringBuilder sb = new StringBuilder(INITIAL_STRING_SIZE);
235 for (int i = 0; i < nextLine.length; i++) {
236
237 if (i != 0) {
238 sb.append(separator);
239 }
240
241 String nextElement = nextLine[i];
242
243 if (nextElement == null)
244 continue;
245
246 Boolean stringContainsSpecialCharacters = stringContainsSpecialCharacters(nextElement);
247
248 if ((applyQuotesToAll || stringContainsSpecialCharacters) && quotechar != NO_QUOTE_CHARACTER)
249 sb.append(quotechar);
250
251 if (stringContainsSpecialCharacters) {
252 sb.append(processLine(nextElement));
253 } else {
254 sb.append(nextElement);
255 }
256
257 if ((applyQuotesToAll || stringContainsSpecialCharacters) && quotechar != NO_QUOTE_CHARACTER)
258 sb.append(quotechar);
259 }
260
261 sb.append(lineEnd);
262 pw.write(sb.toString());
263 }
264
265 /**
266 * Writes the next line to the file.
267 *
268 * @param nextLine a string array with each comma-separated element as a separate
269 * entry.
270 */
271 public void writeNext(String[] nextLine) {
272 writeNext(nextLine, true);
273 }
274
275 private boolean stringContainsSpecialCharacters(String line) {
276 return line.indexOf(quotechar) != -1 || line.indexOf(escapechar) != -1 || line.indexOf(separator) != -1 || line.indexOf("\n") != -1 || line.indexOf("\r") != -1;
277 }
278
279 protected StringBuilder processLine(String nextElement) {
280 StringBuilder sb = new StringBuilder(INITIAL_STRING_SIZE);
281 for (int j = 0; j < nextElement.length(); j++) {
282 char nextChar = nextElement.charAt(j);
283 if (escapechar != NO_ESCAPE_CHARACTER && nextChar == quotechar) {
284 sb.append(escapechar).append(nextChar);
285 } else if (escapechar != NO_ESCAPE_CHARACTER && nextChar == escapechar) {
286 sb.append(escapechar).append(nextChar);
287 } else {
288 sb.append(nextChar);
289 }
290 }
291
292 return sb;
293 }
294
295 /**
296 * Flush underlying stream to writer.
297 *
298 * @throws IOException if bad things happen
299 */
300 public void flush() throws IOException {
301
302 pw.flush();
303
304 }
305
306 /**
307 * Close the underlying stream writer flushing any buffered content.
308 *
309 * @throws IOException if bad things happen
310 */
311 public void close() throws IOException {
312 flush();
313 pw.close();
314 rawWriter.close();
315 }
316
317 /**
318 * Checks to see if the there has been an error in the printstream.
319 */
320 public boolean checkError() {
321 return pw.checkError();
322 }
323
324 public void setResultService(ResultSetHelper resultService) {
325 this.resultService = resultService;
326 }
327
328 public void flushQuietly() {
329 try {
330 flush();
331 } catch (IOException e) {
332 // catch exception and ignore.
333 }
334 }
335 }