mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
* no effective code change: add missing @override in "io.ebean.*" package * no effective code change: add missing @override in "io.ebeaninternal.api.*" package * no effective code change: add missing @override in "io.ebeaninternal.server.*" package * no effective code change: add missing @override in "io.ebeaninternal.util.*" package * no effective code change: add missing @override in "io.ebeanservice.*" package * no effective code change: add missing @override in "src/test/java/"
360 lines
9.6 KiB
Java
360 lines
9.6 KiB
Java
package io.ebeaninternal.server.text.csv;
|
|
|
|
import io.ebean.EbeanServer;
|
|
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;
|
|
|
|
/**
|
|
* Implementation of the CsvReader
|
|
*/
|
|
public class TCsvReader<T> implements CsvReader<T> {
|
|
|
|
private static final TimeStringParser TIME_PARSER = new TimeStringParser();
|
|
|
|
private final EbeanServer server;
|
|
|
|
private final BeanDescriptor<T> descriptor;
|
|
|
|
private final List<CsvColumn> columnList = new ArrayList<>();
|
|
|
|
private final CsvColumn ignoreColumn = new CsvColumn();
|
|
|
|
private boolean hasHeader;
|
|
|
|
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();
|
|
|
|
/**
|
|
* The batch size used for JDBC statement batching.
|
|
*/
|
|
protected int persistBatchSize = 30;
|
|
|
|
private boolean addPropertiesFromHeader;
|
|
|
|
public TCsvReader(EbeanServer 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 setIgnoreHeader() {
|
|
setHasHeader(true, false);
|
|
}
|
|
|
|
@Override
|
|
public void setAddPropertiesFromHeader() {
|
|
setHasHeader(true, true);
|
|
}
|
|
|
|
@Override
|
|
public void setHasHeader(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)) {
|
|
// the line content is expected to be ok for processing
|
|
if (line.length != columnList.size()) {
|
|
// we have not got the expected number of columns
|
|
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) {
|
|
// notify that an error occurred so that any
|
|
// transaction can be rolled back if required
|
|
callback.endWithError(row, e);
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
private void addPropertiesFromHeader(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);
|
|
}
|
|
|
|
/**
|
|
* Processes a column in the csv content.
|
|
*/
|
|
public static class CsvColumn {
|
|
|
|
private final ExpressionPath path;
|
|
private final StringParser parser;
|
|
|
|
/**
|
|
* Constructor for the IGNORE column.
|
|
*/
|
|
private CsvColumn() {
|
|
this.path = null;
|
|
this.parser = null;
|
|
}
|
|
|
|
/**
|
|
* Construct with a property and parser.
|
|
*/
|
|
public CsvColumn(ExpressionPath path, StringParser parser) {
|
|
this.path = path;
|
|
this.parser = parser;
|
|
}
|
|
|
|
/**
|
|
* Convert the string to the appropriate value and set it to the bean.
|
|
*/
|
|
public void convertAndSet(String strValue, EntityBean bean) {
|
|
|
|
if (parser != null && path != null) {
|
|
Object value = parser.parse(strValue);
|
|
path.pathSet(bean, value);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A StringParser for converting custom date/time/datetime strings into
|
|
* appropriate java types (Date, Calendar, SQL Date, Time, Timestamp, JODA
|
|
* etc).
|
|
*/
|
|
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 [" + value + "] using format[" + format + "]", e);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|