package org.simpleflatmapper.csv.impl;
import org.simpleflatmapper.csv.CsvColumnKey;
import org.simpleflatmapper.csv.CsvMapper;
import org.simpleflatmapper.csv.CsvParser;
import org.simpleflatmapper.csv.CsvReader;
import org.simpleflatmapper.csv.mapper.*;
import org.simpleflatmapper.csv.parser.CellConsumer;
import org.simpleflatmapper.map.ConsumerErrorHandler;
import org.simpleflatmapper.map.MappingException;
import org.simpleflatmapper.util.ErrorHelper;
import org.simpleflatmapper.util.CheckedConsumer;
import java.io.IOException;
import java.io.Reader;
import java.util.*;
//IFJAVA8_START
import java.util.function.Consumer;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
//IFJAVA8_END
public final class CsvMapperImpl<T> implements CsvMapper<T> {
private static final DelayedCellSetter[] EMPTY_DELAYED_CELL_SETTERS = new DelayedCellSetter[0];
private final DelayedCellSetterFactory<T, ?>[] delayedCellSetterFactories;
private final CellSetter<T>[] setters;
private final CsvColumnKey[] joinKeys;
private final ConsumerErrorHandler consumerErrorHandlers;
private final CsvMapperCellHandlerFactory<T> csvMapperCellHandlerFactory;
private final boolean hasSetterSubProperties;
private final boolean hasSubProperties;
public CsvMapperImpl(CsvMapperCellHandlerFactory<T> csvMapperCellHandlerFactory,
DelayedCellSetterFactory<T, ?>[] delayedCellSetterFactories,
CellSetter<T>[] setters,
CsvColumnKey[] joinKeys,
ConsumerErrorHandler consumerErrorHandlers) {
super();
this.csvMapperCellHandlerFactory = csvMapperCellHandlerFactory;
this.delayedCellSetterFactories = delayedCellSetterFactories;
this.setters = setters;
this.joinKeys = joinKeys;
this.consumerErrorHandlers = consumerErrorHandlers;
this.hasSetterSubProperties = hasSetterSubProperties(setters);
this.hasSubProperties = hasSetterSubProperties || hasDelayedMarker(delayedCellSetterFactories);
}
private boolean hasDelayedMarker(DelayedCellSetterFactory<T, ?>[] delayedCellSetterFactories) {
for(DelayedCellSetterFactory<T, ?> setter : delayedCellSetterFactories) {
if (setter instanceof DelegateMarkerDelayedCellSetterFactory) {
return true;
}
}
return false;
}
private boolean hasSetterSubProperties(CellSetter<T>[] setters) {
for(CellSetter<T> setter : setters) {
if (setter instanceof DelegateMarkerSetter) {
return true;
}
}
return false;
}
@Override
public final <H extends CheckedConsumer<? super T>> H forEach(final Reader reader, final H handler) throws IOException, MappingException {
return forEach(CsvParser.reader(reader), handler);
}
@Override
public <H extends CheckedConsumer<? super T>> H forEach(CsvReader reader, H handle) throws IOException, MappingException {
reader.parseAll(newCellConsumer(handle));
return handle;
}
@Override
public final <H extends CheckedConsumer<? super T>> H forEach(final Reader reader, final H handler, final int skip) throws IOException, MappingException {
return forEach(CsvParser.skip(skip).reader(reader), handler);
}
@Override
public final <H extends CheckedConsumer<? super T>> H forEach(final Reader reader, final H handler, final int skip, final int limit) throws IOException, MappingException {
return forEach(CsvParser.skip(skip).reader(reader), handler, limit);
}
@Override
public final <H extends CheckedConsumer<? super T>> H forEach(CsvReader reader, H handle, int limit) throws IOException, MappingException {
reader.parseRows(newCellConsumer(handle), limit);
return handle;
}
@Override
public Iterator<T> iterator(Reader reader) throws IOException {
return iterator(CsvParser.reader(reader));
}
@Override
public Iterator<T> iterator(CsvReader csvReader) {
return new CsvMapperIterator<T>(csvReader, this);
}
@Override
public Iterator<T> iterator(Reader reader, int skip) throws IOException {
return iterator(CsvParser.skip(skip).reader(reader));
}
//IFJAVA8_START
@Override
public Stream<T> stream(Reader reader) throws IOException {
return stream(CsvParser.reader(reader));
}
@Override
public Stream<T> stream(CsvReader csvReader) {
return StreamSupport.stream(new CsvSpliterator(csvReader), false);
}
@Override
public Stream<T> stream(Reader reader, int skip) throws IOException {
return stream(CsvParser.skip(skip).reader(reader));
}
public class CsvSpliterator implements Spliterator<T> {
private final CsvReader csvReader;
private final CellConsumer cellConsumer;
private T current;
public CsvSpliterator(CsvReader csvReader) {
this.csvReader = csvReader;
this.cellConsumer = newCellConsumer(new CheckedConsumer<T>() {
@Override
public void accept(T t) throws Exception {
current = t;
}
});
}
@Override
public boolean tryAdvance(Consumer<? super T> action) {
current = null;
try {
csvReader.parseRow(cellConsumer);
} catch (IOException e) {
return ErrorHelper.rethrow(e);
}
if (current != null) {
action.accept(current);
return true;
} else {
return false;
}
}
@Override
public void forEachRemaining(Consumer<? super T> action) {
try {
csvReader.parseAll(newCellConsumer(new CheckedConsumer<T>() {
@Override
public void accept(T t) throws Exception {
action.accept(t);
}
}));
} catch (IOException e) {
ErrorHelper.rethrow(e);
}
}
@Override
public Spliterator<T> trySplit() {
return null;
}
@Override
public long estimateSize() {
return Long.MAX_VALUE;
}
@Override
public int characteristics() {
return Spliterator.ORDERED | Spliterator.NONNULL;
}
}
//IFJAVA8_END
protected CsvMapperCellConsumer newCellConsumer(final CheckedConsumer<? super T> handler) {
return newCellConsumer(handler, null, false);
}
protected CsvMapperCellConsumer<T> newCellConsumer(final CheckedConsumer<? super T> handler, BreakDetector parentBreakDetector, boolean appendCollection) {
CsvMapperCellConsumer<?>[] cellHandlers = null;
if (hasSubProperties) {
cellHandlers = new CsvMapperCellConsumer<?>[delayedCellSetterFactories.length + setters.length];
}
BreakDetector breakDetector = null;
// check is need to skip breakdetector if no key and append to collection
if ((joinKeys != null && joinKeys.length > 0) || !appendCollection) {
breakDetector = newBreakDetector(parentBreakDetector, delayedCellSetterFactories.length - 1);
}
DelayedCellSetter<T, ?>[] outDelayedCellSetters = getDelayedCellSetters(cellHandlers, breakDetector);
CellSetter<T>[] outSetters = getCellSetters(cellHandlers, breakDetector);
CsvMapperCellHandler<T> mapperSetters = csvMapperCellHandlerFactory.newInstance(outDelayedCellSetters, outSetters);
return new CsvMapperCellConsumer<T>(mapperSetters,
consumerErrorHandlers,
handler,
breakDetector, toList(cellHandlers));
}
@SuppressWarnings("unchecked")
private DelayedCellSetter<T, ?>[] getDelayedCellSetters(CsvMapperCellConsumer<?>[] cellHandlers, BreakDetector breakDetector) {
if (delayedCellSetterFactories.length == 0) {
return EMPTY_DELAYED_CELL_SETTERS;
} else {
return buildDelayedCellSetters(cellHandlers, breakDetector);
}
}
@SuppressWarnings("unchecked")
private DelayedCellSetter<T, ?>[] buildDelayedCellSetters(CsvMapperCellConsumer<?>[] cellHandlers, BreakDetector breakDetector) {
DelayedCellSetter<T, ?>[] outDelayedCellSetters = new DelayedCellSetter[delayedCellSetterFactories.length];
for(int i = delayedCellSetterFactories.length - 1; i >= 0 ; i--) {
DelayedCellSetterFactory<T, ?> delayedCellSetterFactory = delayedCellSetterFactories[i];
if (delayedCellSetterFactory != null) {
outDelayedCellSetters[i] = delayedCellSetterFactory.newCellSetter(breakDetector, cellHandlers);
}
}
return outDelayedCellSetters;
}
private Collection<CsvMapperCellConsumer<?>> toList(CsvMapperCellConsumer<?>[] cellHandlers) {
if (cellHandlers == null) return Collections.emptyList();
List<CsvMapperCellConsumer<?>> consumers = new ArrayList<CsvMapperCellConsumer<?>>();
for(CsvMapperCellConsumer<?> consumer : cellHandlers) {
if (consumer != null) {
consumers.add(consumer);
}
}
return consumers;
}
@SuppressWarnings("unchecked")
private CellSetter<T>[] getCellSetters(CsvMapperCellConsumer<?>[] cellHandlers, BreakDetector breakDetector) {
if (hasSetterSubProperties) {
return rebuildCellSetters(cellHandlers, breakDetector);
} else {
return setters;
}
}
@SuppressWarnings("unchecked")
private CellSetter<T>[] rebuildCellSetters(CsvMapperCellConsumer<?>[] cellHandlers, BreakDetector breakDetector) {
CellSetter<T>[] outSetters = new CellSetter[setters.length];
for(int i = setters.length - 1; i >= 0 ; i--) {
if (setters[i] instanceof DelegateMarkerSetter) {
DelegateCellSetter<T, ?> delegateCellSetter = getDelegateCellSetter((DelegateMarkerSetter)setters[i], cellHandlers, breakDetector, i);
outSetters[i] = delegateCellSetter;
} else {
outSetters[i] = setters[i];
}
}
return outSetters;
}
@SuppressWarnings("unchecked")
private <P> DelegateCellSetter<T, P> getDelegateCellSetter(DelegateMarkerSetter marker, CsvMapperCellConsumer<?>[] cellHandlers, BreakDetector breakDetector, int i) {
final int parent = marker.getParent();
final int cellIndex = i + delayedCellSetterFactories.length;
if(parent == cellIndex) {
final DelegateCellSetter<T, P> tpDelegateCellSetter = new DelegateCellSetter<T, P>(marker, cellIndex, breakDetector);
cellHandlers[cellIndex] = tpDelegateCellSetter.getCellConsumer();
return tpDelegateCellSetter;
} else {
final CsvMapperCellConsumer<?> cellHandler = cellHandlers[parent];
if (cellHandler==null) {
throw new NullPointerException("No cell handler on parent " + parent);
}
return new DelegateCellSetter<T, P>(marker, (CsvMapperCellConsumer<P>) cellHandler, cellIndex);
}
}
private BreakDetector newBreakDetector(BreakDetector parentBreakDetector, int delayedSetterEnd) {
if (parentBreakDetector != null || joinKeys.length > 0) {
return new BreakDetector(joinKeys, parentBreakDetector, delayedSetterEnd);
} else {
return null;
}
}
public CsvMapperCellHandlerFactory<T> csvMapperCellHandlerFactory() {
return csvMapperCellHandlerFactory;
}
@Override
public String toString() {
return "CsvMapperImpl{" +
"targetSettersFactory=" + csvMapperCellHandlerFactory +
", delayedCellSetters=" + Arrays.toString(delayedCellSetterFactories) +
", setters=" + Arrays.toString(setters) +
'}';
}
}