KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > Ostermiller > util > CSVPrinter


1 /*
2  * Write files in comma separated value format.
3  * Copyright (C) 2001-2004 Stephen Ostermiller
4  * http://ostermiller.org/contact.pl?regarding=Java+Utilities
5  * Copyright (C) 2003 Pierre Dittgen <pierre dot dittgen at pass-tech dot fr>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * See COPYING.TXT for details.
18  */

19
20 package com.Ostermiller.util;
21 import java.io.*;
22
23 /**
24  * Print values as a comma separated list.
25  * More information about this class is available from <a target="_top" HREF=
26  * "http://ostermiller.org/utils/CSV.html">ostermiller.org</a>.
27  *
28  * @author Stephen Ostermiller http://ostermiller.org/contact.pl?regarding=Java+Utilities
29  * @author Pierre Dittgen <pierre dot dittgen at pass-tech dot fr>
30  * @since ostermillerutils 1.00.00
31  */

32 public class CSVPrinter implements CSVPrint {
33
34     /**
35      * If auto flushing is enabled.
36      *
37      * @since ostermillerutils 1.02.26
38      */

39     protected boolean autoFlush = true;
40
41     /**
42      * If auto flushing is enabled.
43      *
44      * @since ostermillerutils 1.02.26
45      */

46     protected boolean alwaysQuote = false;
47
48     /**
49      * true iff an error has occurred.
50      *
51      * @since ostermillerutils 1.02.26
52      */

53     protected boolean error = false;
54
55     /**
56      * Delimiter character written.
57      *
58      * @since ostermillerutils 1.02.18
59      */

60     protected char delimiterChar = ',';
61
62     /**
63      * Quoting character written.
64      *
65      * @since ostermillerutils 1.02.18
66      */

67     protected char quoteChar = '"';
68
69     /**
70      * The place that the values get written.
71      *
72      * @since ostermillerutils 1.00.00
73      */

74     protected Writer out;
75
76     /**
77      * True iff we just began a new line.
78      *
79      * @since ostermillerutils 1.00.00
80      */

81     protected boolean newLine = true;
82
83     /**
84      * Character used to start comments. (Default is '#')
85      *
86      * @since ostermillerutils 1.00.00
87      */

88     protected char commentStart = '#';
89
90     /**
91      * Change this printer so that it uses a new delimiter.
92      *
93      * @param newDelimiter The new delimiter character to use.
94      * @throws BadDelimiterException if the character cannot be used as a delimiter.
95      *
96      * @author Pierre Dittgen <pierre dot dittgen at pass-tech dot fr>
97      * @since ostermillerutils 1.02.18
98      */

99     public void changeDelimiter(char newDelimiter) throws BadDelimiterException {
100         if (delimiterChar == newDelimiter) return; // no need to do anything.
101
if (newDelimiter == '\n' || newDelimiter == '\r' ||
102                 newDelimiter == delimiterChar || newDelimiter == quoteChar){
103             throw new BadDelimiterException();
104         }
105         delimiterChar = newDelimiter;
106     }
107
108     /**
109      * Change this printer so that it uses a new character for quoting.
110      *
111      * @param newQuote The new character to use for quoting.
112      * @throws BadQuoteException if the character cannot be used as a quote.
113      *
114      * @author Pierre Dittgen <pierre dot dittgen at pass-tech dot fr>
115      * @since ostermillerutils 1.02.18
116      */

117     public void changeQuote(char newQuote) throws BadQuoteException {
118         if (newQuote == '\n' || newQuote == '\r' ||
119                 newQuote == delimiterChar || newQuote == quoteChar){
120             throw new BadQuoteException();
121         }
122         quoteChar = newQuote;
123     }
124
125     /**
126      * Create a printer that will print values to the given
127      * stream. Character to byte conversion is done using
128      * the default character encoding. Comments will be
129      * written using the default comment character '#', the delimiter will
130      * be the comma, the quote character will be double quotes,
131      * quotes will be used when needed, and auto flushing
132      * will be enabled.
133      *
134      * @param out stream to which to print.
135      *
136      * @since ostermillerutils 1.00.00
137      */

138     public CSVPrinter(OutputStream out){
139         this.out = new OutputStreamWriter(out);
140     }
141
142     /**
143      * Create a printer that will print values to the given
144      * stream. Comments will be
145      * written using the default comment character '#', the delimiter will
146      * be the comma, the quote character will be double quotes,
147      * quotes will be used when needed, and auto flushing
148      * will be enabled.
149      *
150      * @param out stream to which to print.
151      *
152      * @since ostermillerutils 1.00.00
153      */

154     public CSVPrinter(Writer out){
155         this.out = out;
156     }
157
158     /**
159      * Create a printer that will print values to the given
160      * stream. Character to byte conversion is done using
161      * the default character encoding. The delimiter will
162      * be the comma, the quote character will be double quotes,
163      * quotes will be used when needed, and auto flushing
164      * will be enabled.
165      *
166      * @param out stream to which to print.
167      * @param commentStart Character used to start comments.
168      *
169      * @since ostermillerutils 1.00.00
170      */

171     public CSVPrinter(OutputStream out, char commentStart){
172         this(out);
173         this.commentStart = commentStart;
174     }
175
176     /**
177      * Create a printer that will print values to the given
178      * stream. The delimiter will
179      * be the comma, the quote character will be double quotes,
180      * quotes will be used when needed, and auto flushing
181      * will be enabled.
182      *
183      * @param out stream to which to print.
184      * @param commentStart Character used to start comments.
185      *
186      * @since ostermillerutils 1.00.00
187      */

188     public CSVPrinter(Writer out, char commentStart){
189         this(out);
190         this.commentStart = commentStart;
191     }
192
193     /**
194      * Create a printer that will print values to the given
195      * stream. The comment character will be the number sign, the delimiter will
196      * be the comma, and the quote character will be double quotes.
197      *
198      * @param out stream to which to print.
199      * @param alwaysQuote true if quotes should be used even when not strictly needed.
200      * @param autoFlush should auto flushing be enabled.
201      *
202      * @since ostermillerutils 1.02.26
203      */

204     public CSVPrinter(Writer out, boolean alwaysQuote, boolean autoFlush){
205         this.out = out;
206         setAlwaysQuote(alwaysQuote);
207         setAutoFlush(autoFlush);
208     }
209
210     /**
211      * Create a printer that will print values to the given
212      * stream. Quotes will be used when needed, and auto flushing
213      * will be enabled.
214      *
215      * @param out stream to which to print.
216      * @param commentStart Character used to start comments.
217      * @param delimiter The new delimiter character to use.
218      * @param quote The new character to use for quoting.
219      * @throws BadQuoteException if the character cannot be used as a quote.
220      * @throws BadDelimiterException if the character cannot be used as a delimiter.
221      *
222      * @since ostermillerutils 1.02.26
223      */

224     public CSVPrinter(Writer out, char commentStart, char quote, char delimiter) throws BadDelimiterException, BadQuoteException {
225         this.out = out;
226         this.commentStart = commentStart;
227         changeQuote(quote);
228         changeDelimiter(delimiter);
229     }
230
231     /**
232      * Create a printer that will print values to the given
233      * stream.
234      *
235      * @param out stream to which to print.
236      * @param commentStart Character used to start comments.
237      * @param delimiter The new delimiter character to use.
238      * @param quote The new character to use for quoting.
239      * @param alwaysQuote true if quotes should be used even when not strictly needed.
240      * @param autoFlush should auto flushing be enabled.
241      * @throws BadQuoteException if the character cannot be used as a quote.
242      * @throws BadDelimiterException if the character cannot be used as a delimiter.
243      *
244      * @since ostermillerutils 1.02.26
245      */

246     public CSVPrinter(Writer out, char commentStart, char quote, char delimiter, boolean alwaysQuote, boolean autoFlush) throws BadDelimiterException, BadQuoteException {
247         this.out = out;
248         this.commentStart = commentStart;
249         changeQuote(quote);
250         changeDelimiter(delimiter);
251         setAlwaysQuote(alwaysQuote);
252         setAutoFlush(autoFlush);
253     }
254
255     /**
256      * Print the string as the last value on the line. The value
257      * will be quoted if needed.
258      * <p>
259      * This method never throws an I/O exception. The client may inquire as to whether
260      * any errors have occurred by invoking checkError(). If an I/O Exception is
261      * desired, the client should use the corresponding writeln method.
262      *
263      * @param value value to be outputted.
264      *
265      * @since ostermillerutils 1.00.00
266      */

267     public void println(String JavaDoc value){
268         try {
269             writeln(value);
270         } catch (IOException iox){
271             error = true;
272         }
273     }
274
275     /**
276      * Print the string as the last value on the line. The value
277      * will be quoted if needed.
278      *
279      * @param value value to be outputted.
280      * @throws IOException if an error occurs while writing.
281      *
282      * @since ostermillerutils 1.02.26
283      */

284     public void writeln(String JavaDoc value) throws IOException {
285         try {
286             write(value);
287             writeln();
288         } catch (IOException iox){
289             error = true;
290             throw iox;
291         }
292     }
293
294     /**
295      * Output a blank line.
296      * <p>
297      * This method never throws an I/O exception. The client may inquire as to whether
298      * any errors have occurred by invoking checkError(). If an I/O Exception is
299      * desired, the client should use the corresponding writeln method.
300      *
301      * @since ostermillerutils 1.00.00
302      */

303     public void println(){
304         try {
305             writeln();
306         } catch (IOException iox){
307             error = true;
308         }
309     }
310
311     /**
312      * Output a blank line.
313      *
314      * @throws IOException if an error occurs while writing.
315      *
316      * @since ostermillerutils 1.02.26
317      */

318     public void writeln() throws IOException {
319         try {
320             out.write("\n");
321             if (autoFlush) flush();
322             newLine = true;
323         } catch (IOException iox){
324             error = true;
325             throw iox;
326         }
327     }
328
329     /**
330      * Print a single line of comma separated values.
331      * The values will be quoted if needed. Quotes and
332      * and other characters that need it will be escaped.
333      * <p>
334      * This method never throws an I/O exception. The client may inquire as to whether
335      * any errors have occurred by invoking checkError(). If an I/O Exception is
336      * desired, the client should use the corresponding writeln method.
337      *
338      * @param values values to be outputted.
339      *
340      * @since ostermillerutils 1.00.00
341      */

342     public void println(String JavaDoc[] values){
343         try {
344             writeln(values);
345         } catch (IOException iox){
346             error = true;
347         }
348     }
349
350     /**
351      * Print a single line of comma separated values.
352      * The values will be quoted if needed. Quotes and
353      * and other characters that need it will be escaped.
354      *
355      * @param values values to be outputted.
356      * @throws IOException if an error occurs while writing.
357      *
358      * @since ostermillerutils 1.02.26
359      */

360     public void writeln(String JavaDoc[] values) throws IOException {
361         try {
362             print(values);
363             writeln();
364         } catch (IOException iox){
365             error = true;
366             throw iox;
367         }
368     }
369
370     /**
371      * Print a single line of comma separated values.
372      * The values will be quoted if needed. Quotes and
373      * and other characters that need it will be escaped.
374      * <p>
375      * This method never throws an I/O exception. The client may inquire as to whether
376      * any errors have occurred by invoking checkError(). If an I/O Exception is
377      * desired, the client should use the corresponding writeln method.
378      *
379      * @param values values to be outputted.
380      *
381      * @since ostermillerutils 1.00.00
382      */

383     public void print(String JavaDoc[] values){
384         try {
385             write(values);
386         } catch (IOException iox){
387             error = true;
388         }
389     }
390
391     /**
392      * Print a single line of comma separated values.
393      * The values will be quoted if needed. Quotes and
394      * and other characters that need it will be escaped.
395      *
396      * @param values values to be outputted.
397      * @throws IOException if an error occurs while writing.
398      *
399      * @since ostermillerutils 1.02.26
400      */

401     public void write(String JavaDoc[] values) throws IOException {
402         try {
403             for (int i=0; i<values.length; i++){
404                 write(values[i]);
405             }
406         } catch (IOException iox){
407             error = true;
408             throw iox;
409         }
410     }
411
412     /**
413      * Print several lines of comma separated values.
414      * The values will be quoted if needed. Quotes and
415      * newLine characters will be escaped.
416      * <p>
417      * This method never throws an I/O exception. The client may inquire as to whether
418      * any errors have occurred by invoking checkError(). If an I/O Exception is
419      * desired, the client should use the corresponding writeln method.
420      *
421      * @param values values to be outputted.
422      *
423      * @since ostermillerutils 1.00.00
424      */

425     public void println(String JavaDoc[][] values){
426         try {
427             writeln(values);
428         } catch (IOException iox){
429             error = true;
430         }
431     }
432
433     /**
434      * Print several lines of comma separated values.
435      * The values will be quoted if needed. Quotes and
436      * newLine characters will be escaped.
437      *
438      * @param values values to be outputted.
439      * @throws IOException if an error occurs while writing.
440      *
441      * @since ostermillerutils 1.02.26
442      */

443     public void writeln(String JavaDoc[][] values) throws IOException {
444         try {
445             for (int i=0; i<values.length; i++){
446                 writeln(values[i]);
447             }
448             if (values.length == 0){
449                 writeln();
450             }
451         } catch (IOException iox){
452             error = true;
453             throw iox;
454         }
455     }
456
457     /**
458      * Put a comment among the comma separated values.
459      * Comments will always begin on a new line and occupy a
460      * least one full line. The character specified to star
461      * comments and a space will be inserted at the beginning of
462      * each new line in the comment. If the comment is null,
463      * an empty comment is outputted.
464      * <p>
465      * This method never throws an I/O exception. The client may inquire as to whether
466      * any errors have occurred by invoking checkError(). If an I/O Exception is
467      * desired, the client should use the corresponding writelnComment method.
468      *
469      * @param comment the comment to output.
470      *
471      * @since ostermillerutils 1.00.00
472      */

473     public void printlnComment(String JavaDoc comment){
474         try {
475             writelnComment(comment);
476         } catch (IOException iox){
477             error = true;
478         }
479     }
480
481     /**
482      * Put a comment among the comma separated values.
483      * Comments will always begin on a new line and occupy a
484      * least one full line. The character specified to star
485      * comments and a space will be inserted at the beginning of
486      * each new line in the comment. If the comment is null,
487      * an empty comment is outputted.
488      *
489      * @param comment the comment to output.
490      * @throws IOException if an error occurs while writing.
491      *
492      * @since ostermillerutils 1.02.26
493      */

494     public void writelnComment(String JavaDoc comment) throws IOException {
495         try {
496             if (comment==null) comment = "";
497             if (!newLine){
498                 writeln();
499             }
500             out.write(commentStart);
501             out.write(' ');
502             for (int i=0; i<comment.length(); i++){
503                 char c = comment.charAt(i);
504                 switch (c){
505                     case '\r': {
506                         if (i+1 < comment.length() && comment.charAt(i+1) == '\n'){
507                             i++;
508                         }
509                     } //break intentionally excluded.
510
case '\n': {
511                         writeln();
512                         out.write(commentStart);
513                         out.write(' ');
514                     } break;
515                     default: {
516                         out.write(c);
517                     } break;
518                 }
519             }
520             writeln();
521         } catch (IOException iox){
522             error = true;
523             throw iox;
524         }
525     }
526
527     /**
528      * Print the string as the next value on the line. The value
529      * will be quoted if needed. If value is null, an empty value is printed.
530      * <p>
531      * This method never throws an I/O exception. The client may inquire as to whether
532      * any errors have occurred by invoking checkError(). If an I/O Exception is
533      * desired, the client should use the corresponding println method.
534      *
535      * @param value value to be outputted.
536      *
537      * @since ostermillerutils 1.00.00
538      */

539     public void print(String JavaDoc value){
540         try {
541             write(value);
542         } catch (IOException iox){
543             error = true;
544         }
545     }
546
547     /**
548      * Print the string as the next value on the line. The value
549      * will be quoted if needed. If value is null, an empty value is printed.
550      *
551      * @param value value to be outputted.
552      * @throws IOException if an error occurs while writing.
553      *
554      * @since ostermillerutils 1.02.26
555      */

556     public void write(String JavaDoc value) throws IOException {
557         try {
558             if (value == null) value = "";
559             boolean quote = false;
560             if (alwaysQuote){
561                 quote = true;
562             } else if (value.length() > 0){
563                 char c = value.charAt(0);
564                 if (newLine && (c<'0' || (c>'9' && c<'A') || (c>'Z' && c<'a') || (c>'z'))){
565                     quote = true;
566                 }
567                 if (c==' ' || c=='\f' || c=='\t'){
568                     quote = true;
569                 }
570                 for (int i=0; i<value.length(); i++){
571                     c = value.charAt(i);
572                     if (c==quoteChar || c==delimiterChar || c=='\n' || c=='\r'){
573                         quote = true;
574                     }
575                 }
576                 if (c==' ' || c=='\f' || c=='\t'){
577                     quote = true;
578                 }
579             } else if (newLine) {
580                 // always quote an empty token that is the first
581
// on the line, as it may be the only thing on the
582
// line. If it were not quoted in that case,
583
// an empty line has no tokens.
584
quote = true;
585             }
586             if (newLine){
587                 newLine = false;
588             } else {
589                 out.write(delimiterChar);
590             }
591             if (quote){
592                 out.write(escapeAndQuote(value));
593             } else {
594                 out.write(value);
595             }
596             if (autoFlush) flush();
597         } catch (IOException iox){
598             error = true;
599             throw iox;
600         }
601     }
602
603     /**
604      * Enclose the value in quotes and escape the quote
605      * and comma characters that are inside.
606      *
607      * @param value needs to be escaped and quoted
608      * @return the value, escaped and quoted.
609      *
610      * @since ostermillerutils 1.00.00
611      */

612     private String JavaDoc escapeAndQuote(String JavaDoc value){
613         int count = 2;
614         for (int i=0; i<value.length(); i++){
615             char c = value.charAt(i);
616             switch(c){
617                 case '\n': case '\r': case '\\': {
618                     count ++;
619                 } break;
620                 default: {
621                     if (c == quoteChar){
622                         count++;
623                     }
624                 } break;
625             }
626         }
627         StringBuffer JavaDoc sb = new StringBuffer JavaDoc(value.length() + count);
628         sb.append(quoteChar);
629         for (int i=0; i<value.length(); i++){
630             char c = value.charAt(i);
631             switch(c){
632                 case '\n': {
633                     sb.append("\\n");
634                 } break;
635                 case '\r': {
636                     sb.append("\\r");
637                 } break;
638                 case '\\': {
639                     sb.append("\\\\");
640                 } break;
641                 default: {
642                     if (c == quoteChar){
643                         sb.append("\\" + quoteChar);
644                     } else {
645                         sb.append(c);
646                     }
647                 }
648             }
649         }
650         sb.append(quoteChar);
651         return (sb.toString());
652     }
653
654     /**
655      * Flush any data written out to underlying streams.
656      *
657      * @since ostermillerutils 1.02.26
658      */

659     public void flush() throws IOException {
660         out.flush();
661     }
662
663     /**
664      * Close any underlying streams.
665      *
666      * @since ostermillerutils 1.02.26
667      */

668     public void close() throws IOException {
669         out.close();
670     }
671
672     /**
673      * Flush the stream if it's not closed and check its error state.
674      * Errors are cumulative; once the stream encounters an error,
675      * this routine will return true on all successive calls.
676      *
677      * @return True if the print stream has encountered an error,
678      * either on the underlying output stream or during a format conversion.
679      *
680      * @since ostermillerutils 1.02.26
681      */

682     public boolean checkError(){
683         try {
684             if (error) return true;
685             flush();
686             if (error) return true;
687             if (out instanceof PrintWriter){
688                 error = ((PrintWriter)out).checkError();
689             }
690         } catch (IOException iox){
691             error = true;
692         }
693         return error;
694     }
695
696     /**
697      * Set flushing behavior. Iff set, a flush command
698      * will be issued to any underlying stream after each
699      * print or write command.
700      *
701      * @param autoFlush should auto flushing be enabled.
702      *
703      * @since ostermillerutils 1.02.26
704      */

705     public void setAutoFlush(boolean autoFlush){
706         this.autoFlush = autoFlush;
707     }
708
709     /**
710      * Set whether values printers should always be quoted, or
711      * whether the printer may, at its discretion, omit quotes
712      * around the value.
713      *
714      * @param alwaysQuote true if quotes should be used even when not strictly needed.
715      *
716      * @since ostermillerutils 1.02.26
717      */

718     public void setAlwaysQuote(boolean alwaysQuote){
719         this.alwaysQuote = alwaysQuote;
720     }
721
722     /**
723      * Write some test data to the given file.
724      *
725      * @param args First argument is the file name. System.out used if no filename given.
726      *
727      * @since ostermillerutils 1.00.00
728      */

729     private static void main(String JavaDoc[] args) {
730         OutputStream out;
731         try {
732             if (args.length > 0){
733                 File f = new File(args[0]);
734                 if (!f.exists()){
735                         f.createNewFile();
736                     if (f.canWrite()){
737                         out = new FileOutputStream(f);
738                     } else {
739                         throw new IOException("Could not open " + args[0]);
740                     }
741                 } else {
742                     throw new IOException("File already exists: " + args[0]);
743                 }
744             } else {
745                 out = System.out;
746             }
747             CSVPrinter p = new CSVPrinter(out);
748             p.print("unquoted");
749             p.print("un\\quoted");
750             p.print("escaped\"quote");
751             p.print("escaped\"quote\\");
752             p.println("comma,comma");
753             p.print("!quoted");
754             p.print("!unquoted");
755             p.print(" quoted");
756             p.print("quoted ");
757             p.printlnComment("A comment.");
758             p.print("one");
759             p.print("");
760             p.print("");
761             p.print("");
762             p.print("");
763             p.printlnComment("Multi\nLine\rComment\r\nto test line breaks\r");
764             p.println("two");
765             p.printlnComment("Comment after explicit new line.");
766             p.print("\nthree\nline\n");
767             p.println("\ttab");
768         } catch (IOException e){
769             System.out.println(e.getMessage());
770         }
771     }
772 }
773
Popular Tags