All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.ebeaninternal.server.text.csv.TCsvReader Maven / Gradle / Ivy

There is a newer version: 15.8.1
Show newest version
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 implements CsvReader {

  private static final TimeStringParser TIME_PARSER = new TimeStringParser();

  private final EbeanServer server;

  private final BeanDescriptor descriptor;

  private final List 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 descriptor) {
    this.server = server;
    this.descriptor = descriptor;
  }

  public void setDefaultLocale(Locale defaultLocale) {
    this.defaultLocale = defaultLocale;
  }

  public void setDefaultTimeFormat(String defaultTimeFormat) {
    this.defaultTimeFormat = defaultTimeFormat;
  }

  public void setDefaultDateFormat(String defaultDateFormat) {
    this.defaultDateFormat = defaultDateFormat;
  }

  public void setDefaultTimestampFormat(String defaultTimestampFormat) {
    this.defaultTimestampFormat = defaultTimestampFormat;
  }

  public void setPersistBatchSize(int persistBatchSize) {
    this.persistBatchSize = persistBatchSize;
  }

  public void setIgnoreHeader() {
    setHasHeader(true, false);
  }

  public void setAddPropertiesFromHeader() {
    setHasHeader(true, true);
  }

  public void setHasHeader(boolean hasHeader, boolean addPropertiesFromHeader) {
    this.hasHeader = hasHeader;
    this.addPropertiesFromHeader = addPropertiesFromHeader;
  }

  public void setLogInfoFrequency(int logInfoFrequency) {
    this.logInfoFrequency = logInfoFrequency;
  }

  public void addIgnore() {
    columnList.add(ignoreColumn);
  }

  public void addProperty(String propertyName) {
    addProperty(propertyName, null);
  }

  public void addDateTime(String propertyName, String dateTimeFormat) {
    addDateTime(propertyName, dateTimeFormat, Locale.getDefault());
  }

  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 + "]");
    }
  }

  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);
  }

  public void process(Reader reader) throws Exception {
    DefaultCsvCallback callback = new DefaultCsvCallback<>(persistBatchSize, logInfoFrequency);
    process(reader, callback);
  }

  public void process(Reader reader, CsvCallback 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;

      int columnPos = 0;
      for (; 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;
    }

    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);
      }
    }

  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy