8383 */
8484public class CSVParser implements Iterable <CSVRecord >, Closeable {
8585
86- private final Lexer lexer ;
87- private final Map <String , Integer > headerMap ;
88- private long recordNumber ;
8986 private final CSVFormat format ;
87+ private final Map <String , Integer > headerMap ;
88+ private final Lexer lexer ;
89+ /** A record buffer for getRecord(). Grows as necessary and is reused. */
90+ private final List <String > record = new ArrayList <String >();
9091
9192 // the following objects are shared to reduce garbage
9293
93- /** A record buffer for getRecord(). Grows as necessary and is reused. */
94- private final List <String > record = new ArrayList <String >();
94+ private long recordNumber ;
9595 private final Token reusableToken = new Token ();
9696
9797 /**
@@ -143,17 +143,26 @@ public CSVParser(final String input, final CSVFormat format) throws IOException
143143 this (new StringReader (input ), format );
144144 }
145145
146+ private void addRecordValue () {
147+ final String input = reusableToken .content .toString ();
148+ final String nullString = this .format .getNullString ();
149+ if (nullString == null ) {
150+ record .add (input );
151+ } else {
152+ record .add (input .equalsIgnoreCase (nullString ) ? null : input );
153+ }}
154+
146155 /**
147- * Returns a copy of the header map that iterates in column order.
148- * <p>
149- * The map keys are column names.
150- * The map values are 0-based indices.
151- *
152- * @return a copy of the header map that iterates in column order.
156+ * Closes resources.
157+ *
158+ * @throws IOException
159+ * If an I/O error occurs
153160 */
154- public Map <String , Integer > getHeaderMap () {
155- return new LinkedHashMap <String , Integer >(headerMap );
156- }
161+ public void close () throws IOException {
162+ if (lexer != null ) {
163+ lexer .close ();
164+ }
165+ }
157166
158167 /**
159168 * Returns the current line number in the input stream.
@@ -166,6 +175,18 @@ public long getCurrentLineNumber() {
166175 return lexer .getCurrentLineNumber ();
167176 }
168177
178+ /**
179+ * Returns a copy of the header map that iterates in column order.
180+ * <p>
181+ * The map keys are column names.
182+ * The map values are 0-based indices.
183+ *
184+ * @return a copy of the header map that iterates in column order.
185+ */
186+ public Map <String , Integer > getHeaderMap () {
187+ return new LinkedHashMap <String , Integer >(headerMap );
188+ }
189+
169190 /**
170191 * Returns the current record number in the input stream.
171192 * <p/>
@@ -177,75 +198,6 @@ public long getRecordNumber() {
177198 return recordNumber ;
178199 }
179200
180- /**
181- * Parses the next record from the current point in the stream.
182- *
183- * @return the record as an array of values, or <tt>null</tt> if the end of the stream has been reached
184- * @throws IOException
185- * on parse error or input read-failure
186- */
187- CSVRecord nextRecord () throws IOException {
188- CSVRecord result = null ;
189- record .clear ();
190- StringBuilder sb = null ;
191- do {
192- reusableToken .reset ();
193- lexer .nextToken (reusableToken );
194- switch (reusableToken .type ) {
195- case TOKEN :
196- this .addRecordValue ();
197- break ;
198- case EORECORD :
199- this .addRecordValue ();
200- break ;
201- case EOF :
202- if (reusableToken .isReady ) {
203- this .addRecordValue ();
204- }
205- break ;
206- case INVALID :
207- throw new IOException ("(line " + getCurrentLineNumber () + ") invalid parse sequence" );
208- case COMMENT : // Ignored currently
209- if (sb == null ) { // first comment for this record
210- sb = new StringBuilder ();
211- } else {
212- sb .append (Constants .LF );
213- }
214- sb .append (reusableToken .content );
215- reusableToken .type = TOKEN ; // Read another token
216- break ;
217- }
218- } while (reusableToken .type == TOKEN );
219-
220- if (!record .isEmpty ()) {
221- recordNumber ++;
222- final String comment = sb == null ? null : sb .toString ();
223- result = new CSVRecord (record .toArray (new String [record .size ()]), headerMap , comment , this .recordNumber );
224- }
225- return result ;
226- }
227-
228- private void addRecordValue () {
229- final String input = reusableToken .content .toString ();
230- final String nullString = this .format .getNullString ();
231- if (nullString == null ) {
232- record .add (input );
233- } else {
234- record .add (input .equalsIgnoreCase (nullString ) ? null : input );
235- }}
236-
237- /**
238- * Closes resources.
239- *
240- * @throws IOException
241- * If an I/O error occurs
242- */
243- public void close () throws IOException {
244- if (lexer != null ) {
245- lexer .close ();
246- }
247- }
248-
249201 /**
250202 * Parses the CSV input according to the given format and returns the content as an array of {@link CSVRecord}
251203 * entries.
@@ -294,6 +246,10 @@ private Map<String, Integer> initializeHeader() throws IOException {
294246 return hdrMap ;
295247 }
296248
249+ public boolean isClosed () {
250+ return lexer .isClosed ();
251+ }
252+
297253 /**
298254 * Returns an iterator on the records. IOExceptions occurring during the iteration are wrapped in a
299255 * RuntimeException.
@@ -346,8 +302,52 @@ public void remove() {
346302 };
347303 }
348304
349- public boolean isClosed () {
350- return lexer .isClosed ();
351- }
305+ /**
306+ * Parses the next record from the current point in the stream.
307+ *
308+ * @return the record as an array of values, or <tt>null</tt> if the end of the stream has been reached
309+ * @throws IOException
310+ * on parse error or input read-failure
311+ */
312+ CSVRecord nextRecord () throws IOException {
313+ CSVRecord result = null ;
314+ record .clear ();
315+ StringBuilder sb = null ;
316+ do {
317+ reusableToken .reset ();
318+ lexer .nextToken (reusableToken );
319+ switch (reusableToken .type ) {
320+ case TOKEN :
321+ this .addRecordValue ();
322+ break ;
323+ case EORECORD :
324+ this .addRecordValue ();
325+ break ;
326+ case EOF :
327+ if (reusableToken .isReady ) {
328+ this .addRecordValue ();
329+ }
330+ break ;
331+ case INVALID :
332+ throw new IOException ("(line " + getCurrentLineNumber () + ") invalid parse sequence" );
333+ case COMMENT : // Ignored currently
334+ if (sb == null ) { // first comment for this record
335+ sb = new StringBuilder ();
336+ } else {
337+ sb .append (Constants .LF );
338+ }
339+ sb .append (reusableToken .content );
340+ reusableToken .type = TOKEN ; // Read another token
341+ break ;
342+ }
343+ } while (reusableToken .type == TOKEN );
344+
345+ if (!record .isEmpty ()) {
346+ recordNumber ++;
347+ final String comment = sb == null ? null : sb .toString ();
348+ result = new CSVRecord (record .toArray (new String [record .size ()]), headerMap , comment , this .recordNumber );
349+ }
350+ return result ;
351+ }
352352
353353}
0 commit comments