View Javadoc

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          this(writer, DEFAULT_SEPARATOR);
76      }
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          this(writer, separator, DEFAULT_QUOTE_CHARACTER);
88      }
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     	this(writer, separator, quotechar, DEFAULT_ESCAPE_CHARACTER);
102     }
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         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
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         this(writer, separator, quotechar, DEFAULT_ESCAPE_CHARACTER, lineEnd);
135     }   
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     public CSVWriter(Writer writer, char separator, char quotechar, char escapechar, String lineEnd) {
154         this.rawWriter = writer;
155         this.pw = new PrintWriter(writer);
156         this.separator = separator;
157         this.quotechar = quotechar;
158         this.escapechar = escapechar;
159         this.lineEnd = lineEnd;
160     }
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     	for (String[] line : allLines) {
172 			writeNext(line);
173 		}
174     }
175 
176     protected void writeColumnNames(ResultSetMetaData metadata)
177     	throws SQLException {
178     	
179     	int columnCount =  metadata.getColumnCount();
180     	
181     	String[] nextLine = new String[columnCount];
182 		for (int i = 0; i < columnCount; i++) {
183 			nextLine[i] = metadata.getColumnName(i + 1);
184 		}
185     	writeNext(nextLine);
186     }
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     	ResultSetMetaData metadata = rs.getMetaData();
202     	
203     	
204     	if (includeColumnNames) {
205 			writeColumnNames(metadata);
206 		}
207 
208     	int columnCount =  metadata.getColumnCount();
209     	
210     	while (rs.next())
211     	{
212         	String[] nextLine = new String[columnCount];
213         	
214         	for (int i = 0; i < columnCount; i++) {
215 				nextLine[i] = getColumnValue(rs, metadata.getColumnType(i + 1), i + 1);
216 			}
217         	
218     		writeNext(nextLine);
219     	}
220     }
221     
222     private static String getColumnValue(ResultSet rs, int colType, int colIndex)
223     		throws SQLException, IOException {
224 
225     	String value = "";
226     	
227 		switch (colType)
228 		{
229 			case Types.BIT:
230             case Types.JAVA_OBJECT:
231 				Object obj = rs.getObject(colIndex);
232 				if (obj != null) {
233 					value = String.valueOf(obj);
234 				}
235 			break;
236 			case Types.BOOLEAN:
237 				boolean b = rs.getBoolean(colIndex);
238 				if (!rs.wasNull()) {
239 					value = Boolean.valueOf(b).toString();
240 				}
241 			break;
242 			case Types.CLOB:
243 				Clob c = rs.getClob(colIndex);
244 				if (c != null) {
245 					value = read(c);
246 				}
247 			break;
248 			case Types.BIGINT:
249 				long lv = rs.getLong(colIndex);
250 				if (!rs.wasNull()) {
251 					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 				BigDecimal bd = rs.getBigDecimal(colIndex);
260 				if (bd != null) {
261 					value = bd.toString();
262 				}
263 			break;
264 			case Types.INTEGER:
265 			case Types.TINYINT:
266 			case Types.SMALLINT:
267 				int intValue = rs.getInt(colIndex);
268 				if (!rs.wasNull()) {
269 					value = Integer.toString(intValue);
270 				}
271 			break;
272 			case Types.DATE:
273 				java.sql.Date date = rs.getDate(colIndex);
274 				if (date != null) {
275 				    SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MMM-yyyy");
276 					value = dateFormat.format(date);
277 				}
278 			break;
279 			case Types.TIME:
280 				Time t = rs.getTime(colIndex);
281 				if (t != null) {
282 					value = t.toString();
283 				}
284 			break;
285 			case Types.TIMESTAMP:
286 				Timestamp tstamp = rs.getTimestamp(colIndex);
287 				if (tstamp != null) {
288 					SimpleDateFormat timeFormat = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss");
289 					value = timeFormat.format(tstamp);
290 				}
291 			break;
292 			case Types.LONGVARCHAR:
293 			case Types.VARCHAR:
294 			case Types.CHAR:
295 				value = rs.getString(colIndex);
296 			break;
297 			default:
298 				value = "";
299 		}
300 
301 		
302 		if (value == null)
303 		{
304 			value = "";
305 		}
306 		
307 		return value;
308     	
309     }
310 
311 	private static String read(Clob c) throws SQLException, IOException
312 	{
313 		StringBuilder sb = new StringBuilder( (int) c.length());
314 		Reader r = c.getCharacterStream();
315 		char[] cbuf = new char[2048];
316 		int n;
317 		while ((n = r.read(cbuf, 0, cbuf.length)) != -1) {
318 			if (n > 0) {
319 				sb.append(cbuf, 0, n);
320 			}
321 		}
322 		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     	if (nextLine == null)
335     		return;
336     	
337         StringBuilder sb = new StringBuilder(INITIAL_STRING_SIZE);
338         for (int i = 0; i < nextLine.length; i++) {
339 
340             if (i != 0) {
341                 sb.append(separator);
342             }
343 
344             String nextElement = nextLine[i];
345             if (nextElement == null)
346                 continue;
347             if (quotechar !=  NO_QUOTE_CHARACTER)
348             	sb.append(quotechar);
349             
350             sb.append(stringContainsSpecialCharacters(nextElement) ? processLine(nextElement) : nextElement);
351 
352             if (quotechar != NO_QUOTE_CHARACTER)
353             	sb.append(quotechar);
354         }
355         
356         sb.append(lineEnd);
357         pw.write(sb.toString());
358 
359     }
360 
361 	private boolean stringContainsSpecialCharacters(String line) {
362 	    return line.indexOf(quotechar) != -1 || line.indexOf(escapechar) != -1;
363     }
364 
365 	protected StringBuilder processLine(String nextElement)
366     {
367 		StringBuilder sb = new StringBuilder(INITIAL_STRING_SIZE);
368 	    for (int j = 0; j < nextElement.length(); j++) {
369 	        char nextChar = nextElement.charAt(j);
370 	        if (escapechar != NO_ESCAPE_CHARACTER && nextChar == quotechar) {
371 	        	sb.append(escapechar).append(nextChar);
372 	        } else if (escapechar != NO_ESCAPE_CHARACTER && nextChar == escapechar) {
373 	        	sb.append(escapechar).append(nextChar);
374 	        } else {
375 	            sb.append(nextChar);
376 	        }
377 	    }
378 	    
379 	    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         pw.flush();
390 
391     } 
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         flush();
401         pw.close();
402         rawWriter.close();
403     }
404 
405 }