001: /*
002: * Write files in Excel comma separated value format.
003: * Copyright (C) 2001-2007 Stephen Ostermiller
004: * http://ostermiller.org/contact.pl?regarding=Java+Utilities
005: * Copyright (C) 2003 Pierre Dittgen <pierre dot dittgen at pass-tech dot fr>
006: *
007: * This program is free software; you can redistribute it and/or modify
008: * it under the terms of the GNU General Public License as published by
009: * the Free Software Foundation; either version 2 of the License, or
010: * (at your option) any later version.
011: *
012: * This program is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: * GNU General Public License for more details.
016: *
017: * See COPYING.TXT for details.
018: */
019:
020: package com.Ostermiller.util;
021:
022: import java.io.*;
023:
024: /**
025: * Print values as a comma separated list that can be read by the
026: * Excel spreadsheet.
027: * More information about this class is available from <a target="_top" href=
028: * "http://ostermiller.org/utils/ExcelCSV.html">ostermiller.org</a>.
029: *
030: * @author Stephen Ostermiller http://ostermiller.org/contact.pl?regarding=Java+Utilities
031: * @author Pierre Dittgen <pierre dot dittgen at pass-tech dot fr>
032: * @since ostermillerutils 1.00.00
033: */
034: public class ExcelCSVPrinter implements CSVPrint {
035:
036: /**
037: * Default state of auto flush
038: */
039: private static final boolean AUTO_FLUSH_DEFAULT = true;
040:
041: /**
042: * If auto flushing is enabled.
043: *
044: * @since ostermillerutils 1.02.26
045: */
046: protected boolean autoFlush = AUTO_FLUSH_DEFAULT;
047:
048: /**
049: * Default state of always quote
050: */
051: private static final boolean ALWAYS_QUOTE_DEFAULT = false;
052:
053: /**
054: * If auto flushing is enabled.
055: *
056: * @since ostermillerutils 1.02.26
057: */
058: protected boolean alwaysQuote = ALWAYS_QUOTE_DEFAULT;
059:
060: /**
061: * true iff an error has occurred.
062: *
063: * @since ostermillerutils 1.02.26
064: */
065: protected boolean error = false;
066:
067: /**
068: * Default delimiter character
069: */
070: private static final char DELIMITER_DEFAULT = ',';
071:
072: /**
073: * Character written as field delimiter.
074: *
075: * @since ostermillerutils 1.02.18
076: */
077: protected char delimiterChar = DELIMITER_DEFAULT;
078:
079: /**
080: * Default quoting character
081: */
082: private static final char QUOTE_DEFAULT = '"';
083:
084: /**
085: * Quoting character.
086: *
087: * @since ostermillerutils 1.02.18
088: */
089: protected char quoteChar = QUOTE_DEFAULT;
090:
091: /**
092: * The place that the values get written.
093: *
094: * @since ostermillerutils 1.00.00
095: */
096: protected Writer out;
097:
098: /**
099: * True iff we just began a new line.
100: *
101: * @since ostermillerutils 1.00.00
102: */
103: protected boolean newLine = true;
104:
105: /**
106: * Line ending default
107: */
108: private static final String LINE_ENDING_DEFAULT = "\n";
109:
110: /**
111: * Line ending indicating the system line ending should be chosen
112: */
113: private static final String LINE_ENDING_SYSTEM = null;
114:
115: /**
116: * The line ending, must be one of "\n", "\r", or "\r\n"
117: *
118: * @since ostermillerutils 1.06.01
119: */
120: protected String lineEnding = "\n";
121:
122: /**
123: * Create a printer that will print values to the given
124: * stream. Character to byte conversion is done using
125: * the default character encoding. The delimiter will
126: * be the comma, the line ending will be the default system
127: * line ending, the quote character will be double quotes,
128: * quotes will be used when needed, and auto flushing
129: * will be enabled.
130: *
131: * @param out stream to which to print.
132: *
133: * @since ostermillerutils 1.00.00
134: */
135: public ExcelCSVPrinter(OutputStream out) {
136: this (new OutputStreamWriter(out));
137: }
138:
139: /**
140: * Create a printer that will print values to the given
141: * stream. The delimiter will be the comma, the line
142: * ending will be the default system line ending, the quote
143: * character will be double quotes, quotes will be used
144: * when needed, and auto flushing will be enabled.
145: *
146: * @param out stream to which to print.
147: *
148: * @since ostermillerutils 1.00.00
149: */
150: public ExcelCSVPrinter(Writer out) {
151: this (out, ALWAYS_QUOTE_DEFAULT, AUTO_FLUSH_DEFAULT);
152: }
153:
154: /**
155: * Create a printer that will print values to the given
156: * stream. The delimiter will be the comma, the line ending
157: * will be the default system line ending, and the quote character
158: * will be double quotes.
159: *
160: * @param out stream to which to print.
161: * @param alwaysQuote true if quotes should be used even when not strictly needed.
162: * @param autoFlush should auto flushing be enabled.
163: *
164: * @since ostermillerutils 1.02.26
165: */
166: public ExcelCSVPrinter(Writer out, boolean alwaysQuote,
167: boolean autoFlush) {
168: this (out, QUOTE_DEFAULT, DELIMITER_DEFAULT, alwaysQuote,
169: autoFlush);
170: }
171:
172: /**
173: * Create a printer that will print values to the given
174: * stream. The line ending will be the default system line
175: * ending, quotes will be used when needed, and auto flushing
176: * will be enabled.
177: *
178: * @param out stream to which to print.
179: * @param delimiter The new delimiter character to use.
180: * @param quote The new character to use for quoting.
181: * @throws BadQuoteException if the character cannot be used as a quote.
182: * @throws BadDelimiterException if the character cannot be used as a delimiter.
183: *
184: * @since ostermillerutils 1.02.26
185: */
186: public ExcelCSVPrinter(Writer out, char quote, char delimiter)
187: throws BadDelimiterException, BadQuoteException {
188: this (out, quote, delimiter, ALWAYS_QUOTE_DEFAULT,
189: AUTO_FLUSH_DEFAULT);
190: }
191:
192: /**
193: * Create a printer that will print values to the given
194: * stream. Quotes will be used when needed, and auto flushing
195: * will be enabled.
196: *
197: * @param out stream to which to print.
198: * @param delimiter The new delimiter character to use.
199: * @param quote The new character to use for quoting.
200: * @param lineEnding The new line ending, or null to use the default line ending.
201: * @throws BadQuoteException if the character cannot be used as a quote.
202: * @throws BadDelimiterException if the character cannot be used as a delimiter.
203: * @throws BadLineEndingException if the line ending is not one of the three legal line endings.
204: *
205: * @since ostermillerutils 1.06.01
206: */
207: public ExcelCSVPrinter(Writer out, char quote, char delimiter,
208: String lineEnding) throws BadDelimiterException,
209: BadQuoteException, BadLineEndingException {
210: this (out, quote, delimiter, lineEnding, ALWAYS_QUOTE_DEFAULT,
211: AUTO_FLUSH_DEFAULT);
212: }
213:
214: /**
215: * Create a printer that will print values to the given
216: * stream. The line ending will be the default system line ending.
217: *
218: * @param out stream to which to print.
219: * @param delimiter The new delimiter character to use.
220: * @param quote The new character to use for quoting.
221: * @param alwaysQuote true if quotes should be used even when not strictly needed.
222: * @param autoFlush should auto flushing be enabled.
223: * @throws BadQuoteException if the character cannot be used as a quote.
224: * @throws BadDelimiterException if the character cannot be used as a delimiter.
225: *
226: * @since ostermillerutils 1.02.26
227: */
228: public ExcelCSVPrinter(Writer out, char quote, char delimiter,
229: boolean alwaysQuote, boolean autoFlush)
230: throws BadDelimiterException, BadQuoteException {
231: this (out, quote, delimiter, LINE_ENDING_SYSTEM, alwaysQuote,
232: autoFlush);
233: }
234:
235: /**
236: * Create a printer that will print values to the given
237: * stream.
238: *
239: * @param out stream to which to print.
240: * @param delimiter The new delimiter character to use.
241: * @param quote The new character to use for quoting.
242: * @param lineEnding The new line ending, or null to use the default line ending.
243: * @param alwaysQuote true if quotes should be used even when not strictly needed.
244: * @param autoFlush should auto flushing be enabled.
245: * @throws BadQuoteException if the character cannot be used as a quote.
246: * @throws BadDelimiterException if the character cannot be used as a delimiter.
247: * @throws BadLineEndingException if the line ending is not one of the three legal line endings.
248: *
249: * @since ostermillerutils 1.06.01
250: */
251: public ExcelCSVPrinter(Writer out, char quote, char delimiter,
252: String lineEnding, boolean alwaysQuote, boolean autoFlush)
253: throws BadDelimiterException, BadQuoteException,
254: BadLineEndingException {
255: this .out = out;
256: changeQuote(quote);
257: changeDelimiter(delimiter);
258: setAlwaysQuote(alwaysQuote);
259: setAutoFlush(autoFlush);
260: setLineEnding(lineEnding);
261: }
262:
263: /**
264: * Change this printer so that it uses a new delimiter.
265: *
266: * @param newDelimiter The new delimiter character to use.
267: * @throws BadDelimiterException if the character cannot be used as a delimiter.
268: *
269: * @author Pierre Dittgen <pierre dot dittgen at pass-tech dot fr>
270: * @since ostermillerutils 1.02.18
271: */
272: public void changeDelimiter(char newDelimiter)
273: throws BadDelimiterException {
274: if (delimiterChar == newDelimiter)
275: return; // no need to do anything.
276: if (newDelimiter == '\n' || newDelimiter == '\r'
277: || newDelimiter == delimiterChar
278: || newDelimiter == quoteChar) {
279: throw new BadDelimiterException();
280: }
281: delimiterChar = newDelimiter;
282: }
283:
284: /**
285: * Change this printer so that it uses a new character for quoting.
286: *
287: * @param newQuote The new character to use for quoting.
288: * @throws BadQuoteException if the character cannot be used as a quote.
289: *
290: * @author Pierre Dittgen <pierre dot dittgen at pass-tech dot fr>
291: * @since ostermillerutils 1.02.18
292: */
293: public void changeQuote(char newQuote) throws BadQuoteException {
294: if (quoteChar == newQuote)
295: return; // no need to do anything.
296: if (newQuote == '\n' || newQuote == '\r'
297: || newQuote == delimiterChar || newQuote == quoteChar) {
298: throw new BadQuoteException();
299: }
300: quoteChar = newQuote;
301: }
302:
303: /**
304: * Change this printer so that it uses a new line ending.
305: * <p>
306: * A line ending must be one of "\n", "\r", or "\r\n".
307: * <p>
308: * The default line ending is the system line separator as specified by
309: * <code>System.getProperty("line.separator")</code>, or "\n" if the system
310: * line separator is not a legal line ending.
311: *
312: * @param lineEnding The new line ending, or null to use the default line ending.
313: * @throws BadLineEndingException if the line ending is not one of the three legal line endings.
314: *
315: * @since ostermillerutils 1.06.01
316: */
317: public void setLineEnding(String lineEnding)
318: throws BadLineEndingException {
319: boolean setDefault = lineEnding == null;
320: if (setDefault) {
321: lineEnding = System.getProperty("line.separator");
322: }
323: if (!"\n".equals(lineEnding) && !"\r".equals(lineEnding)
324: && !"\r\n".equals(lineEnding)) {
325: if (setDefault) {
326: lineEnding = LINE_ENDING_DEFAULT;
327: } else {
328: throw new BadLineEndingException();
329: }
330: }
331: this .lineEnding = lineEnding;
332: }
333:
334: /**
335: * Print the string as the last value on the line. The value
336: * will be quoted if needed.
337: * <p>
338: * This method never throws an I/O exception. The client may inquire as to whether
339: * any errors have occurred by invoking checkError(). If an I/O Exception is
340: * desired, the client should use the corresponding writeln method.
341: *
342: * @param value value to be outputted.
343: *
344: * @since ostermillerutils 1.00.00
345: */
346: public void println(String value) {
347: try {
348: writeln(value);
349: } catch (IOException iox) {
350: error = true;
351: }
352: }
353:
354: /**
355: * Print the string as the last value on the line. The value
356: * will be quoted if needed.
357: *
358: * @param value value to be outputted.
359: * @throws IOException if an error occurs while writing.
360: *
361: * @since ostermillerutils 1.02.26
362: */
363: public void writeln(String value) throws IOException {
364: try {
365: write(value);
366: writeln();
367: } catch (IOException iox) {
368: error = true;
369: throw iox;
370: }
371: }
372:
373: /**
374: * Output a blank line.
375: * <p>
376: * This method never throws an I/O exception. The client may inquire as to whether
377: * any errors have occurred by invoking checkError(). If an I/O Exception is
378: * desired, the client should use the corresponding writeln method.
379: *
380: * @since ostermillerutils 1.00.00
381: */
382: public void println() {
383: try {
384: writeln();
385: } catch (IOException iox) {
386: error = true;
387: }
388: }
389:
390: /**
391: * Output a blank line.
392: *
393: * @throws IOException if an error occurs while writing.
394: *
395: * @since ostermillerutils 1.02.26
396: */
397: public void writeln() throws IOException {
398: try {
399: out.write(lineEnding);
400: if (autoFlush)
401: flush();
402: newLine = true;
403: } catch (IOException iox) {
404: error = true;
405: throw iox;
406: }
407: }
408:
409: /**
410: * Print a single line of comma separated values.
411: * The values will be quoted if needed. Quotes and
412: * and other characters that need it will be escaped.
413: * <p>
414: * This method never throws an I/O exception. The client may inquire as to whether
415: * any errors have occurred by invoking checkError(). If an I/O Exception is
416: * desired, the client should use the corresponding writeln method.
417: *
418: * @param values values to be outputted.
419: *
420: * @since ostermillerutils 1.00.00
421: */
422: public void println(String[] values) {
423: try {
424: writeln(values);
425: } catch (IOException iox) {
426: error = true;
427: }
428: }
429:
430: /**
431: * Print a single line of comma separated values.
432: * The values will be quoted if needed. Quotes and
433: * and other characters that need it will be escaped.
434: *
435: * @param values values to be outputted.
436: * @throws IOException if an error occurs while writing.
437: *
438: * @since ostermillerutils 1.02.26
439: */
440: public void writeln(String[] values) throws IOException {
441: try {
442: print(values);
443: writeln();
444: } catch (IOException iox) {
445: error = true;
446: throw iox;
447: }
448: }
449:
450: /**
451: * Print a single line of comma separated values.
452: * The values will be quoted if needed. Quotes and
453: * and other characters that need it will be escaped.
454: * <p>
455: * This method never throws an I/O exception. The client may inquire as to whether
456: * any errors have occurred by invoking checkError(). If an I/O Exception is
457: * desired, the client should use the corresponding writeln method.
458: *
459: * @param values values to be outputted.
460: *
461: * @since ostermillerutils 1.00.00
462: */
463: public void print(String[] values) {
464: try {
465: write(values);
466: } catch (IOException iox) {
467: error = true;
468: }
469: }
470:
471: /**
472: * Print a single line of comma separated values.
473: * The values will be quoted if needed. Quotes and
474: * and other characters that need it will be escaped.
475: *
476: * @param values values to be outputted.
477: * @throws IOException if an error occurs while writing.
478: *
479: * @since ostermillerutils 1.02.26
480: */
481: public void write(String[] values) throws IOException {
482: try {
483: for (int i = 0; i < values.length; i++) {
484: write(values[i]);
485: }
486: } catch (IOException iox) {
487: error = true;
488: throw iox;
489: }
490: }
491:
492: /**
493: * Print several lines of comma separated values.
494: * The values will be quoted if needed. Quotes and
495: * newLine characters will be escaped.
496: * <p>
497: * This method never throws an I/O exception. The client may inquire as to whether
498: * any errors have occurred by invoking checkError(). If an I/O Exception is
499: * desired, the client should use the corresponding writeln method.
500: *
501: * @param values values to be outputted.
502: *
503: * @since ostermillerutils 1.00.00
504: */
505: public void println(String[][] values) {
506: try {
507: writeln(values);
508: } catch (IOException iox) {
509: error = true;
510: }
511: }
512:
513: /**
514: * Print several lines of comma separated values.
515: * The values will be quoted if needed. Quotes and
516: * newLine characters will be escaped.
517: *
518: * @param values values to be outputted.
519: * @throws IOException if an error occurs while writing.
520: *
521: * @since ostermillerutils 1.02.26
522: */
523: public void writeln(String[][] values) throws IOException {
524: try {
525: for (int i = 0; i < values.length; i++) {
526: writeln(values[i]);
527: }
528: if (values.length == 0) {
529: writeln();
530: }
531: } catch (IOException iox) {
532: error = true;
533: throw iox;
534: }
535: }
536:
537: /**
538: * Since ExcelCSV format does not support comments,
539: * this method will ignore the comment and start
540: * a new row.
541: * <p>
542: * This method never throws an I/O exception. The client may inquire as to whether
543: * any errors have occurred by invoking checkError(). If an I/O Exception is
544: * desired, the client should use the corresponding writelnComment method.
545: *
546: * @param comment the comment to output (ignored).
547: *
548: * @since ostermillerutils 1.00.00
549: */
550: public void printlnComment(String comment) {
551: println();
552: }
553:
554: /**
555: * Since ExcelCSV format does not support comments,
556: * this method will ignore the comment and start
557: * a new row.
558: *
559: * @param comment the comment to output (ignored).
560: * @throws IOException if an error occurs while writing.
561: *
562: * @since ostermillerutils 1.02.26
563: */
564: public void writelnComment(String comment) throws IOException {
565: writeln();
566: }
567:
568: /**
569: * Print the string as the next value on the line. The value
570: * will be quoted if needed. If value is null, an empty value is printed.
571: * <p>
572: * This method never throws an I/O exception. The client may inquire as to whether
573: * any errors have occurred by invoking checkError(). If an I/O Exception is
574: * desired, the client should use the corresponding println method.
575: *
576: * @param value value to be outputted.
577: *
578: * @since ostermillerutils 1.00.00
579: */
580: public void print(String value) {
581: try {
582: write(value);
583: } catch (IOException iox) {
584: error = true;
585: }
586: }
587:
588: /**
589: * Print the string as the next value on the line. The value
590: * will be quoted if needed. If value is null, an empty value is printed.
591: *
592: * @param value value to be outputted.
593: * @throws IOException if an error occurs while writing.
594: *
595: * @since ostermillerutils 1.02.26
596: */
597: public void write(String value) throws IOException {
598: try {
599: if (value == null)
600: value = "";
601: boolean quote = false;
602: if (alwaysQuote) {
603: quote = true;
604: } else if (value.length() > 0) {
605: for (int i = 0; i < value.length(); i++) {
606: char c = value.charAt(i);
607: if (c == quoteChar || c == delimiterChar
608: || c == '\n' || c == '\r') {
609: quote = true;
610: }
611: }
612: } else if (newLine) {
613: // always quote an empty token that is the first
614: // on the line, as it may be the only thing on the
615: // line. If it were not quoted in that case,
616: // an empty line has no tokens.
617: quote = true;
618: }
619: if (newLine) {
620: newLine = false;
621: } else {
622: out.write(delimiterChar);
623: }
624: if (quote) {
625: out.write(escapeAndQuote(value));
626: } else {
627: out.write(value);
628: }
629: if (autoFlush)
630: flush();
631: } catch (IOException iox) {
632: error = true;
633: throw iox;
634: }
635: }
636:
637: /**
638: * Enclose the value in quotes and escape the quote
639: * and comma characters that are inside.
640: *
641: * @param value needs to be escaped and quoted.
642: *
643: * @return the value, escaped and quoted.
644: * @since ostermillerutils 1.00.00
645: */
646: private String escapeAndQuote(String value) {
647: String s = StringHelper.replace(value, String
648: .valueOf(quoteChar), String.valueOf(quoteChar)
649: + String.valueOf(quoteChar));
650: return (new StringBuffer(2 + s.length())).append(quoteChar)
651: .append(s).append(quoteChar).toString();
652: }
653:
654: /**
655: * Flush any data written out to underlying streams.
656: *
657: * @throws IOException if an error occurs while writing.
658: * @since ostermillerutils 1.02.26
659: */
660: public void flush() throws IOException {
661: out.flush();
662: }
663:
664: /**
665: * Close any underlying streams.
666: *
667: * @throws IOException if an error occurs while writing.
668: * @since ostermillerutils 1.02.26
669: */
670: public void close() throws IOException {
671: out.close();
672: }
673:
674: /**
675: * Flush the stream if it's not closed and check its error state.
676: * Errors are cumulative; once the stream encounters an error,
677: * this routine will return true on all successive calls.
678: *
679: * @return True if the print stream has encountered an error,
680: * either on the underlying output stream or during a format conversion.
681: *
682: * @since ostermillerutils 1.02.26
683: */
684: public boolean checkError() {
685: try {
686: if (error)
687: return true;
688: flush();
689: if (error)
690: return true;
691: if (out instanceof PrintWriter) {
692: error = ((PrintWriter) out).checkError();
693: }
694: } catch (IOException iox) {
695: error = true;
696: }
697: return error;
698: }
699:
700: /**
701: * Set whether values printers should always be quoted, or
702: * whether the printer may, at its discretion, omit quotes
703: * around the value.
704: *
705: * @param alwaysQuote true if quotes should be used even when not strictly needed.
706: *
707: * @since ostermillerutils 1.02.26
708: */
709: public void setAlwaysQuote(boolean alwaysQuote) {
710: this .alwaysQuote = alwaysQuote;
711: }
712:
713: /**
714: * Set flushing behavior. Iff set, a flush command
715: * will be issued to any underlying stream after each
716: * print or write command.
717: *
718: * @param autoFlush should auto flushing be enabled.
719: *
720: * @since ostermillerutils 1.02.26
721: */
722: public void setAutoFlush(boolean autoFlush) {
723: this.autoFlush = autoFlush;
724: }
725: }
|