You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@cocoon.apache.org by th...@apache.org on 2012/02/14 14:16:00 UTC

svn commit: r1243911 - in /cocoon/cocoon3/trunk/cocoon-optional/src: main/java/org/apache/cocoon/optional/pipeline/components/sax/csv/ main/java/org/apache/cocoon/pipeline/components/parameters/ main/resources/META-INF/cocoon/spring-optional/ test/java...

Author: thorsten
Date: Tue Feb 14 13:15:59 2012
New Revision: 1243911

URL: http://svn.apache.org/viewvc?rev=1243911&view=rev
Log:
COCOON3-90
Migrating CSVGenerator fom 2.2 to c3.
Extending the generator to work as well with string input and not only with url.
Adding basic test case.

Added:
    cocoon/cocoon3/trunk/cocoon-optional/src/main/java/org/apache/cocoon/optional/pipeline/components/sax/csv/
    cocoon/cocoon3/trunk/cocoon-optional/src/main/java/org/apache/cocoon/optional/pipeline/components/sax/csv/CSVGenerator.java
    cocoon/cocoon3/trunk/cocoon-optional/src/test/java/org/apache/cocoon/optional/pipeline/components/sax/csv/
    cocoon/cocoon3/trunk/cocoon-optional/src/test/java/org/apache/cocoon/optional/pipeline/components/sax/csv/CSVGeneratorTest.java
Modified:
    cocoon/cocoon3/trunk/cocoon-optional/src/main/java/org/apache/cocoon/pipeline/components/parameters/Parameters.java
    cocoon/cocoon3/trunk/cocoon-optional/src/main/resources/META-INF/cocoon/spring-optional/cocoon-optional-generators.xml

