package io.ebeaninternal.server.text.csv;
import io.ebean.Database;
import io.ebean.bean.EntityBean;
import io.ebean.plugin.ExpressionPath;
import io.ebean.text.StringParser;
import io.ebean.text.TextException;
import io.ebean.text.TimeStringParser;
import io.ebean.text.csv.CsvCallback;
import io.ebean.text.csv.CsvReader;
import io.ebean.text.csv.DefaultCsvCallback;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
import io.ebeaninternal.server.el.ElPropertyValue;
import java.io.Reader;
import java.sql.Types;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Locale;
public class TCsvReader<T> implements CsvReader<T> {
private static final TimeStringParser TIME_PARSER = new TimeStringParser();
private final Database server;
private final BeanDescriptor<T> descriptor;
private final List<CsvColumn> columnList = new ArrayList<>();
private final CsvColumn ignoreColumn = new CsvColumn();
private boolean ;
private int logInfoFrequency = 1000;
private String defaultTimeFormat = "HH:mm:ss";
private String defaultDateFormat = "yyyy-MM-dd";
private String defaultTimestampFormat = "yyyy-MM-dd hh:mm:ss.fffffffff";
private Locale defaultLocale = Locale.getDefault();
protected int persistBatchSize = 30;
private boolean ;
public TCsvReader(Database server, BeanDescriptor<T> descriptor) {
this.server = server;
this.descriptor = descriptor;
}
@Override
public void setDefaultLocale(Locale defaultLocale) {
this.defaultLocale = defaultLocale;
}
@Override
public void setDefaultTimeFormat(String defaultTimeFormat) {
this.defaultTimeFormat = defaultTimeFormat;
}
@Override
public void setDefaultDateFormat(String defaultDateFormat) {
this.defaultDateFormat = defaultDateFormat;
}
@Override
public void setDefaultTimestampFormat(String defaultTimestampFormat) {
this.defaultTimestampFormat = defaultTimestampFormat;
}
@Override
public void setPersistBatchSize(int persistBatchSize) {
this.persistBatchSize = persistBatchSize;
}
@Override
public void () {
setHasHeader(true, false);
}
@Override
public void () {
setHasHeader(true, true);
}
@Override
public void (boolean hasHeader, boolean addPropertiesFromHeader) {
this.hasHeader = hasHeader;
this.addPropertiesFromHeader = addPropertiesFromHeader;
}
@Override
public void setLogInfoFrequency(int logInfoFrequency) {
this.logInfoFrequency = logInfoFrequency;
}
@Override
public void addIgnore() {
columnList.add(ignoreColumn);
}
@Override
public void addProperty(String propertyName) {
addProperty(propertyName, null);
}
@Override
public void addDateTime(String propertyName, String dateTimeFormat) {
addDateTime(propertyName, dateTimeFormat, Locale.getDefault());
}
@Override
public void addDateTime(String propertyName, String dateTimeFormat, Locale locale) {
ExpressionPath elProp = descriptor.getExpressionPath(propertyName);
if (!elProp.isDateTimeCapable()) {
throw new TextException("Property " + propertyName + " is not DateTime capable");
}
if (dateTimeFormat == null) {
dateTimeFormat = getDefaultDateTimeFormat(elProp.getJdbcType());
}
if (locale == null) {
locale = defaultLocale;
}
SimpleDateFormat sdf = new SimpleDateFormat(dateTimeFormat, locale);
DateTimeParser parser = new DateTimeParser(sdf, dateTimeFormat, elProp);
CsvColumn column = new CsvColumn(elProp, parser);
columnList.add(column);
}
private String getDefaultDateTimeFormat(int jdbcType) {
switch (jdbcType) {
case Types.TIME:
return defaultTimeFormat;
case Types.DATE:
return defaultDateFormat;
case Types.TIMESTAMP:
return defaultTimestampFormat;
default:
throw new RuntimeException("Expected java.sql.Types TIME,DATE or TIMESTAMP but got [" + jdbcType + "]");
}
}
@Override
public void addProperty(String propertyName, StringParser parser) {
ExpressionPath elProp = descriptor.getExpressionPath(propertyName);
if (parser == null) {
parser = elProp.getStringParser();
}
CsvColumn column = new CsvColumn(elProp, parser);
columnList.add(column);
}
@Override
public void process(Reader reader) throws Exception {
DefaultCsvCallback<T> callback = new DefaultCsvCallback<>(persistBatchSize, logInfoFrequency);
process(reader, callback);
}
@Override
public void process(Reader reader, CsvCallback<T> callback) throws Exception {
if (reader == null) {
throw new NullPointerException("reader is null?");
}
if (callback == null) {
throw new NullPointerException("callback is null?");
}
CsvUtilReader utilReader = new CsvUtilReader(reader);
callback.begin(server);
int row = 0;
if (hasHeader) {
String[] line = utilReader.readNext();
if (addPropertiesFromHeader) {
addPropertiesFromHeader(line);
}
callback.readHeader(line);
}
try {
do {
++row;
String[] line = utilReader.readNext();
if (line == null) {
--row;
break;
}
if (callback.processLine(row, line)) {
if (line.length != columnList.size()) {
String msg = "Error at line " + row + ". Expected [" + columnList.size() + "] columns "
+ "but instead we have [" + line.length + "]. Line[" + Arrays.toString(line) + "]";
throw new TextException(msg);
}
T bean = buildBeanFromLineContent(row, line);
callback.processBean(row, line, bean);
}
} while (true);
callback.end(row);
} catch (Exception e) {
callback.endWithError(row, e);
throw e;
}
}
private void (String[] line) {
for (String aLine : line) {
ElPropertyValue elProp = descriptor.getElGetValue(aLine);
if (elProp == null) {
throw new TextException("Property [" + aLine + "] not found");
}
if (Types.TIME == elProp.getJdbcType()) {
addProperty(aLine, TIME_PARSER);
} else if (isDateTimeType(elProp.getJdbcType())) {
addDateTime(aLine, null, null);
} else if (elProp.isAssocProperty()) {
BeanPropertyAssocOne<?> assocOne = (BeanPropertyAssocOne<?>) elProp.getBeanProperty();
String idProp = assocOne.getBeanDescriptor().getIdBinder().getIdProperty();
addProperty(aLine + "." + idProp);
} else {
addProperty(aLine);
}
}
}
private boolean isDateTimeType(int t) {
return t == Types.TIMESTAMP || t == Types.DATE || t == Types.TIME;
}
@SuppressWarnings("unchecked")
protected T buildBeanFromLineContent(int row, String[] line) {
try {
EntityBean entityBean = descriptor.createEntityBean();
T bean = (T) entityBean;
for (int columnPos = 0; columnPos < line.length; columnPos++) {
convertAndSetColumn(columnPos, line[columnPos], entityBean);
}
return bean;
} catch (RuntimeException e) {
String msg = "Error at line: " + row + " line[" + Arrays.toString(line) + "]";
throw new RuntimeException(msg, e);
}
}
protected void convertAndSetColumn(int columnPos, String strValue, EntityBean bean) {
strValue = strValue.trim();
if (strValue.isEmpty()) {
return;
}
CsvColumn c = columnList.get(columnPos);
c.convertAndSet(strValue, bean);
}
public static class CsvColumn {
private final ExpressionPath path;
private final StringParser parser;
private CsvColumn() {
this.path = null;
this.parser = null;
}
public CsvColumn(ExpressionPath path, StringParser parser) {
this.path = path;
this.parser = parser;
}
public void convertAndSet(String strValue, EntityBean bean) {
if (parser != null && path != null) {
Object value = parser.parse(strValue);
path.pathSet(bean, value);
}
}
}
private static class DateTimeParser implements StringParser {
private final DateFormat dateFormat;
private final ExpressionPath path;
private final String format;
DateTimeParser(DateFormat dateFormat, String format, ExpressionPath path) {
this.dateFormat = dateFormat;
this.path = path;
this.format = format;
}
@Override
public Object parse(String value) {
try {
Date dt = dateFormat.parse(value);
return path.parseDateTime(dt.getTime());
} catch (ParseException e) {
throw new TextException("Error parsing [{}] using format[" + format + "]", value, e);
}
}
}
}