1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
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;
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;
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
545
546
547
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 }