Added: cocoon/cocoon3/trunk/cocoon-optional/src/main/java/org/apache/cocoon/optional/pipeline/components/sax/csv/CSVGenerator.java
URL: http://svn.apache.org/viewvc/cocoon/cocoon3/trunk/cocoon-optional/src/main/java/org/apache/cocoon/optional/pipeline/components/sax/csv/CSVGenerator.java?rev=1243911&view=auto
==============================================================================
--- cocoon/cocoon3/trunk/cocoon-optional/src/main/java/org/apache/cocoon/optional/pipeline/components/sax/csv/CSVGenerator.java (added)
+++ cocoon/cocoon3/trunk/cocoon-optional/src/main/java/org/apache/cocoon/optional/pipeline/components/sax/csv/CSVGenerator.java Tue Feb 14 13:15:59 2012
@@ -0,0 +1,534 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+package org.apache.cocoon.optional.pipeline.components.sax.csv;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.CharArrayWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringReader;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.cocoon.pipeline.components.parameters.Parameters;
+import org.apache.cocoon.sax.AbstractSAXGenerator;
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+/**
+ * <p>
+ * A simple parser converting a Comma Separated Values (CSV) file into XML.
+ * </p>
+ * 
+ * <p>
+ * This parser is controlled by the following sitemap parameters:
+ * </p>
+ * 
+ * <ul>
+ * <li>
+ * <b>process-headers</b>: whether the first line in the CSV is considered to be
+ * the header defining column names (the resulting output will be different if
+ * this is <i>true</i> or <i>false</i> (default: <i>false</i>).</li>
+ * <li>
+ * <b>max-records</b>: the maximum number of records to read (default: <i>-1</i>
+ * read all records).</li>
+ * <li>
+ * <b>encoding</b>: the character encoding (UTF-8, ISO8859-1, ...) used to
+ * interpret the input CSV source file (default: <i>system default</i>).</li>
+ * <li>
+ * <b>separator</b>: the field-separator character in the CSV file (comma, tab,
+ * ...) (default: <i>,</i> <small>comma</small>).</li>
+ * <li>
+ * <b>escape</b>: the character used to escape fields, or part of them, in the
+ * CSV file (default: <i>"</i> <small>quote</small>).</li>
+ * <li>
+ * <b>buffer-size</b>: the size of the buffer used for reading the source CSV
+ * file (default: <i>4096 bytes</i>).</li>
+ * </ul>
+ * 
+ * <p>
+ * The generated output will look something like the following:
+ * </p>
+ * 
+ * <pre>
+ * &lt;?xml version="1.0" encoding="ISO-8859-1"?&gt;
+ * &lt;csv:document xmlns:csv="http://apache.org/cocoon/csv/1.0"&gt;
+ *   &lt;csv:header&gt;
+ *     &lt;csv:column number="1"&gt;Column A&lt;/csv:column&gt;
+ *     &lt;csv:column number="2"&gt;Column B&lt;/csv:column&gt;
+ *     &lt;csv:column number="3"&gt;Column C&lt;/csv:column&gt;
+ *   &lt;/csv:header&gt;
+ *   &lt;csv:record number="1"&gt;
+ *     &lt;csv:field number="1" column="Column A"&gt;Field A1&lt;/csv:field&gt;
+ *     &lt;csv:field number="2" column="Column B"&gt;Field B1&lt;/csv:field&gt;
+ *     &lt;csv:field number="3" column="Column C"&gt;Field C1&lt;/csv:field&gt;
+ *   &lt;/csv:record&gt;
+ *   &lt;csv:record number="2"&gt;
+ *     &lt;csv:field number="1" column="Column A"&gt;Field A2&lt;/csv:field&gt;
+ *     &lt;csv:field number="2" column="Column B"&gt;Field B2&lt;/csv:field&gt;
+ *     &lt;csv:field number="3" column="Column C"&gt;Field C2&lt;/csv:field&gt;
+ *   &lt;/csv:record&gt;
+ * &lt;/csv:document&gt;
+ * </pre>
+ * 
+ * <p>
+ * Note that this generator has been thoroughly tested with CSV files generated
+ * by <a href="http://office.microsoft.com/" target="_new">Microsoft Excel</a>.
+ * Unfortunately no official CSV specification has ever been published by any
+ * standard body, so the interpretation of the format might be slightly
+ * different in cases.
+ * </p>
+ */
+public class CSVGenerator extends AbstractSAXGenerator {// implements
+                                                        // CachingPipelineComponent
+
+    /**
+     * <p>
+     * The namespace URI of XML generated by this instance.
+     * </p>
+     */
+    public static final String NAMESPACE_URI = "http://apache.org/cocoon/csv/1.0";
+    /**
+     * <p>
+     * The namespace prefix of XML generated by this instance.
+     * </p>
+     */
+    public static final String NAMESPACE_PREFIX = "csv";
+
+    /**
+     * <p>
+     * The default encoding configured in the Java VM.
+     * </p>
+     */
+    private static final String DEFAULT_ENCODING = new InputStreamReader(
+            new ByteArrayInputStream(new byte[0])).getEncoding();
+    /**
+     * <p>
+     * The default field separator character.
+     * </p>
+     */
+    private static final String DEFAULT_SEPARATOR = ",";
+    /**
+     * <p>
+     * The default field separator character.
+     * </p>
+     */
+    private static final String DEFAULT_ESCAPE = "\"";
+    /**
+     * <p>
+     * The default field separator character.
+     * </p>
+     */
+    private static final int DEFAULT_BUFFER_SIZE = 4096;
+    private static final int UNLIMITED_MAXRECORDS = -1;
+    /**
+     * <p>
+     * A string used for indenting.
+     * </p>
+     */
+    private static final char INDENT_STRING[] = "\n          ".toCharArray();
+
+    /**
+     * <p>
+     * The encoding used to read the CSV resource from a stream.
+     * </p>
+     */
+    private String encoding = DEFAULT_ENCODING;
+    /**
+     * <p>
+     * The character used to separate fields.
+     * </p>
+     */
+    private char separator = DEFAULT_SEPARATOR.charAt(0);
+    /**
+     * <p>
+     * The character used to initiate and terminate esacaped sequences.
+     * </p>
+     */
+    private char escape = DEFAULT_ESCAPE.charAt(0);
+    /**
+     * <p>
+     * The size of the buffer used to read the input.
+     * </p>
+     */
+    private int buffersize = DEFAULT_BUFFER_SIZE;
+    /**
+     * <p>
+     * The current field (column) number in the current record.
+     * </p>
+     */
+    private int fieldnumber = 1;
+    /**
+     * <p>
+     * The current record (line) number in the current CSV.
+     * </p>
+     */
+    private int recordnumber = 1;
+    /**
+     * <p>
+     * The maximum number of records to read (-1 = read all records)
+     * </p>
+     */
+    private int maxrecords;
+    /**
+     * <p>
+     * A flag indicating whether the &lt;record&gt; tag was opened.
+     * </p>
+     */
+    private boolean openrecord = false;
+    /**
+     * <p>
+     * The character buffer for the current field.
+     * </p>
+     */
+    private CharArrayWriter buffer = null;
+    /**
+     * <p>
+     * A map of all known columns or null if no headers are processed.
+     * </p>
+     */
+    private Map columns = null;
+    private URL url;
+    private String sourceString;
+
+    /**
+     * <p>
+     * Create a new {@link CSVGenerator} instance.
+     * </p>
+     */
+    public CSVGenerator() {
+        super();
+    }
+
+    public CSVGenerator(String csv) {
+        super();
+        this.sourceString = csv;
+    }
+
+    public void recycle() {
+        this.encoding = DEFAULT_ENCODING;
+        this.separator = DEFAULT_SEPARATOR.charAt(0);
+        this.escape = DEFAULT_ESCAPE.charAt(0);
+        this.buffersize = DEFAULT_BUFFER_SIZE;
+        this.buffer = null;
+        this.columns = null;
+        this.recordnumber = 1;
+        this.fieldnumber = 1;
+        this.openrecord = false;
+    }
+
+    @Override
+    public void setConfiguration(
+            final Map<String, ? extends Object> configuration) {
+        final Parameters parameters = new Parameters(configuration);
+        boolean header = parameters.getParameterAsBoolean("process-headers",
+                false);
+        this.url = (URL) configuration.get("source");
+
+        this.encoding = (String) parameters.getParameter("encoding",
+                DEFAULT_ENCODING);
+        this.separator = ((String) parameters.getParameter("separator",
+                DEFAULT_SEPARATOR)).charAt(0);
+        this.escape = ((String) parameters.getParameter("escape",
+                DEFAULT_ESCAPE)).charAt(0);
+        this.buffersize = parameters.getAsInteger("buffer-size",
+                DEFAULT_BUFFER_SIZE);
+        this.maxrecords = parameters.getAsInteger("max-records",
+                UNLIMITED_MAXRECORDS);
+        this.buffer = new CharArrayWriter();
+        this.columns = (header ? new HashMap() : null);
+        this.recordnumber = (header ? 0 : 1);
+        this.fieldnumber = 1;
+        this.openrecord = false;
+    }
+
+    @Override
+    public void execute() {
+
+        CSVReader csv = null;
+        try {
+            if (null == sourceString) { /*
+                                * Create a new Reader correctly decoding the
+                                * source stream
+                                */
+                csv = new CSVReader(this.url, this.encoding, this.buffersize);
+            }else{
+                csv = new CSVReader(sourceString, this.buffersize);
+            }
+            /* Start the document */
+            this.getSAXConsumer().setDocumentLocator(csv);
+            this.getSAXConsumer().startDocument();
+            this.getSAXConsumer().startPrefixMapping(NAMESPACE_PREFIX,
+                    NAMESPACE_URI);
+            this.indent(0);
+            this.startElement("document");
+
+            /* Allocate buffer and status for parsing */
+            boolean unescaped = true;
+            int prev = -1;
+            int curr = -1;
+
+            /* Parse the file reading characters one-by-one */
+            while ((curr = csv.read()) >= 0
+                    && (this.maxrecords == UNLIMITED_MAXRECORDS || recordnumber <= this.maxrecords)) {
+
+                /* Process any occurrence of the escape character */
+                if (curr == this.escape) {
+                    if ((unescaped) && (prev == this.escape)) {
+                        this.buffer.write(this.escape);
+                    }
+                    unescaped = !unescaped;
+                    prev = curr;
+                    continue;
+                }
+
+                /* Process any occurrence of the field separator */
+                if ((unescaped) && (curr == this.separator)) {
+                    this.dumpField();
+                    prev = curr;
+                    continue;
+                }
+
+                /* Process newline characters */
+                if ((unescaped) && ((curr == '\r') || (curr == '\n'))) {
+                    this.dumpField();
+                    this.dumpRecord();
+
+                    /* Record numbering */
+                    if (((curr == '\n') && (prev != '\r')) || (curr == '\r')) {
+                        this.recordnumber++;
+                    }
+
+                    /* Nothing else to do */
+                    prev = curr;
+                    continue;
+                }
+
+                /* Any other character simply gets added to the buffer */
+                this.buffer.write(curr);
+                prev = curr;
+            }
+
+            /* Terminate any hanging open record element (just in case) */
+            this.dumpField();
+            this.dumpRecord();
+
+            /* Terminate the document */
+            this.indent(0);
+            this.endElement("document");
+            this.getSAXConsumer().endPrefixMapping(NAMESPACE_PREFIX);
+            this.getSAXConsumer().endDocument();
+
+        } catch (IOException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        } catch (SAXException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        } finally {
+            if (null != csv) {
+                try {
+                    csv.close();
+                } catch (IOException e) {
+                    // TODO Auto-generated catch block
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+
+    private void dumpField() throws SAXException {
+        if (this.buffer.size() < 1) {
+            this.fieldnumber++;
+            return;
+        }
+
+        if (!this.openrecord) {
+            this.indent(4);
+
+            if (this.recordnumber > 0) {
+                AttributesImpl attributes = new AttributesImpl();
+                String value = Integer.toString(this.recordnumber);
+                attributes.addAttribute("", "number", "number", "CDATA", value);
+                this.startElement("record", attributes);
+            } else {
+                this.startElement("header");
+            }
+            this.openrecord = true;
+        }
+
+        /* Enclode the field in the proper element */
+        String element = "field";
+        char array[] = this.buffer.toCharArray();
+        this.indent(8);
+
+        AttributesImpl attributes = new AttributesImpl();
+        String value = Integer.toString(this.fieldnumber);
+        attributes.addAttribute("", "number", "number", "CDATA", value);
+
+        if (this.recordnumber < 1) {
+            this.columns.put(new Integer(this.fieldnumber), new String(array));
+            element = "column";
+        } else if (this.columns != null) {
+            String header = (String) this.columns.get(new Integer(
+                    this.fieldnumber));
+            if (header != null) {
+                attributes
+                        .addAttribute("", "column", "column", "CDATA", header);
+            }
+        }
+
+        this.startElement(element, attributes);
+        this.getSAXConsumer().characters(array, 0, array.length);
+        this.endElement(element);
+        this.buffer.reset();
+
+        this.fieldnumber++;
+    }
+
+    private void dumpRecord() throws SAXException {
+        if (this.openrecord) {
+            this.indent(4);
+            if (this.recordnumber > 0) {
+                this.endElement("record");
+            } else {
+                this.endElement("header");
+            }
+            this.openrecord = false;
+        }
+        this.fieldnumber = 1;
+    }
+
+    private void indent(int level) throws SAXException {
+        this.getSAXConsumer().characters(INDENT_STRING, 0, level + 1);
+    }
+
+    private void startElement(String name) throws SAXException {
+        this.startElement(name, new AttributesImpl());
+    }
+
+    private void startElement(String name, Attributes atts) throws SAXException {
+        if (name == null)
+            throw new NullPointerException("Null name");
+        if (atts == null)
+            atts = new AttributesImpl();
+        String qual = NAMESPACE_PREFIX + ':' + name;
+        this.getSAXConsumer().startElement(NAMESPACE_URI, name, qual, atts);
+    }
+
+    private void endElement(String name) throws SAXException {
+        String qual = NAMESPACE_PREFIX + ':' + name;
+        this.getSAXConsumer().endElement(NAMESPACE_URI, name, qual);
+    }
+
+    private static final class CSVReader extends Reader implements Locator {
+
+        private String uri = null;
+        private Reader input = null;
+        private int column = 1;
+        private int line = 1;
+        private int last = -1;
+
+        private CSVReader(String csv, int buffer)
+                throws IOException {
+            Reader reader = new StringReader(csv);
+            this.input = new BufferedReader(reader, buffer);
+        }
+
+        private CSVReader(URL url, String encoding, int buffer)
+                throws IOException {
+            InputStream stream = url.openStream();
+            Reader reader = new InputStreamReader(stream, encoding);
+            this.input = new BufferedReader(reader, buffer);
+            this.uri = url.toExternalForm();
+        }
+
+        public String getPublicId() {
+            return null;
+        }
+
+        public String getSystemId() {
+            return this.uri;
+        }
+
+        public int getLineNumber() {
+            return this.line;
+        }
+
+        public int getColumnNumber() {
+            return this.column;
+        }
+
+        public void close() throws IOException {
+            this.input.close();
+        }
+
+        public int read() throws IOException {
+            int c = this.input.read();
+            if (c < 0)
+                return c;
+
+            if (((c == '\n') && (this.last != '\r')) || (c == '\r')) {
+                this.column = 1;
+                this.line++;
+            }
+
+            this.last = c;
+            return c;
+        }
+
+        public int read(char b[], int o, int l) throws IOException {
+            if (b == null)
+                throw new NullPointerException();
+            if ((o < 0) || (o > b.length) || (l < 0) || ((o + l) > b.length)
+                    || ((o + l) < 0)) {
+                throw new IndexOutOfBoundsException();
+            }
+            if (l == 0)
+                return 0;
+
+            int c = read();
+            if (c == -1)
+                return -1;
+            b[o] = (char) c;
+
+            int i = 1;
+            try {
+                for (i = 1; i < l; i++) {
+                    c = read();
+                    if (c == -1)
+                        break;
+                    b[o + i] = (char) c;
+                }
+            } catch (IOException ee) {
+                return i;
+            }
+            return i;
+        }
+    }
+    /*
+     * @Override public CacheKey constructCacheKey() { if (this.url == null) {
+     * throw new SetupException(this.getClass().getSimpleName() +
+     * " has no source."); } }
+     */
+}

Modified: cocoon/cocoon3/trunk/cocoon-optional/src/main/java/org/apache/cocoon/pipeline/components/parameters/Parameters.java
URL: http://svn.apache.org/viewvc/cocoon/cocoon3/trunk/cocoon-optional/src/main/java/org/apache/cocoon/pipeline/components/parameters/Parameters.java?rev=1243911&r1=1243910&r2=1243911&view=diff
==============================================================================
--- cocoon/cocoon3/trunk/cocoon-optional/src/main/java/org/apache/cocoon/pipeline/components/parameters/Parameters.java (original)
+++ cocoon/cocoon3/trunk/cocoon-optional/src/main/java/org/apache/cocoon/pipeline/components/parameters/Parameters.java Tue Feb 14 13:15:59 2012
@@ -16,6 +16,7 @@
  */
 package org.apache.cocoon.pipeline.components.parameters;
 
+import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
 
@@ -24,7 +25,7 @@ public class Parameters {
     private Map<String, ? extends Object> values;
 
     public Parameters(Map<String, ? extends Object> configuration) {
-        values = configuration;
+        values = new HashMap<String, Object>(configuration);
     }
 
     public int getAsInteger(String key, int defaultValue) {
@@ -32,8 +33,12 @@ public class Parameters {
         if (value == null) {
             return defaultValue;
         }
-
-        return Integer.valueOf((String) value);
+        if (value instanceof Integer){
+            return (Integer) value;
+        }else if (value instanceof String){
+            return Integer.valueOf((String) value);
+        }
+        return defaultValue;
     }
 
     public String get(String key, String defaultValue) {
@@ -79,17 +84,20 @@ public class Parameters {
 
     public boolean getParameterAsBoolean(final String name,
             final boolean defaultValue) {
-        final String value = (String) getParameter(name, null);
+        final Object value = getParameter(name, null);
         if (value == null) {
             return defaultValue;
         }
-        if (value.equalsIgnoreCase("true")) {
-            return true;
-        } else if (value.equalsIgnoreCase("false")) {
-            return false;
-        } else {
-            return defaultValue;
+        if (value instanceof Boolean){
+            return (Boolean) value;
+        }else if (value instanceof String){
+            if (((String) value).equalsIgnoreCase("true")) {
+                return true;
+            } else if (((String) value).equalsIgnoreCase("false")) {
+                return false;
+            }
         }
+        return defaultValue;
     }
 
     public Parameters merge(final Parameters other) {

Modified: cocoon/cocoon3/trunk/cocoon-optional/src/main/resources/META-INF/cocoon/spring-optional/cocoon-optional-generators.xml
URL: http://svn.apache.org/viewvc/cocoon/cocoon3/trunk/cocoon-optional/src/main/resources/META-INF/cocoon/spring-optional/cocoon-optional-generators.xml?rev=1243911&r1=1243910&r2=1243911&view=diff
==============================================================================
--- cocoon/cocoon3/trunk/cocoon-optional/src/main/resources/META-INF/cocoon/spring-optional/cocoon-optional-generators.xml (original)
+++ cocoon/cocoon3/trunk/cocoon-optional/src/main/resources/META-INF/cocoon/spring-optional/cocoon-optional-generators.xml Tue Feb 14 13:15:59 2012
@@ -23,5 +23,6 @@
 
   <bean name="generator:dir" class="org.apache.cocoon.optional.pipeline.components.sax.directory.DirectoryGenerator" scope="prototype" />
   <bean name="generator:html" class="org.apache.cocoon.optional.pipeline.components.sax.neko.NekoGenerator" scope="prototype" />
+  <bean name="generator:csv" class="org.apache.cocoon.optional.pipeline.components.sax.csv.CSVGenerator" scope="prototype" />
 
 </beans>

Added: cocoon/cocoon3/trunk/cocoon-optional/src/test/java/org/apache/cocoon/optional/pipeline/components/sax/csv/CSVGeneratorTest.java
URL: http://svn.apache.org/viewvc/cocoon/cocoon3/trunk/cocoon-optional/src/test/java/org/apache/cocoon/optional/pipeline/components/sax/csv/CSVGeneratorTest.java?rev=1243911&view=auto
==============================================================================
--- cocoon/cocoon3/trunk/cocoon-optional/src/test/java/org/apache/cocoon/optional/pipeline/components/sax/csv/CSVGeneratorTest.java (added)
+++ cocoon/cocoon3/trunk/cocoon-optional/src/test/java/org/apache/cocoon/optional/pipeline/components/sax/csv/CSVGeneratorTest.java Tue Feb 14 13:15:59 2012
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+package org.apache.cocoon.optional.pipeline.components.sax.csv;
+
+import static org.custommonkey.xmlunit.XMLAssert.assertNodeTestPasses;
+
+import java.io.ByteArrayOutputStream;
+import java.net.MalformedURLException;
+import java.net.URISyntaxException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.cocoon.pipeline.NonCachingPipeline;
+import org.apache.cocoon.pipeline.Pipeline;
+import org.apache.cocoon.sax.SAXPipelineComponent;
+import org.apache.cocoon.sax.component.XMLSerializer;
+import org.custommonkey.xmlunit.examples.CountingNodeTester;
+import org.junit.Test;
+import org.w3c.dom.Node;
+
+public final class CSVGeneratorTest {
+    @Test
+    public void testPipelineWithDirectoryGenerator() throws Exception {
+        ByteArrayOutputStream baos = getPipelineResult();
+        System.out.println(new String(baos.toByteArray()));
+        CountingNodeTester countingNodeTester = new CountingNodeTester(79);
+        assertNodeTestPasses(new String(baos.toByteArray()),
+                countingNodeTester, Node.ELEMENT_NODE);
+    }
+
+    private ByteArrayOutputStream getPipelineResult()
+            throws URISyntaxException, MalformedURLException, Exception {
+        Pipeline<SAXPipelineComponent> pipeline = new NonCachingPipeline<SAXPipelineComponent>();
+
+        CSVGenerator generator = new CSVGenerator(csv);
+        Map<String, Object> parameters = new HashMap<String, Object>();
+        parameters.put("process-headers", true);
+        parameters.put("max-records", 10);
+        generator.setConfiguration(parameters);
+        pipeline.addComponent(generator);
+        pipeline.addComponent(new XMLSerializer());
+
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        pipeline.setup(baos);
+        pipeline.execute();
+        return baos;
+    }
+
+    /*
+     * Using here a string since the csv file needs license headers for RAT, but the generator
+     * does not like this header and tries to parse it.
+     * 
+     * Here a listening of a couple of South Park episodes
+     */
+    private final String csv = "number,season,episode,production code,airdate,title,special\n"
+            + "1,1,1,\"101\",13/Aug/97,\"Cartman Gets an Anal Probe\"\n"
+            + "2,1,2,\"103\",20/Aug/97,\"Volcano\"\n"
+            + "3,1,3,\"102\",27/Aug/97,\"Weight Gain 4000\"\n"
+            + "4,1,4,\"104\",03/Sep/97,\"Big Gay Al's Big Gay Boat Ride\"\n"
+            + "5,1,5,\"105\",10/Sep/97,\"An Elephant Makes Love to a Pig\"\n"
+            + "6,1,6,\"106\",17/Sep/97,\"Death\"\n"
+            + "7,1,7,\"107\",29/Oct/97,\"Pinkeye\"\n"
+            + "8,1,8,\"109\",19/Nov/97,\"Starvin' Marvin\"\n"
+            + "9,1,9,\"110\",17/Dec/97,\"Mr. Hankey, the Christmas Poo\"\n"
+            + "10,1,10,\"108\",04/Feb/98,\"Damien\"\n"
+            + "11,1,11,\"111\",11/Feb/98,\"Tom's Rhinoplasty\"\n"
+            + "12,1,12,\"112\",18/Feb/98,\"Mecha-Streisand\"\n"
+            + "13,1,13,\"113\",25/Feb/98,\"Cartman's Mom is a Dirty Slut (1)\"\n"
+            + "14,2,1,\"201\",01/Apr/98,\"Terrance & Phillip in \"\"Not Without My Anus\"\"\"\n"
+            + "15,2,2,\"202\",22/Apr/98,\"Cartman's Mom is Still a Dirty Slut (2)\"\n"
+            + "16,2,3,\"204\",20/May/98,\"Ike's Wee Wee\"\n"
+            + "17,2,4,\"203\",27/May/98,\"Chickenlover\"\n"
+            + "18,2,5,\"205\",03/Jun/98,\"Conjoined Fetus Lady\"\n"
+            + "19,2,6,\"206\",10/Jun/98,\"The Mexican Staring Frog of Southern Sri Lanka\"\n"
+            + "20,2,7,\"207\",17/Jun/98,\"City on the Edge of Forever (a.k.a. Flashbacks)\"\n"
+            + "21,2,8,\"208\",24/Jun/98,\"Summer Sucks\",n\n";
+}