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

cn.gjing.tools.excel.read.resolver.ReadExecutor Maven / Gradle / Ivy

There is a newer version: 2021.12.3
Show newest version
package cn.gjing.tools.excel.read.resolver;

import cn.gjing.tools.excel.ExcelField;
import cn.gjing.tools.excel.convert.DataConvert;
import cn.gjing.tools.excel.convert.DefaultDataConvert;
import cn.gjing.tools.excel.convert.ExcelDataConvert;
import cn.gjing.tools.excel.exception.ExcelAssertException;
import cn.gjing.tools.excel.exception.ExcelInitException;
import cn.gjing.tools.excel.exception.ExcelResolverException;
import cn.gjing.tools.excel.exception.ExcelTemplateException;
import cn.gjing.tools.excel.metadata.ExcelReaderResolver;
import cn.gjing.tools.excel.read.ExcelReaderContext;
import cn.gjing.tools.excel.read.listener.ExcelEmptyReadListener;
import cn.gjing.tools.excel.read.listener.ExcelReadListener;
import cn.gjing.tools.excel.read.listener.ExcelRowReadListener;
import cn.gjing.tools.excel.read.valid.ExcelAssert;
import cn.gjing.tools.excel.util.BeanUtils;
import cn.gjing.tools.excel.util.ListenerChain;
import cn.gjing.tools.excel.util.ParamUtils;
import com.google.gson.Gson;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import java.lang.reflect.Field;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;

/**
 * @author Gjing
 **/
class ReadExecutor implements ExcelReaderResolver {
    private ExcelReaderContext context;
    private Map>, DataConvert> dataConvertMap;
    private Boolean save;

    @Override
    public void init(ExcelReaderContext readerContext) {
        this.context = readerContext;
        this.dataConvertMap = new HashMap<>(16);
        this.dataConvertMap.put(DefaultDataConvert.class, new DefaultDataConvert());
    }

    @Override
    public void read(int headerIndex, String sheetName) {
        if (this.context.isCheckTemplate()) {
            if (this.context.getWorkbook().getSheetIndex("identificationSheet") == -1) {
                throw new ExcelTemplateException();
            }
            for (Row row : this.context.getWorkbook().getSheet("identificationSheet")) {
                if (!ParamUtils.equals(this.context.getExcelClass().getSimpleName(), row.getCell(0).getStringCellValue(), false)) {
                    throw new ExcelTemplateException();
                }
                break;
            }
        }
        Sheet sheet = this.context.getWorkbook().getSheet(sheetName);
        if (sheet == null) {
            throw new ExcelResolverException("The" + sheetName + " is not found in the workbook");
        }
        this.context.setSheet(sheet);
        this.reader(headerIndex, this.context.getResultReadListener() == null ? null : new ArrayList<>());
    }

    /**
     * Start read
     *
     * @param headerIndex Excel header index
     * @param dataList    All data
     */
    private void reader(int headerIndex, List dataList) {
        R r;
        this.save = true;
        boolean stop = false;
        ExpressionParser parser = new SpelExpressionParser();
        EvaluationContext context = new StandardEvaluationContext();
        List rowReadListeners = this.context.getReadListenersCache().get(ExcelRowReadListener.class);
        Gson gson = new Gson();
        List otherValues;
        ListenerChain.doReadBefore(rowReadListeners, this.context);
        for (Row row : this.context.getSheet()) {
            if (stop) {
                break;
            }
            if (row.getRowNum() > headerIndex) {
                try {
                    r = this.context.getExcelClass().newInstance();
                    context.setVariable(this.context.getExcelClass().getSimpleName(), r);
                } catch (InstantiationException | IllegalAccessException e) {
                    throw new ExcelInitException("Excel entity init failure, " + e.getMessage());
                }
                for (int c = 0, fieldSize = this.context.getExcelFields().size(); c < fieldSize && save; c++) {
                    Field field = this.context.getExcelFields().get(c);
                    ExcelField excelField = field.getAnnotation(ExcelField.class);
                    Cell valueCell = row.getCell(c);
                    Object value;
                    try {
                        if (valueCell != null) {
                            value = this.getValue(r, valueCell, field, excelField, gson);
                            context.setVariable(field.getName(), value);
                            this.assertValue(parser, context, row, c, field, excelField);
                            value = ListenerChain.doReadCell(rowReadListeners, value, field, row.getRowNum(), c, false, true);
                            value = this.convert(field, value, parser, context, this.createDataConvert(field, excelField));
                            if (save && value != null) {
                                this.setValue(r, field, value);
                            }
                        } else {
                            this.allowEmpty(r, field, excelField, row.getRowNum(), c);
                            context.setVariable(field.getName(), null);
                            this.assertValue(parser, context, row, c, field, excelField);
                            value = ListenerChain.doReadCell(rowReadListeners, null, field, row.getRowNum(), c, false, true);
                            value = this.convert(field, value, parser, context, this.createDataConvert(field, excelField));
                            this.setValue(r, field, value);
                        }
                        context.setVariable(field.getName(), value);
                    } catch (Exception e) {
                        if (e instanceof ExcelAssertException) {
                            throw (ExcelAssertException) e;
                        }
                        throw new ExcelResolverException(e.getMessage());
                    }
                }
                if (save) {
                    try {
                        if (dataList != null) {
                            dataList.add(r);
                        }
                        ListenerChain.doReadRow(rowReadListeners, r, null, row.getRowNum(), false, true);
                    } catch (Exception e) {
                        throw new ExcelResolverException(e.getMessage());
                    }
                }
                continue;
            }
            if (this.context.isMetaInfo()) {
                boolean isHead = row.getRowNum() == headerIndex;
                otherValues = new ArrayList<>();
                for (Cell cell : row) {
                    otherValues.add(ListenerChain.doReadCell(rowReadListeners, cell.getStringCellValue(), null, row.getRowNum(), cell.getColumnIndex(), isHead, false));
                }
                stop = ListenerChain.doReadRow(rowReadListeners, null, otherValues, row.getRowNum(), isHead, false);
            }
        }
        ListenerChain.doReadFinish(rowReadListeners, this.context);
        ListenerChain.doResultNotify(this.context.getResultReadListener(), dataList);
    }

