7 package com.google.appinventor.components.runtime.util;
11 import java.io.IOException;
12 import java.io.Reader;
13 import java.io.StringReader;
14 import java.util.ArrayList;
15 import java.util.Iterator;
16 import java.util.List;
17 import java.util.regex.Pattern;
30 CsvParser csvParser =
new CsvParser(
new StringReader(csvString));
31 ArrayList<YailList> csvList =
new ArrayList<YailList>();
32 while (csvParser.hasNext()) {
35 csvParser.throwAnyProblem();
40 CsvParser csvParser =
new CsvParser(
new StringReader(csvString));
41 if (csvParser.hasNext()) {
43 if (csvParser.hasNext()) {
45 throw new IllegalArgumentException(
"CSV text has multiple rows. Expected just one row.");
47 csvParser.throwAnyProblem();
50 throw new IllegalArgumentException(
"CSV text cannot be parsed as a row.");
55 StringBuilder csvStringBuilder =
new StringBuilder();
56 makeCsvRow(csvRow, csvStringBuilder);
57 return csvStringBuilder.toString();
64 StringBuilder csvStringBuilder =
new StringBuilder();
65 for (Object rowObj : csvList.
toArray()) {
66 makeCsvRow((
YailList) rowObj, csvStringBuilder);
70 csvStringBuilder.append(
"\r\n");
72 return csvStringBuilder.toString();
75 private static void makeCsvRow(
YailList row, StringBuilder csvStringBuilder) {
76 String fieldDelim =
"";
77 for (Object fieldObj : row.
toArray()) {
78 String field = fieldObj.toString();
79 field = field.replaceAll(
"\"",
"\"\"");
80 csvStringBuilder.append(fieldDelim).append(
"\"").append(field).append(
"\"");
91 private static class CsvParser
implements Iterator<List<String>> {
95 private final Pattern ESCAPED_QUOTE_PATTERN = Pattern.compile(
"\"\"");
104 private final char[] buf =
new char[10240];
106 private final Reader in;
123 private boolean opened =
true;
130 private int cellLength = -1;
138 private int delimitedCellLength = -1;
144 private Exception lastException;
146 private long previouslyRead;
148 public CsvParser(Reader in) {
152 public void skip(
long charPosition)
throws IOException {
153 while (charPosition > 0) {
154 int n = in.read(buf, 0, Math.min((
int) charPosition, buf.length));
163 public boolean hasNext() {
167 return (pos < limit || indexAfterCompactionAndFilling(pos) < limit) && lookingAtCell();
170 public ArrayList<String> next() {
171 ArrayList<String> result = Lists.newArrayList();
172 boolean trailingComma;
173 boolean haveMoreData;
177 if (buf[pos] !=
'"') {
180 result.add(
new String(buf, pos, cellLength).trim());
182 String cell =
new String(buf, pos + 1, cellLength - 2);
183 result.add(ESCAPED_QUOTE_PATTERN.matcher(cell).replaceAll(
"\"").trim());
185 trailingComma = delimitedCellLength > 0 && buf[pos + delimitedCellLength - 1] ==
',';
186 pos += delimitedCellLength;
187 delimitedCellLength = cellLength = -1;
188 haveMoreData = pos < limit || indexAfterCompactionAndFilling(pos) < limit;
189 }
while (trailingComma && haveMoreData && lookingAtCell());
193 public long getCharPosition() {
194 return previouslyRead + pos;
201 private int indexAfterCompactionAndFilling(
int i) {
213 private int compact(
int i) {
216 int toMove = limit - oldPos;
218 System.arraycopy(buf, oldPos, buf, 0, toMove);
221 previouslyRead += oldPos;
228 private void fill() {
229 int toFill = buf.length - limit;
230 while (opened && toFill > 0) {
232 int n = in.read(buf, limit, toFill);
239 }
catch (IOException e) {
246 private boolean lookingAtCell() {
247 return (buf[pos] ==
'"' ? findUnescapedEndQuote(pos + 1) : findUnquotedCellEnd(pos));
250 private boolean findUnescapedEndQuote(
int i) {
251 for (; i < limit || (i = indexAfterCompactionAndFilling(i)) < limit; i++) {
253 i = checkedIndex(i + 1);
254 if (i == limit || buf[i] !=
'"') {
255 cellLength = i - pos;
256 return findDelimOrEnd(i);
260 lastException =
new IllegalArgumentException(
"Syntax Error. unclosed quoted cell");
268 private boolean findDelimOrEnd(
int i) {
269 for (; i < limit || (i = indexAfterCompactionAndFilling(i)) < limit; i++) {
278 int j = checkedIndex(i + 1);
279 delimitedCellLength = (buf[j] ==
'\n' ? checkedIndex(j + 1) : j) - pos;
283 delimitedCellLength = (checkedIndex(i + 1) - pos);
286 lastException =
new IOException(
287 "Syntax Error: non-whitespace between closing quote and delimiter or end");
291 delimitedCellLength = (limit - pos);
300 private int checkedIndex(
int i) {
301 return i < limit ? i : indexAfterCompactionAndFilling(i);
304 private boolean findUnquotedCellEnd(
int i) {
305 for (; i < limit || (i = indexAfterCompactionAndFilling(i)) < limit; i++) {
309 cellLength = i - pos;
310 delimitedCellLength = cellLength + 1;
315 cellLength = i - pos;
316 int j = checkedIndex(i + 1);
317 delimitedCellLength = (buf[j] ==
'\n' ? checkedIndex(j + 1) : j) - pos;
320 lastException =
new IllegalArgumentException(
"Syntax Error: quote in unquoted cell");
324 delimitedCellLength = cellLength = (limit - pos);
328 public void remove() {
329 throw new UnsupportedOperationException();
332 public void throwAnyProblem() throws Exception {
333 if (lastException !=
null) {