2222import java .util .ArrayList ;
2323import java .util .Iterator ;
2424import java .util .LinkedHashMap ;
25- import java .util .List ;
2625import java .util .Map ;
2726import java .util .NoSuchElementException ;
2827
3231public class CSVDecoder {
3332 // Cursor
3433 private static class Cursor implements Iterable <Map <String , String >> {
35- private List <String > keys ;
3634 private Reader reader ;
3735 private char delimiter ;
3836
37+ private StringBuilder valueBuilder = new StringBuilder ();
38+
39+ private ArrayList <String > keys = new ArrayList <>();
40+
3941 private ArrayList <String > values = new ArrayList <>();
4042 private LinkedHashMap <String , String > row = new LinkedHashMap <>();
4143
@@ -46,6 +48,8 @@ private static class Cursor implements Iterable<Map<String, String>> {
4648 public boolean hasNext () {
4749 if (hasNext == null ) {
4850 try {
51+ values .clear ();
52+
4953 decodeValues (reader , values , delimiter );
5054 } catch (IOException exception ) {
5155 throw new RuntimeException (exception );
@@ -75,10 +79,61 @@ public Map<String, String> next() {
7579 }
7680 };
7781
78- private Cursor (List <String > keys , Reader reader , char delimiter ) {
79- this .keys = keys ;
82+ private static final int EOF = -1 ;
83+
84+ public Cursor (Reader reader , char delimiter ) throws IOException {
8085 this .reader = reader ;
8186 this .delimiter = delimiter ;
87+
88+ decodeValues (reader , keys , delimiter );
89+ }
90+
91+ public void decodeValues (Reader reader , ArrayList <String > values , char delimiter ) throws IOException {
92+ int c = reader .read ();
93+
94+ while (c != '\r' && c != '\n' && c != EOF ) {
95+ valueBuilder .setLength (0 );
96+
97+ boolean quoted = false ;
98+
99+ if (c == '"' ) {
100+ quoted = true ;
101+
102+ c = reader .read ();
103+ }
104+
105+ while ((quoted || (c != delimiter && c != '\r' && c != '\n' )) && c != EOF ) {
106+ valueBuilder .append ((char )c );
107+
108+ c = reader .read ();
109+
110+ if (c == '"' ) {
111+ c = reader .read ();
112+
113+ if (c != '"' ) {
114+ quoted = false ;
115+ }
116+ }
117+ }
118+
119+ if (quoted ) {
120+ throw new IOException ("Unterminated quoted value." );
121+ }
122+
123+ values .add (valueBuilder .toString ());
124+
125+ if (c == delimiter ) {
126+ c = reader .read ();
127+ }
128+ }
129+
130+ if (c == '\r' ) {
131+ c = reader .read ();
132+
133+ if (c != '\n' ) {
134+ throw new IOException ("Improperly terminated record." );
135+ }
136+ }
82137 }
83138
84139 @ Override
@@ -89,8 +144,6 @@ public Iterator<Map<String, String>> iterator() {
89144
90145 private char delimiter ;
91146
92- private static final int EOF = -1 ;
93-
94147 /**
95148 * Constructs a new CSV decoder.
96149 */
@@ -131,68 +184,12 @@ public Iterable<Map<String, String>> readValues(InputStream inputStream) throws
131184 * The character stream to read from.
132185 *
133186 * @return
134- * A cursor over the values in the input stream.
187+ * A cursor over the values in the character stream.
135188 *
136189 * @throws IOException
137190 * If an exception occurs.
138191 */
139192 public Iterable <Map <String , String >> readValues (Reader reader ) throws IOException {
140- reader = new BufferedReader (reader );
141-
142- ArrayList <String > keys = new ArrayList <>();
143-
144- decodeValues (reader , keys , delimiter );
145-
146- return new Cursor (keys , reader , delimiter );
147- }
148-
149- private static void decodeValues (Reader reader , ArrayList <String > fields , char delimiter ) throws IOException {
150- fields .clear ();
151-
152- int c = reader .read ();
153-
154- while (c != '\r' && c != '\n' && c != EOF ) {
155- StringBuilder fieldBuilder = new StringBuilder ();
156-
157- boolean quoted = false ;
158-
159- if (c == '"' ) {
160- quoted = true ;
161-
162- c = reader .read ();
163- }
164-
165- while ((quoted || (c != delimiter && c != '\r' && c != '\n' )) && c != EOF ) {
166- fieldBuilder .append ((char )c );
167-
168- c = reader .read ();
169-
170- if (c == '"' ) {
171- c = reader .read ();
172-
173- if (c != '"' ) {
174- quoted = false ;
175- }
176- }
177- }
178-
179- if (quoted ) {
180- throw new IOException ("Unterminated quoted value." );
181- }
182-
183- fields .add (fieldBuilder .toString ());
184-
185- if (c == delimiter ) {
186- c = reader .read ();
187- }
188- }
189-
190- if (c == '\r' ) {
191- c = reader .read ();
192-
193- if (c != '\n' ) {
194- throw new IOException ("Improperly terminated record." );
195- }
196- }
193+ return new Cursor (new BufferedReader (reader ), delimiter );
197194 }
198195}
0 commit comments