You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by ba...@apache.org on 2005/12/17 06:41:34 UTC
svn commit: r357300 [1/2] - in /jakarta/commons/trunks-sandbox/csv: ./ src/
src/java/ src/java/ch/ src/java/ch/netcetera/ src/java/ch/netcetera/wake/
src/java/ch/netcetera/wake/core/ src/java/ch/netcetera/wake/core/format/
src/java/ch/netcetera/wake/co...
Author: bayard
Date: Fri Dec 16 21:41:27 2005
New Revision: 357300
URL: http://svn.apache.org/viewcvs?rev=357300&view=rev
Log:
import of csv parser code, as donated by netcetera [code grant recorded]
Added:
jakarta/commons/trunks-sandbox/csv/
jakarta/commons/trunks-sandbox/csv/LICENSE.txt (with props)
jakarta/commons/trunks-sandbox/csv/NOTICE.txt (with props)
jakarta/commons/trunks-sandbox/csv/project.properties (with props)
jakarta/commons/trunks-sandbox/csv/project.xml (with props)
jakarta/commons/trunks-sandbox/csv/src/
jakarta/commons/trunks-sandbox/csv/src/java/
jakarta/commons/trunks-sandbox/csv/src/java/ch/
jakarta/commons/trunks-sandbox/csv/src/java/ch/netcetera/
jakarta/commons/trunks-sandbox/csv/src/java/ch/netcetera/wake/
jakarta/commons/trunks-sandbox/csv/src/java/ch/netcetera/wake/core/
jakarta/commons/trunks-sandbox/csv/src/java/ch/netcetera/wake/core/format/
jakarta/commons/trunks-sandbox/csv/src/java/ch/netcetera/wake/core/format/csv/
jakarta/commons/trunks-sandbox/csv/src/java/ch/netcetera/wake/core/format/csv/CSVParser.java (with props)
jakarta/commons/trunks-sandbox/csv/src/java/ch/netcetera/wake/core/format/csv/CSVPrinter.java (with props)
jakarta/commons/trunks-sandbox/csv/src/java/ch/netcetera/wake/core/format/csv/ExtendedBufferedReader.java (with props)
jakarta/commons/trunks-sandbox/csv/src/java/ch/netcetera/wake/core/format/csv/package.html (with props)
jakarta/commons/trunks-sandbox/csv/src/test/
jakarta/commons/trunks-sandbox/csv/src/test/ch/
jakarta/commons/trunks-sandbox/csv/src/test/ch/netcetera/
jakarta/commons/trunks-sandbox/csv/src/test/ch/netcetera/wake/
jakarta/commons/trunks-sandbox/csv/src/test/ch/netcetera/wake/core/
jakarta/commons/trunks-sandbox/csv/src/test/ch/netcetera/wake/core/format/
jakarta/commons/trunks-sandbox/csv/src/test/ch/netcetera/wake/core/format/FormatTests.java (with props)
jakarta/commons/trunks-sandbox/csv/src/test/ch/netcetera/wake/core/format/csv/
jakarta/commons/trunks-sandbox/csv/src/test/ch/netcetera/wake/core/format/csv/CSVParserTest.java (with props)
jakarta/commons/trunks-sandbox/csv/src/test/ch/netcetera/wake/core/format/csv/CSVPrinterTest.java (with props)
jakarta/commons/trunks-sandbox/csv/src/test/ch/netcetera/wake/core/format/csv/ExtendedBufferedReaderTest.java (with props)
Added: jakarta/commons/trunks-sandbox/csv/LICENSE.txt
URL: http://svn.apache.org/viewcvs/jakarta/commons/trunks-sandbox/csv/LICENSE.txt?rev=357300&view=auto
==============================================================================
--- jakarta/commons/trunks-sandbox/csv/LICENSE.txt (added)
+++ jakarta/commons/trunks-sandbox/csv/LICENSE.txt Fri Dec 16 21:41:27 2005
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
Propchange: jakarta/commons/trunks-sandbox/csv/LICENSE.txt
------------------------------------------------------------------------------
svn:eol-style = native
Added: jakarta/commons/trunks-sandbox/csv/NOTICE.txt
URL: http://svn.apache.org/viewcvs/jakarta/commons/trunks-sandbox/csv/NOTICE.txt?rev=357300&view=auto
==============================================================================
--- jakarta/commons/trunks-sandbox/csv/NOTICE.txt (added)
+++ jakarta/commons/trunks-sandbox/csv/NOTICE.txt Fri Dec 16 21:41:27 2005
@@ -0,0 +1,2 @@
+This product includes software developed by
+The Apache Software Foundation (http://www.apache.org/).
Propchange: jakarta/commons/trunks-sandbox/csv/NOTICE.txt
------------------------------------------------------------------------------
svn:eol-style = native
Added: jakarta/commons/trunks-sandbox/csv/project.properties
URL: http://svn.apache.org/viewcvs/jakarta/commons/trunks-sandbox/csv/project.properties?rev=357300&view=auto
==============================================================================
--- jakarta/commons/trunks-sandbox/csv/project.properties (added)
+++ jakarta/commons/trunks-sandbox/csv/project.properties Fri Dec 16 21:41:27 2005
@@ -0,0 +1,41 @@
+# Copyright 2003-2004 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#maven.xdoc.jsl=../commons-build/commons-site.jsl
+maven.xdoc.date=bottom
+maven.xdoc.poweredby.image=maven-feather.png
+maven.xdoc.version=${pom.currentVersion}
+maven.xdoc.developmentProcessUrl=http://jakarta.apache.org/commons/charter.html
+maven.xdoc.includeProjectDocumentation=yes
+
+maven.checkstyle.properties = checkstyle.xml
+
+# uncomment the next line to work in offline mode (no jar download & no linkcheck)
+maven.mode.online=
+maven.changelog.factory=org.apache.maven.svnlib.SvnChangeLogFactory
+
+maven.javadoc.author=false
+maven.javadoc.links=http://java.sun.com/products/jdk/1.4/docs/api
+
+maven.compile.debug=on
+maven.compile.deprecation=off
+maven.compile.optimize=off
+
+maven.jarResources.basedir=src/java
+maven.jar.excludes=**/package.html
+maven.junit.fork=true
+maven.junit.sysproperties=org.xml.sax.driver
+org.xml.sax.driver=org.apache.xerces.parsers.SAXParser
+
+clover.excludes=**/Test*.java
Propchange: jakarta/commons/trunks-sandbox/csv/project.properties
------------------------------------------------------------------------------
svn:eol-style = native
Added: jakarta/commons/trunks-sandbox/csv/project.xml
URL: http://svn.apache.org/viewcvs/jakarta/commons/trunks-sandbox/csv/project.xml?rev=357300&view=auto
==============================================================================
--- jakarta/commons/trunks-sandbox/csv/project.xml (added)
+++ jakarta/commons/trunks-sandbox/csv/project.xml Fri Dec 16 21:41:27 2005
@@ -0,0 +1,59 @@
+<?xml version="1.0"?>
+<!--
+ Copyright 2005 The Apache Software Foundation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<project>
+<!-- <extend>../commons-build/sandbox-project.xml</extend> -->
+ <name>Commons Csv</name>
+ <id>commons-csv</id>
+ <logo>/images/csv-logo-white.png</logo>
+ <inceptionYear>2005</inceptionYear>
+ <shortDescription>Commons Csv</shortDescription>
+ <description>
+ Commons Csv is a component that parses comma separated value files.
+ </description>
+ <currentVersion>0.1-dev</currentVersion>
+ <issueTrackingUrl>http://issues.apache.org/bugzilla/</issueTrackingUrl>
+ <siteAddress>cvs.apache.org</siteAddress>
+ <logo></logo>
+
+ <developers>
+ <developer>
+ <name>Henri Yandell</name>
+ <id>bayard</id>
+ <email>bayard@apache.org</email>
+ <organization></organization>
+ <roles>
+ <role>Java Developer</role>
+ </roles>
+ </developer>
+ </developers>
+
+ <dependencies>
+ <dependency>
+ <id>junit</id>
+ <version>3.8.1</version>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <unitTest>
+ <includes>
+ <include>**/*Test*</include>
+ </includes>
+ </unitTest>
+ </build>
+
+</project>
Propchange: jakarta/commons/trunks-sandbox/csv/project.xml
------------------------------------------------------------------------------
svn:eol-style = native
Added: jakarta/commons/trunks-sandbox/csv/src/java/ch/netcetera/wake/core/format/csv/CSVParser.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/trunks-sandbox/csv/src/java/ch/netcetera/wake/core/format/csv/CSVParser.java?rev=357300&view=auto
==============================================================================
--- jakarta/commons/trunks-sandbox/csv/src/java/ch/netcetera/wake/core/format/csv/CSVParser.java (added)
+++ jakarta/commons/trunks-sandbox/csv/src/java/ch/netcetera/wake/core/format/csv/CSVParser.java Fri Dec 16 21:41:27 2005
@@ -0,0 +1,763 @@
+/*
+ * The copyright to the computer program(s) herein is the property of
+ * Netcetera AG, Switzerland. The program(s) may be used and/or copied
+ * only with the written permission of Netcetera AG or in accordance
+ * with the terms and conditions stipulated in the agreement/contract
+ * under which the program(s) have been supplied.
+ *
+ * @(#) $Id: CSVParser.java,v 1.10 2004/09/30 13:37:10 hagger Exp $
+ */
+package ch.netcetera.wake.core.format.csv;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.Vector;
+
+
+/**
+ * Parses cvs files according to the specified configuration.
+ *
+ * Because CSV appears in many different dialects the parser supports many
+ * configuration settings. One of the most popular CSV format as supported
+ * by the M$ corporation (excel-spreadsheets) are supported out-of-the-box
+ * by the corresponding strategy setter (see {@link #setExcelStrategy()}).
+ *
+ * <p>Parsing of a csv-string having ';' as separator:</p>
+ * <pre>
+ * String[][] data =
+ * (new CSVParser(new StringReader("a;b\nc;d"),';')).getAllValues();
+ * </pre>
+ *
+ * <p>
+ * Internal parser state is completely covered by the strategy
+ * and the reader-state.</p>
+ *
+ * <p>see <a href="package-summary.html">package documentation</a>
+ * for more details</p>
+ */
+public class CSVParser {
+
+ /** length of the initial token (content-)buffer */
+ private static final int INITIAL_TOKEN_LENGTH = 50;
+
+ // the token types
+ protected static final int TT_INVALID = -1;
+ protected static final int TT_TOKEN = 0;
+ protected static final int TT_EOF = 1;
+ protected static final int TT_EORECORD = 2;
+
+ // the csv definition
+ private char delimiter;
+ private char encapsulator;
+ private char commentStart;
+ private boolean ignoreLeadingWhitespaces;
+ private boolean interpretUnicodeEscapes;
+ private boolean ignoreEmptyLines;
+
+ // the input stream
+ private ExtendedBufferedReader in;
+
+ /**
+ * Token is an internal token representation.
+ *
+ * It is used as contract between the lexer and the parser.
+ */
+ class Token {
+ // token type see TT_xxx constants
+ int type;
+ // the content buffer
+ StringBuffer content;
+ // token ready flag: indicates a valid token (ready for the parser)
+ boolean isReady;
+ Token() {
+ content = new StringBuffer(INITIAL_TOKEN_LENGTH);
+ type = TT_INVALID;
+ isReady = false;
+ }
+ }
+
+ // ======================================================
+ // static parsers
+ // ======================================================
+
+ /**
+ * Parses the given String according to the default CSV strategy.
+ *
+ * @return parsed String matrix (which is never null)
+ * @throws IOException in case of error
+ * @see #setCSVStrategy()
+ */
+ public static String[][] parse(String s) throws IOException {
+ if (s == null) {
+ throw new IllegalArgumentException("Null argument not allowed.");
+ }
+ return (new CSVParser(new StringReader(s))).getAllValues();
+ }
+
+ /**
+ * Parses the first line only according to the default CSV strategy.
+ *
+ * Parsing empty string will be handled as valid records containing zero
+ * elements, so the following property holds: parseLine("").length == 0.
+ *
+ * @return parsed String vector (which is never null)
+ * @throws IOException in case of error
+ * @see #setCSVStrategy()
+ */
+ public static String[] parseLine(String s) throws IOException {
+ if (s == null) {
+ throw new IllegalArgumentException("Null argument not allowed.");
+ }
+ // uh,jh: make sure that parseLine("").length == 0
+ if (s.length() == 0) {
+ return new String[0];
+ }
+ return (new CSVParser(new StringReader(s))).getLine();
+ }
+
+ // ======================================================
+ // the constructor
+ // ======================================================
+
+ /**
+ * Default strategy for the parser follows the default CSV Strategy.
+ *
+ * @param input an InputStream containing "csv-formatted" stream
+ * @see #setCSVStrategy()
+ */
+ public CSVParser(InputStream input) {
+ this(new InputStreamReader(input));
+ }
+
+ /**
+ * Default strategy for the parser follows the default CSV Strategy.
+ *
+ * @param input a Reader based on "csv-formatted" input
+ * @see #setCSVStrategy()
+ */
+ public CSVParser(Reader input) {
+ // note: must match default-CSV-strategy !!
+ this(input, ',');
+ }
+
+ /**
+ * Customized value delimiter parser.
+ *
+ * The parser follows the default CSV strategy as defined in
+ * {@link #setCSVStrategy()} except for the delimiter setting.
+ *
+ * @param input a Reader based on "csv-formatted" input
+ * @param delimiter a Char used for value separation
+ */
+ public CSVParser(Reader input, char delimiter) {
+ this(input, delimiter, '"', (char) 0);
+ }
+
+ /**
+ * Customized csv parser.
+ *
+ * The parser parses according to the given CSV dialect settings.
+ * Leading whitespaces are truncated whereas unicode escapes are
+ * not interpreted.
+ *
+ * @param input a Reader based on "csv-formatted" input
+ * @param delimiter a Char used for value separation
+ * @param encapsulator a Char used as value encapsulation marker
+ * @param commentStart a Char used for comment identification
+ */
+ public CSVParser(
+ Reader input,
+ char delimiter,
+ char encapsulator,
+ char commentStart) {
+ this.in = new ExtendedBufferedReader(input);
+ this.setDelimiter(delimiter);
+ this.setEncapsulator(encapsulator);
+ this.setCommentStart(commentStart);
+ this.setIgnoreLeadingWhitespaces(true);
+ this.setUnicodeEscapeInterpretation(false);
+ this.setIgnoreEmptyLines(true);
+ }
+
+ // ======================================================
+ // the parser
+ // ======================================================
+
+ /**
+ * Parses the CSV according to the given strategy
+ * and returns the content as an array of records
+ * (whereas records are arrays of single values).
+ * <p>
+ * The returned content starts at the current parse-position in
+ * the stream.
+ *
+ * @return matrix of records x values ('null' when end of file)
+ */
+ public String[][] getAllValues() throws IOException {
+ Vector records = new Vector();
+ String[] values;
+ String[][] ret = null;
+ while ((values = getLine()) != null) {
+ records.add(values);
+ }
+ if (records.size() > 0) {
+ ret = new String[records.size()][];
+ records.toArray(ret);
+ }
+ return ret;
+ }
+
+ /**
+ * Parses the CSV according to the given strategy
+ * and returns the next csv-value as string.
+ *
+ * @return next value in the input stream ('null' when end of file)
+ * @throws IOException
+ */
+ public String nextValue() throws IOException {
+ Token tkn = nextToken();
+ String ret = null;
+ switch (tkn.type) {
+ case TT_TOKEN:
+ case TT_EORECORD:
+ ret = tkn.content.toString();
+ break;
+ case TT_EOF:
+ ret = null;
+ break;
+ case TT_INVALID:
+ default:
+ // error no token available (or error)
+ throw new IOException(
+ "(line " + getLineNumber()
+ + ") invalid parse sequence");
+ // unreachable: break;
+ }
+ return ret;
+ }
+
+ /**
+ * Parses from the current point in the stream til
+ * the end of the current line.
+ *
+ * @return array of values til end of line
+ * ('null' when end of file has been reached)
+ * @throws IOException on parse error or input read-failure
+ */
+ public String[] getLine() throws IOException {
+ Vector record = new Vector();
+ String[] ret = new String[0];
+ Token tkn;
+ while ((tkn = nextToken()).type == TT_TOKEN) {
+ record.add(tkn.content.toString());
+ }
+ // did we reached eorecord or eof ?
+ switch (tkn.type) {
+ case TT_EORECORD:
+ record.add(tkn.content.toString());
+ break;
+ case TT_EOF:
+ ret = null;
+ break;
+ case TT_INVALID:
+ default:
+ // error: throw IOException
+ throw new IOException(
+ "(line " + getLineNumber()
+ + ") invalid parse sequence");
+ // unreachable: break;
+ }
+ if (record.size() > 0) {
+ ret = new String[record.size()];
+ record.toArray(ret);
+ }
+ return ret;
+ }
+
+ /**
+ * Returns the current line number in the input stream.
+ *
+ * ATTENTION: in case your csv has multiline-values the returned
+ * number does not correspond to the record-number
+ *
+ * @return current line number
+ * @throws IOException
+ */
+ public int getLineNumber() throws IOException {
+ return in.getLineNumber();
+ }
+
+ // ======================================================
+ // the lexer(s)
+ // ======================================================
+
+ /**
+ * Returns the next token
+ *
+ * a token coresponds to a term, a record change
+ * or and end-of-file indicator
+ */
+ protected Token nextToken() throws IOException {
+ Token tkn = new Token();
+ StringBuffer wsBuf = new StringBuffer();
+ // boolean skipEmptyLines = false;
+
+ // get the last read char (required for empty line detection)
+ int lastChar = in.readAgain();
+
+ // read the next char and set eol
+ /* note: unfourtunately isEndOfLine may consumes a character silently.
+ * this has no effect outside of the method. so a simple workaround
+ * is to call 'readAgain' on the stream...
+ * uh: might using objects instead of base-types (jdk1.5 autoboxing!)
+ */
+ int c = in.read();
+ boolean eol = isEndOfLine(c);
+ c = in.readAgain();
+
+ // empty line detection: eol AND (last char was EOL or beginning)
+ while (ignoreEmptyLines && eol
+ && (lastChar == '\n'
+ || lastChar == ExtendedBufferedReader.UNDEFINED)
+ && !isEndOfFile(lastChar)) {
+ // go on char ahead ...
+ lastChar = c;
+ c = in.read();
+ eol = isEndOfLine(c);
+ c = in.readAgain();
+ // reached end of file without any content (empty line at the end)
+ if (isEndOfFile(c)) {
+ tkn.type = TT_EOF;
+ return tkn;
+ }
+ }
+
+ // did we reached eof during the last iteration already ? TT_EOF
+ if (isEndOfFile(lastChar)) {
+ tkn.type = TT_EOF;
+ return tkn;
+ }
+
+ // important: make sure a new char gets consumed in each iteration
+ while (!tkn.isReady) {
+ // ignore whitespaces at beginning of a token
+ while (isWhitespace(c) && !eol) {
+ wsBuf.append((char) c);
+ c = in.read();
+ eol = isEndOfLine(c);
+ }
+ // ok, start of token reached: comment, encapsulated, or token
+ if (c == commentStart) {
+ // ignore everything till end of line and continue (incr linecount)
+ in.readLine();
+ tkn = nextToken();
+ } else if (c == delimiter) {
+ // empty token return TT_TOKEN("")
+ tkn.type = TT_TOKEN;
+ tkn.isReady = true;
+ } else if (eol) {
+ // empty token return TT_EORECORD("")
+ tkn.content.append("");
+ tkn.type = TT_EORECORD;
+ tkn.isReady = true;
+ } else if (c == encapsulator) {
+ // consume encapsulated token
+ encapsulatedTokenLexer(tkn, c);
+ } else if (isEndOfFile(c)) {
+ // end of file return TT_EOF()
+ tkn.content.append("");
+ tkn.type = TT_EORECORD;
+ // tkn.type = TT_EOF;
+ tkn.isReady = true;
+ } else {
+ // next token must be a simple token
+ // add removed blanks when not ignoring whitespace chars...
+ if (!this.ignoreLeadingWhitespaces) {
+ tkn.content.append(wsBuf.toString());
+ }
+ simpleTokenLexer(tkn, c);
+ }
+ }
+ return tkn;
+ }
+
+ /**
+ * A simple token lexer
+ *
+ * Simple token are tokens which are not surrounded by encapsulators.
+ * A simple token might contain escaped delimiters (as \, or \;). The
+ * token is finished when one of the following conditions become true:
+ * <ul>
+ * <li>end of line has been reached (TT_EORECORD)</li>
+ * <li>end of stream has been reached (TT_EOF)</li>
+ * <li>an unescaped delimiter has been reached (TT_TOKEN)</li>
+ * </ul>
+ *
+ * @param tkn the current token
+ * @param c the current character
+ * @return the filled token
+ *
+ * @throws IOException on stream access error
+ */
+ private Token simpleTokenLexer(Token tkn, int c) throws IOException {
+ StringBuffer wsBuf = new StringBuffer();
+ while (!tkn.isReady) {
+ if (isEndOfLine(c)) {
+ // end of record
+ tkn.type = TT_EORECORD;
+ tkn.isReady = true;
+ } else if (isEndOfFile(c)) {
+ // end of file
+ // tkn.type = TT_EOF;
+ tkn.type = TT_EORECORD;
+ tkn.isReady = true;
+ } else if (c == delimiter) {
+ // end of token
+ tkn.type = TT_TOKEN;
+ tkn.isReady = true;
+ } else if (c == '\\') {
+ // handle escaped delimiters (remove escaping)
+ if (in.lookAhead() == this.delimiter) {
+ tkn.content.append((char) in.read());
+ } else if (interpretUnicodeEscapes && in.lookAhead() == 'u') {
+ // interpret unicode escaped chars (like \u0070 -> p)
+ tkn.content.append((char) unicodeEscapeLexer(c));
+ } else {
+ tkn.content.append((char) c);
+ }
+ } else if (isWhitespace(c)) {
+ // gather whitespaces
+ // (as long as they are not at the beginning of a token)
+ if (tkn.content.length() > 0) {
+ wsBuf.append((char) c);
+ }
+ } else {
+ // prepend whitespaces (if we have)
+ if (wsBuf.length() > 0) {
+ tkn.content.append(wsBuf);
+ wsBuf.delete(0, wsBuf.length());
+ }
+ tkn.content.append((char) c);
+ }
+ // get the next char
+ if (!tkn.isReady) {
+ c = in.read();
+ }
+ }
+ return tkn;
+ }
+
+
+ /**
+ * An encapsulated token lexer
+ *
+ * Encapsulated tokens are surrounded by the given encapsulating-string.
+ * The encapsulator itself might be included in the token using a
+ * doubling syntax (as "", '') or using escaping (as in \", \').
+ * Whitespaces before and after an encapsulated token are ignored.
+ *
+ * @param tkn the current token
+ * @param c the current character
+ * @return a valid token object
+ * @throws IOException on invalid state
+ */
+ private Token encapsulatedTokenLexer(Token tkn, int c) throws IOException {
+ // save current line
+ int startLineNumber = getLineNumber();
+ // ignore the given delimiter
+ // assert c == delimiter;
+ c = in.read();
+ while (!tkn.isReady) {
+ if (c == encapsulator || c == '\\') {
+ // check lookahead
+ if (in.lookAhead() == encapsulator) {
+ // double or escaped encapsulator -> add single encapsulator to token
+ c = in.read();
+ tkn.content.append((char) c);
+ } else if (c == '\\' && in.lookAhead() == '\\') {
+ // doubled escape character -> add single escape char to stream
+ c = in.read();
+ tkn.content.append((char) c);
+ } else if (
+ interpretUnicodeEscapes
+ && c == '\\'
+ && in.lookAhead() == 'u') {
+ // interpret unicode escaped chars (like \u0070 -> p)
+ tkn.content.append((char) unicodeEscapeLexer(c));
+ } else {
+ // token finish mark reached: ignore ws till delimiter
+ while (!tkn.isReady) {
+ int n = in.lookAhead();
+ if (n == delimiter) {
+ tkn.type = TT_TOKEN;
+ tkn.isReady = true;
+ } else if (isEndOfFile(n)) {
+ // tkn.type = TT_EOF;
+ tkn.type = TT_EORECORD;
+ tkn.isReady = true;
+ } else if (isEndOfLine(n)) {
+ // ok eo token reached
+ tkn.type = TT_EORECORD;
+ tkn.isReady = true;
+ } else if (!isWhitespace(n)) {
+ // error invalid char between token and next delimiter
+ throw new IOException(
+ "(line " + getLineNumber()
+ + ") invalid char between encapsualted token end delimiter"
+ );
+ }
+ c = in.read();
+ }
+ }
+ } else if (isEndOfFile(c)) {
+ // error condition (end of file before end of token)
+ throw new IOException(
+ "(startline " + startLineNumber + ")"
+ + "eof reached before encapsulated token finished"
+ );
+ } else {
+ // consume character
+ tkn.content.append((char) c);
+ }
+ // get the next char
+ if (!tkn.isReady) {
+ c = in.read();
+ }
+ }
+ return tkn;
+ }
+
+
+ /**
+ * Decodes Unicode escapes
+ *
+ * Interpretation of "\\uXXXX" escape sequences
+ * where XXXX is a hex-number
+ * @param c
+ * @return the decoded character
+ * @throws IOException on wrong unicode escape sequence or read error
+ */
+ protected int unicodeEscapeLexer(int c) throws IOException {
+ int ret = 0;
+ // ignore 'u' (assume c==\ now) and read 4 hex digits
+ c = in.read();
+ StringBuffer code = new StringBuffer(4);
+ try {
+ for (int i = 0; i < 4; i++) {
+ c = in.read();
+ if (isEndOfFile(c) || isEndOfLine(c)) {
+ throw new NumberFormatException("number too short");
+ }
+ code.append((char) c);
+ }
+ ret = Integer.parseInt(code.toString(), 16);
+ } catch (NumberFormatException e) {
+ throw new IOException(
+ "(line " + getLineNumber() + ") Wrong unicode escape sequence found '"
+ + code.toString() + "'" + e.toString());
+ }
+ return ret;
+ }
+
+ // ======================================================
+ // strategy utilities
+ // ======================================================
+
+ /**
+ * Sets the "Default CSV" settings.
+ *
+ * The default csv settings are relatively restrictive but implement
+ * something like the "least-common-basis" of CSV.
+ *
+ * Values are separated by ',' (as the C in "CSV"). Complex values must
+ * be surrounded by '"'. Comments are not supported. Leading whitespaces
+ * are ignored, unicode escapes are not interpreted and empty lines
+ * are skiped.
+ */
+ public void setCSVStrategy() {
+ setStrategy(',', '"', (char) 0, true, false, true);
+ }
+
+ /**
+ * Sets the "Excel CSV" settings.
+ *
+ * There are companies out there which interpret "C" as an abbreviation for
+ * "Semicolon". For these companies the following settings might be
+ * appropriate:
+ * <p>
+ * Delimiter Semicolon ';', Complex-values surrounded by '"', leading
+ * whitespaces are not ignored and unicode escapes are not interpreted.
+ */
+ public void setExcelStrategy() {
+ setStrategy(';', '"', (char) 0, false, false, false);
+ }
+
+ /**
+ * Customized CSV strategy setter.
+ *
+ * @param delimiter a Char used for value separation
+ * @param encapsulator a Char used as value encapsulation marker
+ * @param commentStart a Char used for comment identification
+ * @param ignoreLeadingWhitespace TRUE when leading whitespaces should be
+ * ignored
+ * @param interpretUnicodeEscapes TRUE when unicode escapes should be
+ * interpreted
+ * @param ignoreEmptyLines TRUE when the parser should skip emtpy lines
+ */
+ public void setStrategy(
+ char delimiter,
+ char encapsulator,
+ char commentStart,
+ boolean ignoreLeadingWhitespace,
+ boolean interpretUnicodeEscapes,
+ boolean ignoreEmptyLines) {
+ this.setDelimiter(delimiter);
+ this.setEncapsulator(encapsulator);
+ this.setCommentStart(commentStart);
+ this.setIgnoreLeadingWhitespaces(ignoreLeadingWhitespace);
+ this.setUnicodeEscapeInterpretation(interpretUnicodeEscapes);
+ this.setIgnoreEmptyLines(ignoreEmptyLines);
+ }
+
+ /**
+ * Set the desired delimiter
+ *
+ * @param c a Char used for value separation
+ */
+ public void setDelimiter(char c) {
+ this.delimiter = c;
+ }
+
+ /**
+ * Gets the delimiter.
+ *
+ * @return the delimiter character
+ */
+ public char getDelimiter() {
+ return this.delimiter;
+ }
+
+ /**
+ * Set the desired encapsulator
+ *
+ * @param c a Char used as value encapsulation marker
+ */
+ public void setEncapsulator(char c) {
+ this.encapsulator = c;
+ }
+
+ /**
+ * Gets the encapsulator character.
+ *
+ * @return the encapsulator marker
+ */
+ public char getEncapsulator() {
+ return this.encapsulator;
+ }
+
+ /**
+ * Set the desired comment start character
+ *
+ * @param c a Char used for comment identification
+ */
+ public void setCommentStart(char c) {
+ this.commentStart = c;
+ }
+
+ /**
+ * Gets the comment identifier
+ *
+ * @return the comment identifier character
+ */
+ public char getCommentStart() {
+ return this.commentStart;
+ }
+
+ /**
+ * Enables unicode escape interpretation
+ *
+ * @param b TRUE when interpretation should be enabled
+ */
+ public void setUnicodeEscapeInterpretation(boolean b) {
+ this.interpretUnicodeEscapes = b;
+ }
+
+ /**
+ * Shows wether unicode interpretation is enabled
+ *
+ * @return TRUE when unicode interpretation is enabled
+ */
+ public boolean getUnicodeEscapeInterpretation() {
+ return this.interpretUnicodeEscapes;
+ }
+
+ /**
+ * Sets the ignore-leading-whitespaces behaviour.
+ *
+ * Should the lexer ignore leading whitespaces when parsing non
+ * encapsulated tokens
+ *
+ * @param b TRUE when leading whitespaces should be ignored
+ */
+ public void setIgnoreLeadingWhitespaces(boolean b) {
+ this.ignoreLeadingWhitespaces = b;
+ }
+
+ /**
+ * Shows wether unicode interpretation is enabled
+ *
+ * @return TRUE when unicode interpretation is enabled
+ */
+ public boolean getIgnoreLeadingWhitespaces() {
+ return this.ignoreLeadingWhitespaces;
+ }
+
+ /**
+ * Sets the ignore-empty-line behaviour.
+ *
+ * When set to 'true' empty lines in the input will be ignored.
+ *
+ * @param b
+ */
+ public void setIgnoreEmptyLines(boolean b) {
+ this.ignoreEmptyLines = b;
+ }
+
+ // ======================================================
+ // Character class checker
+ // ======================================================
+
+ /**
+ * @return true if the given char is a whitespache character
+ */
+ private boolean isWhitespace(int c) {
+ return Character.isWhitespace((char) c);
+ }
+
+ /**
+ * Greedy - accepts \n and \r\n
+ * This checker consumes silently the second control-character...
+ *
+ * @return true if the given character is a line-terminator
+ */
+ private boolean isEndOfLine(int c) throws IOException {
+ // check if we have \r\n...
+ if (c == '\r') {
+ if (in.lookAhead() == '\n') {
+ // note: does not change c outside of this method !!
+ c = in.read();
+ }
+ }
+ return (c == '\n');
+ }
+
+ /**
+ * @return true if the given character indicates end of file
+ */
+ private boolean isEndOfFile(int c) {
+ return c == ExtendedBufferedReader.END_OF_STREAM;
+ }
+}
\ No newline at end of file
Propchange: jakarta/commons/trunks-sandbox/csv/src/java/ch/netcetera/wake/core/format/csv/CSVParser.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: jakarta/commons/trunks-sandbox/csv/src/java/ch/netcetera/wake/core/format/csv/CSVPrinter.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/trunks-sandbox/csv/src/java/ch/netcetera/wake/core/format/csv/CSVPrinter.java?rev=357300&view=auto
==============================================================================
--- jakarta/commons/trunks-sandbox/csv/src/java/ch/netcetera/wake/core/format/csv/CSVPrinter.java (added)
+++ jakarta/commons/trunks-sandbox/csv/src/java/ch/netcetera/wake/core/format/csv/CSVPrinter.java Fri Dec 16 21:41:27 2005
@@ -0,0 +1,358 @@
+/*
+ * Copyright (C) 2002-2004 by Netcetera AG.
+ * All rights reserved.
+ *
+ * The copyright to the computer program(s) herein is the property of
+ * Netcetera AG, Switzerland. The program(s) may be used and/or copied
+ * only with the written permission of Netcetera AG or in accordance
+ * with the terms and conditions stipulated in the agreement/contract
+ * under which the program(s) have been supplied.
+ *
+ * @(#) $Id: CSVPrinter.java,v 1.4 2004/08/10 12:35:27 rgrunder Exp $
+ */
+
+package ch.netcetera.wake.core.format.csv;
+
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.Writer;
+
+/**
+ * Print values as a comma separated list.
+ */
+public class CSVPrinter {
+
+ /** The place that the values get written. */
+ protected PrintWriter out;
+
+ /** True if we just began a new line. */
+ protected boolean newLine = true;
+
+ /** Character used to start comments. (Default is '#') */
+ protected char commentStart = '#';
+
+ /** Character used to separate entities. (Default is ',') */
+ protected char separatorChar = ',';
+
+ /**
+ * 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);
+ }
+ }
+
+
+ /**
+ * Create a printer that will print values to the given
+ * stream. Character to byte conversion is done using
+ * the default character encoding.
+ *
+ * @param out stream to which to print.
+ * @param commentStart Character used to start comments.
+ */
+ public CSVPrinter(OutputStream out, char commentStart) {
+ this(out);
+ this.commentStart = commentStart;
+ }
+
+
+ /**
+ * Create a printer that will print values to the given
+ * stream.
+ *
+ * @param out stream to which to print.
+ * @param commentStart Character used to start comments.
+ */
+ public CSVPrinter(Writer out, char commentStart) {
+ this(out);
+ this.commentStart = commentStart;
+ }
+
+ /**
+ * Gets the comment start character.
+ *
+ * @return the commentStart character
+ */
+ public char getCommentStart() {
+ return commentStart;
+ }
+
+ /**
+ * Sets the comment start character.
+ *
+ * @param commentStart commentStart character to set.
+ */
+ public void setCommentStart(char commentStart) {
+ this.commentStart = commentStart;
+ }
+
+ /**
+ * Gets the separator character.
+ *
+ * @return Returns the separatorChar.
+ */
+ public char getSeparatorChar() {
+ return separatorChar;
+ }
+ /**
+ * Sets the separator character.
+ *
+ * @param separatorChar The separatorChar to set.
+ */
+ public void setSeparatorChar(char separatorChar) {
+ this.separatorChar = separatorChar;
+ }
+
+ /**
+ * 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 (!newLine) {
+ out.println();
+ }
+ out.print(commentStart);
+ 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(commentStart);
+ 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 == separatorChar || c == '\n' || c == '\r') {
+ quote = true;
+ }
+ }
+ 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(separatorChar);
+ }
+ if (quote) {
+ out.print(escapeAndQuote(value));
+ } else {
+ out.print(value);
+ }
+ out.flush();
+ }
+
+
+ /**
+ * Converts an array of string values into a single CSV line. All
+ * <code>null</code> values are converted to the string <code>"null"</code>,
+ * all strings equal to <code>"null"</code> will additionally get quotes
+ * around.
+ *
+ * @param values the value array
+ * @return the CSV string, will be an empty string if the length of the
+ * value array is 0
+ */
+ public static String printLine(String[] values) {
+
+ // set up a CSVPrinter
+ StringWriter csvWriter = new StringWriter();
+ CSVPrinter csvPrinter = new CSVPrinter(csvWriter);
+
+ // check for null values an "null" as strings and convert them
+ // into the strings "null" and "\"null\""
+ for (int i = 0; i < values.length; i++) {
+ if (values[i] == null) {
+ values[i] = "null";
+ } else if (values[i].equals("null")) {
+ values[i] = "\"null\"";
+ }
+ }
+
+ // convert to CSV
+ csvPrinter.println(values);
+
+ // as the resulting string has \r\n at the end, we will trim that away
+ return csvWriter.toString().trim();
+ }
+
+
+ /**
+ * 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 static 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('"');
+ for (int i = 0; i < value.length(); i++) {
+ char c = value.charAt(i);
+ switch (c) {
+ case '\"' :
+ sb.append("\\\"");
+ break;
+ case '\n' :
+ sb.append("\\n");
+ break;
+ case '\r' :
+ sb.append("\\r");
+ break;
+ case '\\' :
+ sb.append("\\\\");
+ break;
+ default :
+ sb.append(c);
+ }
+ }
+ sb.append('"');
+ return sb.toString();
+ }
+
+}
Propchange: jakarta/commons/trunks-sandbox/csv/src/java/ch/netcetera/wake/core/format/csv/CSVPrinter.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: jakarta/commons/trunks-sandbox/csv/src/java/ch/netcetera/wake/core/format/csv/ExtendedBufferedReader.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/trunks-sandbox/csv/src/java/ch/netcetera/wake/core/format/csv/ExtendedBufferedReader.java?rev=357300&view=auto
==============================================================================
--- jakarta/commons/trunks-sandbox/csv/src/java/ch/netcetera/wake/core/format/csv/ExtendedBufferedReader.java (added)
+++ jakarta/commons/trunks-sandbox/csv/src/java/ch/netcetera/wake/core/format/csv/ExtendedBufferedReader.java Fri Dec 16 21:41:27 2005
@@ -0,0 +1,304 @@
+/*
+* playground
+*
+* Copyright (C) 2004 by Netcetera AG.
+* All rights reserved.
+*
+* The copyright to the computer program(s) herein is the property of
+* Netcetera AG, Switzerland. The program(s) may be used and/or copied
+* only with the written permission of Netcetera AG or in accordance
+* with the terms and conditions stipulated in the agreement/contract
+* under which the program(s) have been supplied.
+*
+* @(#) $Id: ExtendedBufferedReader.java,v 1.3 2004/09/30 13:37:10 hagger Exp $
+*/
+package ch.netcetera.wake.core.format.csv;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.Reader;
+
+/**
+ * ExtendedBufferedReader
+ *
+ * A special reader decorater which supports more
+ * sophisticated access to the underlying reader object.
+ *
+ * In especialy the reader supports a look-ahead option,
+ * which allows you to see the next char returned by
+ * next().
+ * Furthermore the skip-method supports skipping until
+ * (but excluding) a given char. Similar functionality
+ * is supported by the reader as well.
+ *
+ */
+public class ExtendedBufferedReader extends BufferedReader {
+
+
+ /** the end of stream symbol */
+ public static final int END_OF_STREAM = -1;
+ /** undefined state for the lookahead char */
+ public static final int UNDEFINED = -2;
+
+ /** the lookahead chars */
+ private int lookaheadChar = UNDEFINED;
+ /** the last char returned */
+ private int lastChar = UNDEFINED;
+ /** the line counter */
+ private int lineCounter = 0;
+ /**
+ * Created extended buffered reader using default buffer-size
+ *
+ */
+ public ExtendedBufferedReader(Reader r) {
+ super(r);
+ /* note uh: do not fetch the first char here,
+ * because this might block the method!
+ */
+ }
+
+ /**
+ * Create extended buffered reader using the given buffer-size
+ */
+ public ExtendedBufferedReader(Reader r, int bufSize) {
+ super(r, bufSize);
+ /* note uh: do not fetch the first char here,
+ * because this might block the method!
+ */
+ }
+
+ /**
+ * Reads the next char from the input stream.
+ * @return the next char or END_OF_STREAM if end of stream has been reached.
+ */
+ public int read() throws IOException {
+ // initalize the lookahead
+ if (lookaheadChar == UNDEFINED) {
+ lookaheadChar = super.read();
+ }
+ lastChar = lookaheadChar;
+ lookaheadChar = super.read();
+ if (lastChar == '\n') {
+ lineCounter++;
+ }
+ return lastChar;
+ }
+
+ /**
+ * Returns the last read character again.
+ *
+ * @return the last read char or UNDEFINED
+ */
+ public int readAgain() {
+ return lastChar;
+ }
+
+ /**
+ * Non-blocking reading of len chars into buffer buf starting
+ * at bufferposition off.
+ *
+ * performs an iteratative read on the underlying stream
+ * as long as the following conditions hold:
+ * - less than len chars have been read
+ * - end of stream has not been reached
+ * - next read is not blocking
+ *
+ * @return nof chars actually read or END_OF_STREAM
+ */
+ public int read(char[] buf, int off, int len) throws IOException {
+ // do not claim if len == 0
+ if (len == 0) {
+ return 0;
+ }
+
+ // init lookahead, but do not block !!
+ if (lookaheadChar == UNDEFINED) {
+ if (ready()) {
+ lookaheadChar = super.read();
+ } else {
+ return -1;
+ }
+ }
+ // 'first read of underlying stream'
+ if (lookaheadChar == -1) {
+ return -1;
+ }
+ // continue until the lookaheadChar would block
+ int cOff = off;
+ while (len > 0 && ready()) {
+ if (lookaheadChar == -1) {
+ // eof stream reached, do not continue
+ return cOff - off;
+ } else {
+ buf[cOff++] = (char) lookaheadChar;
+ if (lookaheadChar == '\n') {
+ lineCounter++;
+ }
+ lastChar = lookaheadChar;
+ lookaheadChar = super.read();
+ len--;
+ }
+ }
+ return cOff - off;
+ }
+
+ /**
+ * Reads all characters up to (but not including) the given character.
+ *
+ * @param c the character to read up to
+ * @return the string up to the character <code>c</code>
+ * @throws IOException
+ */
+ public String readUntil(char c) throws IOException {
+ if (lookaheadChar == UNDEFINED) {
+ lookaheadChar = super.read();
+ }
+ StringBuffer ret = new StringBuffer("");
+ while (lookaheadChar != c && lookaheadChar != END_OF_STREAM) {
+ ret.append((char) lookaheadChar);
+ if (lookaheadChar == '\n') {
+ lineCounter++;
+ }
+ lastChar = lookaheadChar;
+ lookaheadChar = super.read();
+ }
+ return ret.toString();
+ }
+
+ /**
+ * @return A String containing the contents of the line, not
+ * including any line-termination characters, or null
+ * if the end of the stream has been reached
+ */
+ public String readLine() throws IOException {
+
+ if (lookaheadChar == UNDEFINED) {
+ lookaheadChar = super.read();
+ }
+
+ StringBuffer ret = new StringBuffer("");
+
+ // return null if end of stream has been reached
+ if (lookaheadChar == END_OF_STREAM) {
+ return null;
+ }
+ // do we have a line termination already
+ char laChar = (char) lookaheadChar;
+ if (laChar == '\n' || laChar == '\r') {
+ lastChar = lookaheadChar;
+ lookaheadChar = super.read();
+ // ignore '\r\n' as well
+ if ((char) lookaheadChar == '\n') {
+ lastChar = lookaheadChar;
+ lookaheadChar = super.read();
+ }
+ lineCounter++;
+ return ret.toString();
+ }
+
+ // create the rest-of-line return and update the lookahead
+ ret.append(String.valueOf(laChar));
+ String restOfLine = super.readLine();
+ lastChar = lookaheadChar;
+ lookaheadChar = super.read();
+ if (restOfLine != null) {
+ ret.append(restOfLine);
+ }
+ lineCounter++;
+ return ret.toString();
+ }
+
+ /**
+ * Skips char in the stream
+ *
+ * ATTENTION: invalidates the line-counter !!!!!
+ *
+ * @return nof skiped chars
+ */
+ public long skip(long n) throws IllegalArgumentException, IOException {
+
+ if (lookaheadChar == UNDEFINED) {
+ lookaheadChar = super.read();
+ }
+
+ // illegal argument
+ if (n < 0) {
+ throw new IllegalArgumentException("negative argument not supported");
+ }
+
+ // no skipping
+ if (n == 0 || lookaheadChar == END_OF_STREAM) {
+ return 0;
+ }
+
+ // skip and reread the lookahead-char
+ long skiped = 0;
+ if (n > 1) {
+ skiped = super.skip(n - 1);
+ }
+ lookaheadChar = super.read();
+ // fixme uh: we should check the skiped sequence for line-terminations...
+ lineCounter = Integer.MIN_VALUE;
+ return skiped + 1;
+ }
+
+ /**
+ * Skips all chars in the input until (but excluding) the given char
+ *
+ * @param c
+ * @return
+ * @throws IllegalArgumentException
+ * @throws IOException
+ */
+ public long skipUntil(char c) throws IllegalArgumentException, IOException {
+ if (lookaheadChar == UNDEFINED) {
+ lookaheadChar = super.read();
+ }
+ long counter = 0;
+ while (lookaheadChar != c && lookaheadChar != END_OF_STREAM) {
+ if (lookaheadChar == '\n') {
+ lineCounter++;
+ }
+ lookaheadChar = super.read();
+ counter++;
+ }
+ return counter;
+ }
+
+ /**
+ * Returns the next char in the stream without consuming it.
+ *
+ * Remember the next char read by read(..) will always be
+ * identical to lookAhead().
+ *
+ * @return the next char (without consuming it) or END_OF_STREAM
+ */
+ public int lookAhead() throws IOException {
+ if (lookaheadChar == UNDEFINED) {
+ lookaheadChar = super.read();
+ }
+ return lookaheadChar;
+ }
+
+
+ /**
+ * Returns the nof line read
+ * ATTENTION: the skip-method does invalidate the line-number counter
+ *
+ * @return the current-line-number (or -1)
+ */
+ public int getLineNumber() {
+ if (lineCounter > -1) {
+ return lineCounter;
+ } else {
+ return -1;
+ }
+ }
+ public boolean markSupported() {
+ /* note uh: marking is not supported, cause we cannot
+ * see into the future...
+ */
+ return false;
+ }
+
+}
Propchange: jakarta/commons/trunks-sandbox/csv/src/java/ch/netcetera/wake/core/format/csv/ExtendedBufferedReader.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: jakarta/commons/trunks-sandbox/csv/src/java/ch/netcetera/wake/core/format/csv/package.html
URL: http://svn.apache.org/viewcvs/jakarta/commons/trunks-sandbox/csv/src/java/ch/netcetera/wake/core/format/csv/package.html?rev=357300&view=auto
==============================================================================
--- jakarta/commons/trunks-sandbox/csv/src/java/ch/netcetera/wake/core/format/csv/package.html (added)
+++ jakarta/commons/trunks-sandbox/csv/src/java/ch/netcetera/wake/core/format/csv/package.html Fri Dec 16 21:41:27 2005
@@ -0,0 +1,76 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<!--
+ Copyright (C) 2004 by Netcetera AG.
+ All rights reserved.
+
+ The copyright to the computer program(s) herein is the property of
+ Netcetera AG, Switzerland. The program(s) may be used and/or copied
+ only with the written permission of Netcetera AG or in accordance
+ with the terms and conditions stipulated in the agreement/contract
+ under which the program(s) have been supplied.
+
+ @(#) $Id: package.html,v 1.3 2004/05/18 09:43:23 uhardegg Exp $
+-->
+<html>
+<head>
+<title>ch.netcetera.wake.core.format.csv package</title>
+<link rel="stylesheet" type="text/css" href="../../../../../../stylesheet.css" title="Style">
+</head>
+<body>
+<h3>WAKE CSV Format Support</h3>
+
+<p>CSV (or its dialects) are widely used as interfaces to legacy systems or
+ manual data-imports. Basically CSV stands for "Comma Separated Values" but
+ this simple leads to more confusion than definitions.</p>
+
+<p>Common to all file dialects is its basic structure: The CSV data-format
+ is record oriented, whereas each record starts on a new textual line. A
+ record is build of a list of values. Keep in mind that not all records
+ must have an equal number of values:</p>
+<pre>
+ csv := records*
+ record := values*
+</pre>
+
+<p>The following list contains the csv aspects the WAKE CSV parser supports:</p>
+<dl>
+ <dt>Separators</dt>
+ <dd>The record separators are hardcoded and cannot be changed. The must be '\n' or '\r\n'.
+ The separator (or delimiters) for values is freely configurable (default ',').</dd>
+
+ <dt>Comments</dt>
+ <dd>Some CSV-dialects support a simple comment syntax. A comment is a record
+ which must start with a designated character (the commentStarter). A record
+ of this kind is treated as comment and gets removed from the input (default '(char)0')</dd>
+
+ <dt>Simple values</dt>
+ <dd>A simple value consist of all characters (except the separator) until
+ (but not including) the next separator or a record-terminator. Optionally
+ all leading whitespaces of a simple value can be ignored (default: true).</dd>
+
+ <dt>Complex values</dt>
+ <dd>Complex values are encapsulated within the defined encapsulator character (default '"').
+ The encapsulator itself must be escaped by '\' or doubled when used inside complex values.
+ Complex values preserve all kind of formatting (including newlines -> multiline-values)</dd>
+
+ <dt>Unicode escapes</dt>
+ <dd>Some non-unicode CSVs use unicode escaping sequences for certain unicode characters. The standard
+ unicode-escaping is '\uXXXX' whereas XXXX is the unicode-character-code in hex-format. The parser
+ can optionally resolve unicode escapes (default: disabled).</dd>
+ <dt>Empty line skipping</dt>
+ <dd>Optionally empty lines in CSV files might be skiped. Not skiping empty lines will return
+ an empty record on '\nEOF' combinations.</dd>
+</dl>
+
+<p>In addition to individually defined dialects, two predefined dialects (strict-csv, and excel-csv)
+ can be set directly.</p>
+
+<p>Example usage:</p>
+<pre><blockquote>
+String[] parsedLine = CSVParser.parseLine("a,b,c");
+for (int i = 0; i < parsedLine.length; ++i) {
+ System.out.println("value " + i + "=" + parsedLine[i]);
+}
+</blockquote></pre>
+</body>
+</html>
Propchange: jakarta/commons/trunks-sandbox/csv/src/java/ch/netcetera/wake/core/format/csv/package.html
------------------------------------------------------------------------------
svn:eol-style = native
Added: jakarta/commons/trunks-sandbox/csv/src/test/ch/netcetera/wake/core/format/FormatTests.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/trunks-sandbox/csv/src/test/ch/netcetera/wake/core/format/FormatTests.java?rev=357300&view=auto
==============================================================================
--- jakarta/commons/trunks-sandbox/csv/src/test/ch/netcetera/wake/core/format/FormatTests.java (added)
+++ jakarta/commons/trunks-sandbox/csv/src/test/ch/netcetera/wake/core/format/FormatTests.java Fri Dec 16 21:41:27 2005
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2004 by Netcetera AG.
+ * All rights reserved.
+ *
+ * The copyright to the computer program(s) herein is the property of
+ * Netcetera AG, Switzerland. The program(s) may be used and/or copied
+ * only with the written permission of Netcetera AG or in accordance
+ * with the terms and conditions stipulated in the agreement/contract
+ * under which the program(s) have been supplied.
+ *
+ * @(#) $Id: FormatTests.java,v 1.3 2004/08/10 12:38:45 rgrunder Exp $
+ */
+package ch.netcetera.wake.core.format;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import ch.netcetera.wake.core.format.csv.CSVParserTest;
+import ch.netcetera.wake.core.format.csv.CSVPrinterTest;
+import ch.netcetera.wake.core.format.csv.ExtendedBufferedReaderTest;
+
+
+public final class FormatTests {
+
+ protected FormatTests() {
+ // empty
+ }
+
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(suite());
+ }
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite("wake.core.format tests");
+ suite.addTest(ExtendedBufferedReaderTest.suite());
+ suite.addTest(CSVParserTest.suite());
+ suite.addTest(CSVPrinterTest.suite());
+ return suite;
+ }
+}
Propchange: jakarta/commons/trunks-sandbox/csv/src/test/ch/netcetera/wake/core/format/FormatTests.java
------------------------------------------------------------------------------
svn:eol-style = native
---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org