    /**
     * Get the value of the cell
     *
     * @param cell       cell
     * @param excelField Excel field of current field
     * @param field      Current field
     * @param gson       Gson
     * @param r          Current row generated row
     * @return value
     */
    private Object getValue(R r, Cell cell, Field field, ExcelField excelField, Gson gson) {
        switch (cell.getCellType()) {
            case _NONE:
            case BLANK:
            case ERROR:
                this.allowEmpty(r, field, excelField, cell.getRowIndex(), cell.getColumnIndex());
                break;
            case BOOLEAN:
                return cell.getBooleanCellValue();
            case NUMERIC:
                if (DateUtil.isCellDateFormatted(cell)) {
                    return cell.getDateCellValue();
                }
                return gson.fromJson(gson.toJson(cell.getNumericCellValue()), field.getType());
            case FORMULA:
                return gson.fromJson(gson.toJson(cell.getStringCellValue()), field.getType());
            default:
                return excelField.trim() ? cell.getStringCellValue().trim() : cell.getStringCellValue();
        }
        return null;
    }

    /**
     * Data convert
     *
     * @param field   Current field
     * @param value   Attribute values
     * @param parser  El parser
     * @param context EL context
     * @return new value
     */
    private Object convert(Field field, Object value, ExpressionParser parser, EvaluationContext context, DataConvert dataConvert) {
        ExcelDataConvert excelDataConvert = field.getAnnotation(ExcelDataConvert.class);
        if (excelDataConvert != null && !"".equals(excelDataConvert.expr2())) {
            return parser.parseExpression(excelDataConvert.expr2()).getValue(context);
        }
        return dataConvert.toEntityAttribute(value, field);
    }

    /**
     * Create data convert
     *
     * @param field      Current field
     * @param excelField ExcelField annotation on current field
     * @return DataConvert
     */
    private DataConvert createDataConvert(Field field, ExcelField excelField) {
        DataConvert dataConvert = this.dataConvertMap.get(excelField.convert());
        if (dataConvert == null) {
            try {
                dataConvert = excelField.convert().newInstance();
                this.dataConvertMap.put(excelField.convert(), dataConvert);
            } catch (Exception e) {
                throw new ExcelInitException("Init specified excel header data converter failure " + field.getName() + ", " + e.getMessage());
            }
        }
        return dataConvert;
    }

    /**
     * Set value for the field of the object
     *
     * @param o     object
     * @param field field
     * @param value value
     */
    private void setValue(R o, Field field, Object value) {
        try {
            BeanUtils.setFieldValue(o, field, value);
        } catch (RuntimeException e) {
            if (field.getType() == LocalDate.class) {
                BeanUtils.setFieldValue(o, field, LocalDateTime.ofInstant(((Date) value).toInstant(), ZoneId.systemDefault()).toLocalDate());
                return;
            }
            if (field.getType() == LocalDateTime.class) {
                BeanUtils.setFieldValue(o, field, LocalDateTime.ofInstant(((Date) value).toInstant(), ZoneId.systemDefault()));
                return;
            }
            throw new IllegalArgumentException("Unsupported data type, you can use a data converter to convert " + value.getClass().getTypeName() + " to " + field.getType().getTypeName());
        }
    }

    /**
     * Check is not empty strategy
     *
     * @param field      Current field
     * @param excelField ExcelFiled annotation on current filed
     * @param rowIndex   Current row index
     * @param colIndex   Current col index
     * @param r          Current row generated java object
     */
    private void allowEmpty(R r, Field field, ExcelField excelField, int rowIndex, int colIndex) {
        if (excelField.required()) {
            this.save = ListenerChain.doReadEmpty(this.context.getReadListenersCache()
                    .get(ExcelEmptyReadListener.class), r, field, excelField, rowIndex, colIndex);
        }
    }

    /**
     * Cell value assert
     *
     * @param parser     El parser
     * @param context    EL context
     * @param row        Current row
     * @param c          Current col index
     * @param field      Current field
     * @param excelField ExcelFiled annotation on current filed
     */
    private void assertValue(ExpressionParser parser, EvaluationContext context, Row row, int c, Field field, ExcelField excelField) {
        ExcelAssert excelAssert = field.getAnnotation(ExcelAssert.class);
        if (excelAssert != null) {
            Boolean test = parser.parseExpression(excelAssert.expr()).getValue(context, Boolean.class);
            if (test != null && !test) {
                throw new ExcelAssertException(excelAssert.message(), excelField, field, row.getRowNum(), c);
            }
        }
    }
}