View Javadoc
1   /*
2    *
3    * The DbUnit Database Testing Framework
4    * Copyright (C)2002-2004, DbUnit.org
5    *
6    * This library is free software; you can redistribute it and/or
7    * modify it under the terms of the GNU Lesser General Public
8    * License as published by the Free Software Foundation; either
9    * version 2.1 of the License, or (at your option) any later version.
10   *
11   * This library is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   * Lesser General Public License for more details.
15   *
16   * You should have received a copy of the GNU Lesser General Public
17   * License along with this library; if not, write to the Free Software
18   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19   *
20   */
21  
22  package org.dbunit.dataset.csv;
23  
24  import java.io.BufferedReader;
25  import java.io.File;
26  import java.io.FileInputStream;
27  import java.io.IOException;
28  import java.io.InputStreamReader;
29  import java.io.LineNumberReader;
30  import java.io.Reader;
31  import java.net.URL;
32  import java.text.CharacterIterator;
33  import java.text.StringCharacterIterator;
34  import java.util.ArrayList;
35  import java.util.List;
36  
37  import org.dbunit.dataset.common.handlers.EscapeHandler;
38  import org.dbunit.dataset.common.handlers.IllegalInputCharacterException;
39  import org.dbunit.dataset.common.handlers.IsAlnumHandler;
40  import org.dbunit.dataset.common.handlers.Pipeline;
41  import org.dbunit.dataset.common.handlers.PipelineException;
42  import org.dbunit.dataset.common.handlers.QuoteHandler;
43  import org.dbunit.dataset.common.handlers.SeparatorHandler;
44  import org.dbunit.dataset.common.handlers.TransparentHandler;
45  import org.dbunit.dataset.common.handlers.WhitespacesHandler;
46  import org.slf4j.Logger;
47  import org.slf4j.LoggerFactory;
48  
49  /**
50   * @author fede
51   * @author Last changed by: $Author$
52   * @version $Revision$ $Date$
53   * @since 2.2 (Sep 12, 2004)
54   */
55  public class CsvParserImpl implements CsvParser {
56  
57      /**
58       * Logger for this class
59       */
60      private static final Logger logger = LoggerFactory.getLogger(CsvParserImpl.class);
61  
62      private Pipeline pipeline;
63  
64      public CsvParserImpl() {
65          resetThePipeline();
66      }
67  
68      private void resetThePipeline() {
69          logger.debug("resetThePipeline() - start");
70  
71          pipeline = new Pipeline();
72          getPipeline().putFront(SeparatorHandler.ENDPIECE());
73          getPipeline().putFront(EscapeHandler.ACCEPT());
74          getPipeline().putFront(IsAlnumHandler.QUOTE());
75          getPipeline().putFront(QuoteHandler.QUOTE());
76          getPipeline().putFront(EscapeHandler.ESCAPE());
77          getPipeline().putFront(WhitespacesHandler.IGNORE());
78          getPipeline().putFront(TransparentHandler.IGNORE());
79      }
80  
81      public List parse(String csv) throws PipelineException, IllegalInputCharacterException {
82          logger.debug("parse(csv={}) - start", csv);
83  
84          getPipeline().resetProducts();
85          CharacterIterator iterator = new StringCharacterIterator(csv);
86          for (char c = iterator.first(); c != CharacterIterator.DONE; c = iterator.next()) {
87              getPipeline().handle(c);
88          }
89          getPipeline().noMoreInput();
90          getPipeline().thePieceIsDone();
91          return getPipeline().getProducts();
92      }
93  
94      public List parse(File file) throws IOException, CsvParserException {
95          logger.debug("parse(file={}) - start", file);
96  
97          BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
98          try {
99              return parse(reader, file.getAbsolutePath().toString());
100         }
101         finally {
102             reader.close();
103         }
104     }
105     
106     public List parse(URL url) throws IOException, CsvParserException {
107         logger.debug("parse(url={}) - start", url);
108 
109         BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()));
110         try {
111             return parse(reader, url.toString());
112         }
113         finally {
114             reader.close();
115         }
116     }
117     
118     public List parse(Reader reader, String source) throws IOException, CsvParserException {
119         logger.debug("parse(reader={}, source={}) - start", reader, source);
120 
121         LineNumberReader lineNumberReader = new LineNumberReader(reader);
122         List rows = new ArrayList();
123         List columnsInFirstLine = parseFirstLine(lineNumberReader, source, rows);
124         parseTheData(columnsInFirstLine, lineNumberReader, rows);
125         return rows;
126     }
127 
128 //    private List parseFirstLine(LineNumberReader lineNumberReader, File file, List rows) throws IOException, CsvParserException {
129 //        if(logger.isDebugEnabled())
130 //            logger.debug("parseFirstLine(lineNumberReader={}, file={}, rows={}) - start", 
131 //                new Object[]{lineNumberReader, file, rows} );
132 //
133 //    	return parseFirstLine(lineNumberReader, file.getAbsolutePath().toString(), rows);
134 //    }
135 
136     /** 
137      * parse the first line of data from the given source 
138      */
139     private List parseFirstLine(LineNumberReader lineNumberReader, String source, List rows) throws IOException, CsvParserException {
140         if(logger.isDebugEnabled())
141             logger.debug("parseFirstLine(lineNumberReader={}, source={}, rows={}) - start", 
142                 new Object[]{lineNumberReader,source,rows});
143 
144         String firstLine = lineNumberReader.readLine();
145         if (firstLine == null)
146             throw new CsvParserException("The first line of " + source + " is null");
147 
148         final List columnsInFirstLine = parse(firstLine);
149         rows.add(columnsInFirstLine);
150         return columnsInFirstLine;
151     }
152 
153     private void parseTheData(final List columnsInFirstLine, LineNumberReader lineNumberReader, List rows) throws IOException, CsvParserException {
154         logger.debug("parseTheData(columnsInFirstLine={}, lineNumberReader={}, rows={}) - start", 
155                 new Object[] {columnsInFirstLine, lineNumberReader, rows} );
156 
157         int nColumns = columnsInFirstLine.size();
158         List columns;
159         while ((columns = collectExpectedNumberOfColumns(nColumns, lineNumberReader)) != null) {
160             rows.add(columns);
161         }
162     }
163 
164     private List collectExpectedNumberOfColumns(int expectedNumberOfColumns, LineNumberReader lineNumberReader) throws IOException, CsvParserException  {
165         if(logger.isDebugEnabled())
166             logger.debug("collectExpectedNumberOfColumns(expectedNumberOfColumns={}, lineNumberReader={}) - start",
167                 String.valueOf(expectedNumberOfColumns), lineNumberReader);
168 
169         List columns = null;
170         int columnsCollectedSoFar = 0;
171         StringBuffer buffer = new StringBuffer();
172         String anotherLine = lineNumberReader.readLine();
173         if(anotherLine == null)
174             return null;
175         boolean shouldProceed = false;
176         while (columnsCollectedSoFar < expectedNumberOfColumns) {
177             try {
178                 buffer.append(anotherLine);
179                 columns = parse(buffer.toString());
180                 columnsCollectedSoFar = columns.size();
181             } catch (IllegalStateException e) {
182                 resetThePipeline();
183                 anotherLine = lineNumberReader.readLine();
184                 if(anotherLine == null)
185                     break;
186                 buffer.append("\n");
187                 shouldProceed = true;
188             }
189             if (!shouldProceed)
190                 break;
191         }
192         if (columnsCollectedSoFar != expectedNumberOfColumns) {
193             String message = new StringBuffer("Expected ").append(expectedNumberOfColumns)
194                     .append(" columns on line ").append(lineNumberReader.getLineNumber())
195                     .append(", got ").append(columnsCollectedSoFar).append(". Offending line: ").append(buffer).toString();
196             throw new CsvParserException(message);
197         }
198         return columns;
199     }
200 
201     Pipeline getPipeline() {
202         logger.debug("getPipeline() - start");
203 
204         return pipeline;
205     }
206 
207     void setPipeline(Pipeline pipeline) {
208         logger.debug("setPipeline(pipeline={}) - start", pipeline);
209 
210         this.pipeline = pipeline;
211     }
212 }