package org.simpleflatmapper.csv;
import org.simpleflatmapper.csv.parser.CellConsumer;
import org.simpleflatmapper.csv.parser.CharConsumer;
import org.simpleflatmapper.csv.parser.NullCellConsumer;
import org.simpleflatmapper.csv.parser.StringArrayCellConsumer;
import org.simpleflatmapper.util.CheckedConsumer;
import org.simpleflatmapper.util.ErrorHelper;
import org.simpleflatmapper.util.Function;
import java.io.IOException;
import java.util.Iterator;
import java.util.NoSuchElementException;
//IFJAVA8_START
import java.util.Spliterator;
import java.util.function.Consumer;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
//IFJAVA8_END
public final class CsvReader implements Iterable<String[]> {
private final CharConsumer consumer;
private final Function<? super CellConsumer, ? extends CellConsumer> cellConsumerWrapper;
public CsvReader(CharConsumer charConsumer) {
this(charConsumer, null);
}
public CsvReader(CharConsumer charConsumer, Function<? super CellConsumer, ? extends CellConsumer> cellConsumerWrapper) {
this.consumer = charConsumer;
this.cellConsumerWrapper = cellConsumerWrapper;
}
/**
* parse cvs
* @param cellConsumer the consumer that the parser will callback
* @param <CC> the cell consumer type
* @throws java.io.IOException if an io error occurs
* @return the cell consumer
*/
public <CC extends CellConsumer> CC parseAll(CC cellConsumer)
throws IOException {
_parseAll(wrapConsumer(cellConsumer));
return cellConsumer;
}
private <CC extends CellConsumer> void _parseAll(CC cellConsumer) throws IOException {
do {
consumer.consumeAllBuffer(cellConsumer);
} while(consumer.next());
consumer.finish(cellConsumer);
}
/**
* parse cvs
* @param cellConsumer the cell consumer
* @return true if there was data consumed
* @throws IOException if io error occurs
*/
public boolean parseRow(CellConsumer cellConsumer)
throws IOException {
return _parseRow(wrapConsumer(cellConsumer));
}
private boolean _parseRow(CellConsumer cellConsumer) throws IOException {
do {
if (consumer.consumeToNextRow(cellConsumer)) {
return true;
}
} while(consumer.next());
consumer.finish(cellConsumer);
return false;
}
public void skipRows(int n) throws IOException {
_parseRows(NullCellConsumer.INSTANCE, n);
}
public <CC extends CellConsumer> CC parseRows(CC cellConsumer, int limit) throws IOException {
_parseRows(wrapConsumer(cellConsumer), limit);
return cellConsumer;
}
private <CC extends CellConsumer> void _parseRows(CC cellConsumer, int limit) throws IOException {
for(int i = 0; i < limit; i++) {
_parseRow(cellConsumer);
}
}
public <RH extends CheckedConsumer<String[]>> RH read(RH consumer) throws IOException {
parseAll(toCellConsumer(consumer));
return consumer;
}
public <RH extends CheckedConsumer<String[]>> RH read(RH consumer, int limit) throws IOException {
parseRows(toCellConsumer(consumer), limit);
return consumer;
}
private CellConsumer toCellConsumer(CheckedConsumer<String[]> consumer) {
return StringArrayCellConsumer.newInstance(consumer);
}
private CellConsumer wrapConsumer(CellConsumer cellConsumer) {
if (cellConsumerWrapper == null) return cellConsumer;
return cellConsumerWrapper.apply(cellConsumer);
}
@Override
public Iterator<String[]> iterator() {
return new CsvStringArrayIterator(this);
}
//IFJAVA8_START
public Stream<String[]> stream() {
return StreamSupport.stream(new CsvStringArraySpliterator(this), false);
}
private static class CsvStringArraySpliterator implements Spliterator<String[]> {
private final CsvReader reader;
public CsvStringArraySpliterator(CsvReader csvReader) {
this.reader = csvReader;
}
@Override
public boolean tryAdvance(Consumer<? super String[]> action) {
try {
return reader.parseRow(reader.toCellConsumer(action::accept));
} catch (IOException e) {
return ErrorHelper.rethrow(e);
}
}
@Override
public void forEachRemaining(Consumer<? super String[]> action) {
try {
reader.parseAll(reader.toCellConsumer(action::accept));
} catch (IOException e) {
ErrorHelper.rethrow(e);
}
}
@Override
public Spliterator<String[]> trySplit() {
return null;
}
@Override
public long estimateSize() {
return Long.MAX_VALUE;
}
@Override
public int characteristics() {
return Spliterator.ORDERED | Spliterator.NONNULL;
}
}
//IFJAVA8_END
private static class CsvStringArrayIterator implements Iterator<String[]> {
private final CsvReader reader;
private final CellConsumer cellConsumer;
private boolean isFetched;
private String[] value;
@SuppressWarnings("unchecked")
public CsvStringArrayIterator(CsvReader csvReader) {
cellConsumer = csvReader.toCellConsumer(new CheckedConsumer<String[]>() {
@Override
public void accept(String[] strings) {
value = strings;
}
});
reader = csvReader;
}
@Override
public boolean hasNext() {
fetch();
return value != null;
}
private void fetch() {
if (!isFetched) {
try {
value = null;
reader.parseRow(cellConsumer);
} catch (IOException e) {
ErrorHelper.rethrow(e);
}
isFetched = true;
}
}
@Override
public String[] next() {
fetch();
if (value == null) throw new NoSuchElementException();
isFetched = false;
return value;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
}