/* * $Id:$ * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * * 3. This notice may not be removed or altered from any source * distribution. */ package oms3.io; import java.io.OutputStream; import java.io.PrintWriter; import java.io.Writer; /** * Print values as a comma separated list. */ class CSVPrinter { /** The place that the values get written. */ protected PrintWriter out; /** True if we just began a new line. */ protected boolean newLine = true; private CSVStrategy strategy = CSVStrategy.DEFAULT_STRATEGY; /** * Create a printer that will print values to the given * stream. Character to byte conversion is done using * the default character encoding. Comments will be * written using the default comment character '#'. * * @param out stream to which to print. */ public CSVPrinter(OutputStream out) { this.out = new PrintWriter(out); } /** * Create a printer that will print values to the given * stream. Comments will be * written using the default comment character '#'. * * @param out stream to which to print. */ public CSVPrinter(Writer out) { if (out instanceof PrintWriter) { this.out = (PrintWriter) out; } else { this.out = new PrintWriter(out); } } // ====================================================== // strategies // ====================================================== /** * Sets the specified CSV Strategy * * @return current instance of CSVParser to allow chained method calls */ public CSVPrinter setStrategy(CSVStrategy strategy) { this.strategy = strategy; return this; } /** * Obtain the specified CSV Strategy * * @return strategy currently being used */ public CSVStrategy getStrategy() { return this.strategy; } // ====================================================== // printing implementation // ====================================================== /** * Print the string as the last value on the line. The value * will be quoted if needed. * * @param value value to be outputted. */ public void println(String value) { print(value); out.println(); out.flush(); newLine = true; } /** * Output a blank line */ public void println() { out.println(); out.flush(); newLine = true; } /** * Print a single line of comma separated values. * The values will be quoted if needed. Quotes and * newLine characters will be escaped. * * @param values values to be outputted. */ public void println(String[] values) { for (int i = 0; i < values.length; i++) { print(values[i]); } out.println(); out.flush(); newLine = true; } /** * Print several lines of comma separated values. * The values will be quoted if needed. Quotes and * newLine characters will be escaped. * * @param values values to be outputted. */ public void println(String[][] values) { for (int i = 0; i < values.length; i++) { println(values[i]); } if (values.length == 0) { out.println(); } out.flush(); newLine = true; } /** * Put a comment among the comma separated values. * Comments will always begin on a new line and occupy a * least one full line. The character specified to star * comments and a space will be inserted at the beginning of * each new line in the comment. * * @param comment the comment to output */ public void printlnComment(String comment) { if (this.strategy.isCommentingDisabled()) { return; } if (!newLine) { out.println(); } out.print(this.strategy.getCommentStart()); out.print(' '); for (int i = 0; i < comment.length(); i++) { char c = comment.charAt(i); switch (c) { case '\r': if (i + 1 < comment.length() && comment.charAt(i + 1) == '\n') { i++; } // break intentionally excluded. case '\n': out.println(); out.print(this.strategy.getCommentStart()); out.print(' '); break; default: out.print(c); break; } } out.println(); out.flush(); newLine = true; } /** * Print the string as the next value on the line. The value * will be quoted if needed. * * @param value value to be outputted. */ public void print(String value) { boolean quote = false; if (value.length() > 0) { char c = value.charAt(0); if (newLine && (c < '0' || (c > '9' && c < 'A') || (c > 'Z' && c < 'a') || (c > 'z'))) { quote = true; } if (c == ' ' || c == '\f' || c == '\t') { quote = true; } for (int i = 0; i < value.length(); i++) { c = value.charAt(i); if (c == '"' || c == this.strategy.getDelimiter() || c == '\n' || c == '\r') { quote = true; c = value.charAt(value.length() - 1); break; } } if (c == ' ' || c == '\f' || c == '\t') { quote = true; } } else if (newLine) { // always quote an empty token that is the first // on the line, as it may be the only thing on the // line. If it were not quoted in that case, // an empty line has no tokens. quote = true; } if (newLine) { newLine = false; } else { out.print(this.strategy.getDelimiter()); } if (quote) { out.print(escapeAndQuote(value)); } else { out.print(value); } out.flush(); } /** * Enclose the value in quotes and escape the quote * and comma characters that are inside. * * @param value needs to be escaped and quoted * @return the value, escaped and quoted */ private String escapeAndQuote(String value) { // the initial count is for the preceding and trailing quotes int count = 2; for (int i = 0; i < value.length(); i++) { switch (value.charAt(i)) { case '\"': case '\n': case '\r': case '\\': count++; break; default: break; } } StringBuffer sb = new StringBuffer(value.length() + count); sb.append(strategy.getEncapsulator()); for (int i = 0; i < value.length(); i++) { char c = value.charAt(i); if (c == strategy.getEncapsulator()) { sb.append('\\').append(c); continue; } switch (c) { case '\n': sb.append("\\n"); break; case '\r': sb.append("\\r"); break; case '\\': sb.append("\\\\"); break; default: sb.append(c); } } sb.append(strategy.getEncapsulator()); return sb.toString(); } }