Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
CSVWriter |
|
| 4.125;4.125 |
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.math.BigDecimal; | |
21 | import java.sql.*; | |
22 | import java.text.SimpleDateFormat; | |
23 | import java.util.List; | |
24 | ||
25 | /** | |
26 | * A very simple CSV writer released under a commercial-friendly license. | |
27 | * | |
28 | * @author Glen Smith | |
29 | * | |
30 | */ | |
31 | public class CSVWriter implements Closeable { | |
32 | ||
33 | public static final int INITIAL_STRING_SIZE = 128; | |
34 | ||
35 | private Writer rawWriter; | |
36 | ||
37 | private PrintWriter pw; | |
38 | ||
39 | private char separator; | |
40 | ||
41 | private char quotechar; | |
42 | ||
43 | private char escapechar; | |
44 | ||
45 | private String lineEnd; | |
46 | ||
47 | /** The character used for escaping quotes. */ | |
48 | public static final char DEFAULT_ESCAPE_CHARACTER = '"'; | |
49 | ||
50 | /** The default separator to use if none is supplied to the constructor. */ | |
51 | public static final char DEFAULT_SEPARATOR = ','; | |
52 | ||
53 | /** | |
54 | * The default quote character to use if none is supplied to the | |
55 | * constructor. | |
56 | */ | |
57 | public static final char DEFAULT_QUOTE_CHARACTER = '"'; | |
58 | ||
59 | /** The quote constant to use when you wish to suppress all quoting. */ | |
60 | public static final char NO_QUOTE_CHARACTER = '\u0000'; | |
61 | ||
62 | /** The escape constant to use when you wish to suppress all escaping. */ | |
63 | public static final char NO_ESCAPE_CHARACTER = '\u0000'; | |
64 | ||
65 | /** Default line terminator uses platform encoding. */ | |
66 | public static final String DEFAULT_LINE_END = "\n"; | |
67 | ||
68 | /** | |
69 | * Constructs CSVWriter using a comma for the separator. | |
70 | * | |
71 | * @param writer | |
72 | * the writer to an underlying CSV source. | |
73 | */ | |
74 | public CSVWriter(Writer writer) { | |
75 | 0 | this(writer, DEFAULT_SEPARATOR); |
76 | 0 | } |
77 | ||
78 | /** | |
79 | * Constructs CSVWriter with supplied separator. | |
80 | * | |
81 | * @param writer | |
82 | * the writer to an underlying CSV source. | |
83 | * @param separator | |
84 | * the delimiter to use for separating entries. | |
85 | */ | |
86 | public CSVWriter(Writer writer, char separator) { | |
87 | 0 | this(writer, separator, DEFAULT_QUOTE_CHARACTER); |
88 | 0 | } |
89 | ||
90 | /** | |
91 | * Constructs CSVWriter with supplied separator and quote char. | |
92 | * | |
93 | * @param writer | |
94 | * the writer to an underlying CSV source. | |
95 | * @param separator | |
96 | * the delimiter to use for separating entries | |
97 | * @param quotechar | |
98 | * the character to use for quoted elements | |
99 | */ | |
100 | public CSVWriter(Writer writer, char separator, char quotechar) { | |
101 | 0 | this(writer, separator, quotechar, DEFAULT_ESCAPE_CHARACTER); |
102 | 0 | } |
103 | ||
104 | /** | |
105 | * Constructs CSVWriter with supplied separator and quote char. | |
106 | * | |
107 | * @param writer | |
108 | * the writer to an underlying CSV source. | |
109 | * @param separator | |
110 | * the delimiter to use for separating entries | |
111 | * @param quotechar | |
112 | * the character to use for quoted elements | |
113 | * @param escapechar | |
114 | * the character to use for escaping quotechars or escapechars | |
115 | */ | |
116 | public CSVWriter(Writer writer, char separator, char quotechar, char escapechar) { | |
117 | 0 | this(writer, separator, quotechar, escapechar, DEFAULT_LINE_END); |
118 | 0 | } |
119 | ||
120 | ||
121 | /** | |
122 | * Constructs CSVWriter with supplied separator and quote char. | |
123 | * | |
124 | * @param writer | |
125 | * the writer to an underlying CSV source. | |
126 | * @param separator | |
127 | * the delimiter to use for separating entries | |
128 | * @param quotechar | |
129 | * the character to use for quoted elements | |
130 | * @param lineEnd | |
131 | * the line feed terminator to use | |
132 | */ | |
133 | public CSVWriter(Writer writer, char separator, char quotechar, String lineEnd) { | |
134 | 0 | this(writer, separator, quotechar, DEFAULT_ESCAPE_CHARACTER, lineEnd); |
135 | 0 | } |
136 | ||
137 | ||
138 | ||
139 | /** | |
140 | * Constructs CSVWriter with supplied separator, quote char, escape char and line ending. | |
141 | * | |
142 | * @param writer | |
143 | * the writer to an underlying CSV source. | |
144 | * @param separator | |
145 | * the delimiter to use for separating entries | |
146 | * @param quotechar | |
147 | * the character to use for quoted elements | |
148 | * @param escapechar | |
149 | * the character to use for escaping quotechars or escapechars | |
150 | * @param lineEnd | |
151 | * the line feed terminator to use | |
152 | */ | |
153 | 0 | public CSVWriter(Writer writer, char separator, char quotechar, char escapechar, String lineEnd) { |
154 | 0 | this.rawWriter = writer; |
155 | 0 | this.pw = new PrintWriter(writer); |
156 | 0 | this.separator = separator; |
157 | 0 | this.quotechar = quotechar; |
158 | 0 | this.escapechar = escapechar; |
159 | 0 | this.lineEnd = lineEnd; |
160 | 0 | } |
161 | ||
162 | /** | |
163 | * Writes the entire list to a CSV file. The list is assumed to be a | |
164 | * String[] | |
165 | * | |
166 | * @param allLines | |
167 | * a List of String[], with each String[] representing a line of | |
168 | * the file. | |
169 | */ | |
170 | public void writeAll(List<String[]> allLines) { | |
171 | 0 | for (String[] line : allLines) { |
172 | 0 | writeNext(line); |
173 | } | |
174 | 0 | } |
175 | ||
176 | protected void writeColumnNames(ResultSetMetaData metadata) | |
177 | throws SQLException { | |
178 | ||
179 | 0 | int columnCount = metadata.getColumnCount(); |
180 | ||
181 | 0 | String[] nextLine = new String[columnCount]; |
182 | 0 | for (int i = 0; i < columnCount; i++) { |
183 | 0 | nextLine[i] = metadata.getColumnName(i + 1); |
184 | } | |
185 | 0 | writeNext(nextLine); |
186 | 0 | } |
187 | ||
188 | /** | |
189 | * Writes the entire ResultSet to a CSV file. | |
190 | * | |
191 | * The caller is responsible for closing the ResultSet. | |
192 | * | |
193 | * @param rs the recordset to write | |
194 | * @param includeColumnNames true if you want column names in the output, false otherwise | |
195 | * | |
196 | * @throws java.io.IOException thrown by getColumnValue | |
197 | * @throws java.sql.SQLException thrown by getColumnValue | |
198 | */ | |
199 | public void writeAll(java.sql.ResultSet rs, boolean includeColumnNames) throws SQLException, IOException { | |
200 | ||
201 | 0 | ResultSetMetaData metadata = rs.getMetaData(); |
202 | ||
203 | ||
204 | 0 | if (includeColumnNames) { |
205 | 0 | writeColumnNames(metadata); |
206 | } | |
207 | ||
208 | 0 | int columnCount = metadata.getColumnCount(); |
209 | ||
210 | 0 | while (rs.next()) |
211 | { | |
212 | 0 | String[] nextLine = new String[columnCount]; |
213 | ||
214 | 0 | for (int i = 0; i < columnCount; i++) { |
215 | 0 | nextLine[i] = getColumnValue(rs, metadata.getColumnType(i + 1), i + 1); |
216 | } | |
217 | ||
218 | 0 | writeNext(nextLine); |
219 | 0 | } |
220 | 0 | } |
221 | ||
222 | private static String getColumnValue(ResultSet rs, int colType, int colIndex) | |
223 | throws SQLException, IOException { | |
224 | ||
225 | 0 | String value = ""; |
226 | ||
227 | 0 | switch (colType) |
228 | { | |
229 | case Types.BIT: | |
230 | case Types.JAVA_OBJECT: | |
231 | 0 | Object obj = rs.getObject(colIndex); |
232 | 0 | if (obj != null) { |
233 | 0 | value = String.valueOf(obj); |
234 | } | |
235 | break; | |
236 | case Types.BOOLEAN: | |
237 | 0 | boolean b = rs.getBoolean(colIndex); |
238 | 0 | if (!rs.wasNull()) { |
239 | 0 | value = Boolean.valueOf(b).toString(); |
240 | } | |
241 | break; | |
242 | case Types.CLOB: | |
243 | 0 | Clob c = rs.getClob(colIndex); |
244 | 0 | if (c != null) { |
245 | 0 | value = read(c); |
246 | } | |
247 | break; | |
248 | case Types.BIGINT: | |
249 | 0 | long lv = rs.getLong(colIndex); |
250 | 0 | if (!rs.wasNull()) { |
251 | 0 | value = Long.toString(lv); |
252 | } | |
253 | break; | |
254 | case Types.DECIMAL: | |
255 | case Types.DOUBLE: | |
256 | case Types.FLOAT: | |
257 | case Types.REAL: | |
258 | case Types.NUMERIC: | |
259 | 0 | BigDecimal bd = rs.getBigDecimal(colIndex); |
260 | 0 | if (bd != null) { |
261 | 0 | value = bd.toString(); |
262 | } | |
263 | break; | |
264 | case Types.INTEGER: | |
265 | case Types.TINYINT: | |
266 | case Types.SMALLINT: | |
267 | 0 | int intValue = rs.getInt(colIndex); |
268 | 0 | if (!rs.wasNull()) { |
269 | 0 | value = Integer.toString(intValue); |
270 | } | |
271 | break; | |
272 | case Types.DATE: | |
273 | 0 | java.sql.Date date = rs.getDate(colIndex); |
274 | 0 | if (date != null) { |
275 | 0 | SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MMM-yyyy"); |
276 | 0 | value = dateFormat.format(date); |
277 | 0 | } |
278 | break; | |
279 | case Types.TIME: | |
280 | 0 | Time t = rs.getTime(colIndex); |
281 | 0 | if (t != null) { |
282 | 0 | value = t.toString(); |
283 | } | |
284 | break; | |
285 | case Types.TIMESTAMP: | |
286 | 0 | Timestamp tstamp = rs.getTimestamp(colIndex); |
287 | 0 | if (tstamp != null) { |
288 | 0 | SimpleDateFormat timeFormat = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss"); |
289 | 0 | value = timeFormat.format(tstamp); |
290 | 0 | } |
291 | break; | |
292 | case Types.LONGVARCHAR: | |
293 | case Types.VARCHAR: | |
294 | case Types.CHAR: | |
295 | 0 | value = rs.getString(colIndex); |
296 | 0 | break; |
297 | default: | |
298 | 0 | value = ""; |
299 | } | |
300 | ||
301 | ||
302 | 0 | if (value == null) |
303 | { | |
304 | 0 | value = ""; |
305 | } | |
306 | ||
307 | 0 | return value; |
308 | ||
309 | } | |
310 | ||
311 | private static String read(Clob c) throws SQLException, IOException | |
312 | { | |
313 | 0 | StringBuilder sb = new StringBuilder( (int) c.length()); |
314 | 0 | Reader r = c.getCharacterStream(); |
315 | 0 | char[] cbuf = new char[2048]; |
316 | int n; | |
317 | 0 | while ((n = r.read(cbuf, 0, cbuf.length)) != -1) { |
318 | 0 | if (n > 0) { |
319 | 0 | sb.append(cbuf, 0, n); |
320 | } | |
321 | } | |
322 | 0 | return sb.toString(); |
323 | } | |
324 | ||
325 | /** | |
326 | * Writes the next line to the file. | |
327 | * | |
328 | * @param nextLine | |
329 | * a string array with each comma-separated element as a separate | |
330 | * entry. | |
331 | */ | |
332 | public void writeNext(String[] nextLine) { | |
333 | ||
334 | 0 | if (nextLine == null) |
335 | 0 | return; |
336 | ||
337 | 0 | StringBuilder sb = new StringBuilder(INITIAL_STRING_SIZE); |
338 | 0 | for (int i = 0; i < nextLine.length; i++) { |
339 | ||
340 | 0 | if (i != 0) { |
341 | 0 | sb.append(separator); |
342 | } | |
343 | ||
344 | 0 | String nextElement = nextLine[i]; |
345 | 0 | if (nextElement == null) |
346 | 0 | continue; |
347 | 0 | if (quotechar != NO_QUOTE_CHARACTER) |
348 | 0 | sb.append(quotechar); |
349 | ||
350 | 0 | sb.append(stringContainsSpecialCharacters(nextElement) ? processLine(nextElement) : nextElement); |
351 | ||
352 | 0 | if (quotechar != NO_QUOTE_CHARACTER) |
353 | 0 | sb.append(quotechar); |
354 | } | |
355 | ||
356 | 0 | sb.append(lineEnd); |
357 | 0 | pw.write(sb.toString()); |
358 | ||
359 | 0 | } |
360 | ||
361 | private boolean stringContainsSpecialCharacters(String line) { | |
362 | 0 | return line.indexOf(quotechar) != -1 || line.indexOf(escapechar) != -1; |
363 | } | |
364 | ||
365 | protected StringBuilder processLine(String nextElement) | |
366 | { | |
367 | 0 | StringBuilder sb = new StringBuilder(INITIAL_STRING_SIZE); |
368 | 0 | for (int j = 0; j < nextElement.length(); j++) { |
369 | 0 | char nextChar = nextElement.charAt(j); |
370 | 0 | if (escapechar != NO_ESCAPE_CHARACTER && nextChar == quotechar) { |
371 | 0 | sb.append(escapechar).append(nextChar); |
372 | 0 | } else if (escapechar != NO_ESCAPE_CHARACTER && nextChar == escapechar) { |
373 | 0 | sb.append(escapechar).append(nextChar); |
374 | } else { | |
375 | 0 | sb.append(nextChar); |
376 | } | |
377 | } | |
378 | ||
379 | 0 | return sb; |
380 | } | |
381 | ||
382 | /** | |
383 | * Flush underlying stream to writer. | |
384 | * | |
385 | * @throws IOException if bad things happen | |
386 | */ | |
387 | public void flush() throws IOException { | |
388 | ||
389 | 0 | pw.flush(); |
390 | ||
391 | 0 | } |
392 | ||
393 | /** | |
394 | * Close the underlying stream writer flushing any buffered content. | |
395 | * | |
396 | * @throws IOException if bad things happen | |
397 | * | |
398 | */ | |
399 | public void close() throws IOException { | |
400 | 0 | flush(); |
401 | 0 | pw.close(); |
402 | 0 | rawWriter.close(); |
403 | 0 | } |
404 | ||
405 | } |