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 &lt; 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