View Javadoc

1   /*
2    * Copyright (c) 2004, RV Test Team
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions are met:
7    *
8    * Redistributions of source code must retain the above copyright notice, this
9    * list of conditions and the following disclaimer.
10   *
11   * Redistributions in binary form must reproduce the above copyright notice,
12   * this list of conditions and the following disclaimer in the documentation
13   * and/or other materials provided with the distribution.
14   *
15   * Neither the name of the "RV Test Team" nor the names of its contributors may
16   * be used to endorse or promote products derived from this software without
17   * specific prior written permission.
18   *
19   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
29   * THE POSSIBILITY OF SUCH DAMAGE.
30   *
31   */
32  //Created on Oct 6, 2004
33  package com.reuters.msgtest.load.output.csv;
34  
35  import java.io.IOException;
36  import java.io.OutputStream;
37  import java.io.OutputStreamWriter;
38  import java.io.PrintWriter;
39  import java.io.Writer;
40  
41  
42  /*
43   * Write files in Excel comma separated value format.
44   * Copyright (C) 2001-2004 Stephen Ostermiller
45   * http://ostermiller.org/contact.pl?regarding=Java+Utilities
46   * Copyright (C) 2003 Pierre Dittgen <pierre dot dittgen at pass-tech dot fr>
47   *
48   * This program is free software; you can redistribute it and/or modify
49   * it under the terms of the GNU General Public License as published by
50   * the Free Software Foundation; either version 2 of the License, or
51   * (at your option) any later version.
52   *
53   * This program is distributed in the hope that it will be useful,
54   * but WITHOUT ANY WARRANTY; without even the implied warranty of
55   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
56   * GNU General Public License for more details.
57   *
58   * See COPYING.TXT for details.
59   */
60  
61  /***
62   * Print values as a comma separated list that can be read by the
63   * Excel spreadsheet.
64   * More information about this class is available from <a target="_top" href=
65   * "http://ostermiller.org/utils/ExcelCSV.html">ostermiller.org</a>.
66   *
67   * @author Stephen Ostermiller http://ostermiller.org/contact.pl?regarding=Java+Utilities
68   * @author Pierre Dittgen <pierre dot dittgen at pass-tech dot fr>
69   * @since ostermillerutils 1.00.00
70   */
71  public class ExcelCSVPrinter implements CSVPrint {
72      /***
73       * If auto flushing is enabled.
74       *
75       * @since ostermillerutils 1.02.26
76       */
77      protected boolean autoFlush = true;
78  
79      /***
80       * If auto flushing is enabled.
81       *
82       * @since ostermillerutils 1.02.26
83       */
84      protected boolean alwaysQuote = false;
85  
86      /***
87       * true iff an error has occurred.
88       *
89       * @since ostermillerutils 1.02.26
90       */
91      protected boolean error = false;
92  
93      /***
94       * Delimiter character written.
95       *
96       * @since ostermillerutils 1.02.18
97       */
98      protected char delimiterChar = ',';
99  
100     /***
101      * Quoting character written.
102      *
103      * @since ostermillerutils 1.02.18
104      */
105     protected char quoteChar = '"';
106 
107     /***
108      * The place that the values get written.
109      *
110      * @since ostermillerutils 1.00.00
111      */
112     protected Writer out;
113 
114     /***
115      * True iff we just began a new line.
116      *
117      * @since ostermillerutils 1.00.00
118      */
119     protected boolean newLine = true;
120 
121     /***
122      * Create a printer that will print values to the given
123      * stream.   Character to byte conversion is done using
124      * the default character encoding.  The delimiter will
125      * be the comma, the quote character will be double quotes,
126      * quotes will be used when needed, and auto flushing
127      * will be enabled.
128      *
129      * @param out stream to which to print.
130      *
131      * @since ostermillerutils 1.00.00
132      */
133     public ExcelCSVPrinter(OutputStream out) {
134         this.out = new OutputStreamWriter(out);
135     }
136 
137     /***
138      * Create a printer that will print values to the given
139      * stream.    The delimiter will
140      * be the comma, the quote character will be double quotes,
141      * quotes will be used when needed, and auto flushing
142      * will be enabled.
143      *
144      * @param out stream to which to print.
145      *
146      * @since ostermillerutils 1.00.00
147      */
148     public ExcelCSVPrinter(Writer out) {
149         this.out = out;
150     }
151 
152     /***
153      * Create a printer that will print values to the given
154      * stream.    The delimiter will
155      * be the comma, and the quote character will be double quotes.
156      *
157      * @param out stream to which to print.
158      * @param alwaysQuote true if quotes should be used even when not strictly needed.
159      * @param autoFlush should auto flushing be enabled.
160      *
161      * @since ostermillerutils 1.02.26
162      */
163     public ExcelCSVPrinter(Writer out, boolean alwaysQuote, boolean autoFlush) {
164         this.out = out;
165         setAlwaysQuote(alwaysQuote);
166         setAutoFlush(autoFlush);
167     }
168 
169     /***
170      * Create a printer that will print values to the given
171      * stream.  Quotes will be used when needed, and auto flushing
172      * will be enabled.
173      *
174      * @param out stream to which to print.
175      * @param delimiter The new delimiter character to use.
176      * @param quote The new character to use for quoting.
177      * @throws BadQuoteException if the character cannot be used as a quote.
178      * @throws BadDelimiterException if the character cannot be used as a delimiter.
179      *
180      * @since ostermillerutils 1.02.26
181      */
182     public ExcelCSVPrinter(Writer out, char quote, char delimiter)
183         throws BadDelimeterException, BadQuoteException {
184         this.out = out;
185         changeQuote(quote);
186         changeDelimiter(delimiter);
187     }
188 
189     /***
190      * Create a printer that will print values to the given
191      * stream.
192      *
193      * @param out stream to which to print.
194      * @param delimiter The new delimiter character to use.
195      * @param quote The new character to use for quoting.
196      * @param alwaysQuote true if quotes should be used even when not strictly needed.
197      * @param autoFlush should auto flushing be enabled.
198      * @throws BadQuoteException if the character cannot be used as a quote.
199      * @throws BadDelimiterException if the character cannot be used as a delimiter.
200      *
201      * @since ostermillerutils 1.02.26
202      */
203     public ExcelCSVPrinter(Writer out, char quote, char delimiter,
204         boolean alwaysQuote, boolean autoFlush)
205         throws BadDelimeterException, BadQuoteException {
206         this.out = out;
207         changeQuote(quote);
208         changeDelimiter(delimiter);
209         setAlwaysQuote(alwaysQuote);
210         setAutoFlush(autoFlush);
211     }
212 
213     /***
214      * Change this printer so that it uses a new delimiter.
215      *
216      * @param newDelimiter The new delimiter character to use.
217      * @throws BadDelimiterException if the character cannot be used as a delimiter.
218      *
219      * @author Pierre Dittgen <pierre dot dittgen at pass-tech dot fr>
220      * @since ostermillerutils 1.02.18
221      */
222     public void changeDelimiter(char newDelimiter) throws BadDelimeterException {
223         if (delimiterChar == newDelimiter) {
224             return; // no need to do anything.
225         }
226 
227         if ((newDelimiter == '\n') || (newDelimiter == '\r') ||
228                 (newDelimiter == delimiterChar) || (newDelimiter == quoteChar)) {
229             throw new BadDelimeterException();
230         }
231 
232         delimiterChar = newDelimiter;
233     }
234 
235     /***
236      * Change this printer so that it uses a new character for quoting.
237      *
238      * @param newQuote The new character to use for quoting.
239      * @throws BadQuoteException if the character cannot be used as a quote.
240      *
241      * @author Pierre Dittgen <pierre dot dittgen at pass-tech dot fr>
242      * @since ostermillerutils 1.02.18
243      */
244     public void changeQuote(char newQuote) throws BadQuoteException {
245         if (quoteChar == newQuote) {
246             return; // no need to do anything.
247         }
248 
249         if ((newQuote == '\n') || (newQuote == '\r') ||
250                 (newQuote == delimiterChar) || (newQuote == quoteChar)) {
251             throw new BadQuoteException();
252         }
253 
254         quoteChar = newQuote;
255     }
256 
257     /***
258      * Print the string as the last value on the line.  The value
259      * will be quoted if needed.
260      * <p>
261      * This method never throws an I/O exception. The client may inquire as to whether
262      * any errors have occurred by invoking checkError().  If an I/O Exception is
263      * desired, the client should use the corresponding writeln method.
264      *
265      * @param value value to be outputted.
266      *
267      * @since ostermillerutils 1.00.00
268      */
269     public void println(String value) {
270         try {
271             writeln(value);
272         } catch (IOException iox) {
273             error = true;
274         }
275     }
276 
277     /***
278      * Print the string as the last value on the line.  The value
279      * will be quoted if needed.
280      *
281      * @param value value to be outputted.
282      * @throws IOException if an error occurs while writing.
283      *
284      * @since ostermillerutils 1.02.26
285      */
286     public void writeln(String value) throws IOException {
287         try {
288             write(value);
289             writeln();
290         } catch (IOException iox) {
291             error = true;
292             throw iox;
293         }
294     }
295 
296     /***
297      * Output a blank line.
298      * <p>
299      * This method never throws an I/O exception. The client may inquire as to whether
300      * any errors have occurred by invoking checkError().  If an I/O Exception is
301      * desired, the client should use the corresponding writeln method.
302      *
303      * @since ostermillerutils 1.00.00
304      */
305     public void println() {
306         try {
307             writeln();
308         } catch (IOException iox) {
309             error = true;
310         }
311     }
312 
313     /***
314      * Output a blank line.
315      *
316      * @throws IOException if an error occurs while writing.
317      *
318      * @since ostermillerutils 1.02.26
319      */
320     public void writeln() throws IOException {
321         try {
322             out.write("\n");
323 
324             if (autoFlush) {
325                 flush();
326             }
327 
328             newLine = true;
329         } catch (IOException iox) {
330             error = true;
331             throw iox;
332         }
333     }
334 
335     /***
336      * Print a single line of comma separated values.
337      * The values will be quoted if needed.  Quotes and
338      * and other characters that need it will be escaped.
339      * <p>
340      * This method never throws an I/O exception. The client may inquire as to whether
341      * any errors have occurred by invoking checkError().  If an I/O Exception is
342      * desired, the client should use the corresponding writeln method.
343      *
344      * @param values values to be outputted.
345      *
346      * @since ostermillerutils 1.00.00
347      */
348     public void println(String[] values) {
349         try {
350             writeln(values);
351         } catch (IOException iox) {
352             error = true;
353         }
354     }
355 
356     /***
357      * Print a single line of comma separated values.
358      * The values will be quoted if needed.  Quotes and
359      * and other characters that need it will be escaped.
360      *
361      * @param values values to be outputted.
362      * @throws IOException if an error occurs while writing.
363      *
364      * @since ostermillerutils 1.02.26
365      */
366     public void writeln(String[] values) throws IOException {
367         try {
368             print(values);
369             writeln();
370         } catch (IOException iox) {
371             error = true;
372             throw iox;
373         }
374     }
375 
376     /***
377      * Print a single line of comma separated values.
378      * The values will be quoted if needed.  Quotes and
379      * and other characters that need it will be escaped.
380      * <p>
381      * This method never throws an I/O exception. The client may inquire as to whether
382      * any errors have occurred by invoking checkError().  If an I/O Exception is
383      * desired, the client should use the corresponding writeln method.
384      *
385      * @param values values to be outputted.
386      *
387      * @since ostermillerutils 1.00.00
388      */
389     public void print(String[] values) {
390         try {
391             write(values);
392         } catch (IOException iox) {
393             error = true;
394         }
395     }
396 
397     /***
398      * Print a single line of comma separated values.
399      * The values will be quoted if needed.  Quotes and
400      * and other characters that need it will be escaped.
401      *
402      * @param values values to be outputted.
403      * @throws IOException if an error occurs while writing.
404      *
405      * @since ostermillerutils 1.02.26
406      */
407     public void write(String[] values) throws IOException {
408         try {
409             for (int i = 0; i < values.length; i++) {
410                 write(values[i]);
411             }
412         } catch (IOException iox) {
413             error = true;
414             throw iox;
415         }
416     }
417 
418     /***
419      * Print several lines of comma separated values.
420      * The values will be quoted if needed.  Quotes and
421      * newLine characters will be escaped.
422      * <p>
423      * This method never throws an I/O exception. The client may inquire as to whether
424      * any errors have occurred by invoking checkError().  If an I/O Exception is
425      * desired, the client should use the corresponding writeln method.
426      *
427      * @param values values to be outputted.
428      *
429      * @since ostermillerutils 1.00.00
430      */
431     public void println(String[][] values) {
432         try {
433             writeln(values);
434         } catch (IOException iox) {
435             error = true;
436         }
437     }
438 
439     /***
440      * Print several lines of comma separated values.
441      * The values will be quoted if needed.  Quotes and
442      * newLine characters will be escaped.
443      *
444      * @param values values to be outputted.
445      * @throws IOException if an error occurs while writing.
446      *
447      * @since ostermillerutils 1.02.26
448      */
449     public void writeln(String[][] values) throws IOException {
450         try {
451             for (int i = 0; i < values.length; i++) {
452                 writeln(values[i]);
453             }
454 
455             if (values.length == 0) {
456                 writeln();
457             }
458         } catch (IOException iox) {
459             error = true;
460             throw iox;
461         }
462     }
463 
464     /***
465      * Since ExcelCSV format does not support comments,
466      * this method will ignore the comment and start
467      * a new row.
468      * <p>
469      * This method never throws an I/O exception. The client may inquire as to whether
470      * any errors have occurred by invoking checkError().  If an I/O Exception is
471      * desired, the client should use the corresponding writelnComment method.
472      *
473      * @param comment the comment to output (ignored).
474      *
475      * @since ostermillerutils 1.00.00
476      */
477     public void printlnComment(String comment) {
478         println();
479     }
480 
481     /***
482      * Since ExcelCSV format does not support comments,
483      * this method will ignore the comment and start
484      * a new row.
485      *
486      * @param comment the comment to output (ignored).
487      * @throws IOException if an error occurs while writing.
488      *
489      * @since ostermillerutils 1.02.26
490      */
491     public void writelnComment(String comment) throws IOException {
492         writeln();
493     }
494 
495     /***
496      * Print the string as the next value on the line.  The value
497      * will be quoted if needed.  If value is null, an empty value is printed.
498      * <p>
499      * This method never throws an I/O exception. The client may inquire as to whether
500      * any errors have occurred by invoking checkError().  If an I/O Exception is
501      * desired, the client should use the corresponding println method.
502      *
503      * @param value value to be outputted.
504      *
505      * @since ostermillerutils 1.00.00
506      */
507     public void print(String value) {
508         try {
509             write(value);
510         } catch (IOException iox) {
511             error = true;
512         }
513     }
514 
515     /***
516      * Print the string as the next value on the line.  The value
517      * will be quoted if needed.  If value is null, an empty value is printed.
518      *
519      * @param value value to be outputted.
520      * @throws IOException if an error occurs while writing.
521      *
522      * @since ostermillerutils 1.02.26
523      */
524     public void write(String value) throws IOException {
525         try {
526             if (value == null) {
527                 value = "";
528             }
529 
530             boolean quote = false;
531 
532             if (alwaysQuote) {
533                 quote = true;
534             } else if (value.length() > 0) {
535                 for (int i = 0; i < value.length(); i++) {
536                     char c = value.charAt(i);
537 
538                     if ((c == quoteChar) || (c == delimiterChar) ||
539                             (c == '\n') || (c == '\r')) {
540                         quote = true;
541                     }
542                 }
543             } else if (newLine) {
544                 // always quote an empty token that is the firs
545                 // on the line, as it may be the only thing on the
546                 // line.  If it were not quoted in that case,
547                 // an empty line has no tokens.
548                 quote = true;
549             }
550 
551             if (newLine) {
552                 newLine = false;
553             } else {
554                 out.write(delimiterChar);
555             }
556 
557             if (quote) {
558                 out.write(escapeAndQuote(value));
559             } else {
560                 out.write(value);
561             }
562 
563             if (autoFlush) {
564                 flush();
565             }
566         } catch (IOException iox) {
567             error = true;
568             throw iox;
569         }
570     }
571 
572     /***
573      * Enclose the value in quotes and escape the quote
574      * and comma characters that are inside.
575      *
576      * @param value needs to be escaped and quoted.
577      *
578      * @return the value, escaped and quoted.
579      * @since ostermillerutils 1.00.00
580      */
581     private String escapeAndQuote(String value) {
582         String s = StringHelper.replace(value, String.valueOf(quoteChar),
583                 String.valueOf(quoteChar) + String.valueOf(quoteChar));
584 
585         return (new StringBuffer(2 + s.length())).append(quoteChar).append(s)
586                 .append(quoteChar).toString();
587     }
588 
589     /***
590      * Flush any data written out to underlying streams.
591      *
592      * @since ostermillerutils 1.02.26
593      */
594     public void flush() throws IOException {
595         out.flush();
596     }
597 
598     /***
599      * Close any underlying streams.
600      *
601      * @since ostermillerutils 1.02.26
602      */
603     public void close() throws IOException {
604         out.close();
605     }
606 
607     /***
608      * Flush the stream if it's not closed and check its error state.
609      * Errors are cumulative; once the stream encounters an error,
610      * this routine will return true on all successive calls.
611      *
612      * @return True if the print stream has encountered an error,
613      * either on the underlying output stream or during a format conversion.
614      *
615      * @since ostermillerutils 1.02.26
616      */
617     public boolean checkError() {
618         try {
619             if (error) {
620                 return true;
621             }
622 
623             flush();
624 
625             if (error) {
626                 return true;
627             }
628 
629             if (out instanceof PrintWriter) {
630                 error = ((PrintWriter) out).checkError();
631             }
632         } catch (IOException iox) {
633             error = true;
634         }
635 
636         return error;
637     }
638 
639     /***
640      * Set whether values printers should always be quoted, or
641      * whether the printer may, at its discretion, omit quotes
642      * around the value.
643      *
644      * @param alwaysQuote true if quotes should be used even when not strictly needed.
645      *
646      * @since ostermillerutils 1.02.26
647      */
648     public void setAlwaysQuote(boolean alwaysQuote) {
649         this.alwaysQuote = alwaysQuote;
650     }
651 
652     /***
653      * Set flushing behavior.  Iff set, a flush command
654      * will be issued to any underlying stream after each
655      * print or write command.
656      *
657      * @param autoFlush should auto flushing be enabled.
658      *
659      * @since ostermillerutils 1.02.26
660      */
661     public void setAutoFlush(boolean autoFlush) {
662         this.autoFlush = autoFlush;
663     }
664 }