public class DefaultConvertible implements WriteConvertible, ReadConvertible {

    @Override
    public Object execRead(String object) {
        throw new UnsupportedOperationException();
    }

    @Override
    public Object execWrite(Object object) {
        throw new UnsupportedOperationException();
    }
} public class Excel4JException extends Exception {

    public Excel4JException() {
    }

    public Excel4JException(String message) {
        super(message);
    }

    public Excel4JException(String message, Throwable cause) {
        super(message, cause);
    }

    public Excel4JException(Throwable cause) {
        super(cause);
    }
} public class Excel4jReadException extends RuntimeException {

	private static final long serialVersionUID = 8735084330744657672L;

	public Excel4jReadException() {
	    super();
    }

    public Excel4jReadException(Throwable cause) {
        super(cause);
    }

    public Excel4jReadException(String message) {
        super(message);
    }

    public Excel4jReadException(String message, Throwable cause) {
        super(message, cause);
    }
} public class ExcelHeader implements Comparable<ExcelHeader> {

    /**
     * excel的标题名称
     */
    private String title;

    /**
     * 每一个标题的顺序
     */
    private int order;

    /**
     * 写数据转换器
     */
    private WriteConvertible writeConverter;

    /**
     * 读数据转换器
     */
    private ReadConvertible readConverter;

    /**
     * 注解域
     */
    private String filed;

    /**
     * 属性类型
     */
    private Class<?> filedClazz;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public int getOrder() {
        return order;
    }

    public void setOrder(int order) {
        this.order = order;
    }

    public WriteConvertible getWriteConverter() {
        return writeConverter;
    }

    public void setWriteConverter(WriteConvertible writeConverter) {
        this.writeConverter = writeConverter;
    }

    public ReadConvertible getReadConverter() {
        return readConverter;
    }

    public void setReadConverter(ReadConvertible readConverter) {
        this.readConverter = readConverter;
    }

    public String getFiled() {
        return filed;
    }

    public void setFiled(String filed) {
        this.filed = filed;
    }

    public Class<?> getFiledClazz() {
        return filedClazz;
    }

    public void setFiledClazz(Class<?> filedClazz) {
        this.filedClazz = filedClazz;
    }

    @Override
    public int compareTo(ExcelHeader o) {
        return order - o.order;
    }

    public ExcelHeader() {
        super();
    }

    public ExcelHeader(String title, int order, WriteConvertible writeConverter,
                       ReadConvertible readConverter, String filed, Class<?> filedClazz) {
        super();
        this.title = title;
        this.order = order;
        this.writeConverter = writeConverter;
        this.readConverter = readConverter;
        this.filed = filed;
        this.filedClazz = filedClazz;
    }
} public class SheetTemplate implements Closeable {

    /**
     * 当前工作簿
     */
    Workbook workbook;
    /**
     * 当前工作sheet表
     */
    Sheet sheet;
    /**
     * 当前表编号
     */
    int sheetIndex;
    /**
     * 当前行
     */
    Row currentRow;
    /**
     * 当前列数
     */
    int currentColumnIndex;
    /**
     * 当前行数
     */
    int currentRowIndex;
    /**
     * 默认样式
     */
    CellStyle defaultStyle;
    /**
     * 指定行样式
     */
    Map<Integer, CellStyle> appointLineStyle = new HashMap<>();
    /**
     * 分类样式模板
     */
    Map<String, CellStyle> classifyStyle = new HashMap<>();
    /**
     * 单数行样式
     */
    CellStyle singleLineStyle;
    /**
     * 双数行样式
     */
    CellStyle doubleLineStyle;
    /**
     * 数据的初始化列数
     */
    int initColumnIndex;
    /**
     * 数据的初始化行数
     */
    int initRowIndex;

    /**
     * 最后一行的数据
     */
    int lastRowIndex;
    /**
     * 默认行高
     */
    float rowHeight;
    /**
     * 序号坐标点
     */
    int serialNumberColumnIndex = -1;
    /**
     * 当前序号
     */
    int serialNumber;

    /*-----------------------------------写出数据开始-----------------------------------*/

    /**
     * 将文件写到相应的路径下
     *
     * @param filePath 输出文件路径
     */
    public void write2File(String filePath) throws Excel4JException {

        try (FileOutputStream fos = new FileOutputStream(filePath)) {
            this.workbook.write(fos);
        } catch (IOException e) {
            throw new Excel4JException(e);
        }
    }

    /**
     * 将文件写到某个输出流中
     *
     * @param os 输出流
     */
    public void write2Stream(OutputStream os) throws Excel4JException {

        try {
            this.workbook.write(os);
        } catch (IOException e) {
            throw new Excel4JException(e);
        }
    }

    /*-----------------------------------写出数据结束-----------------------------------*/

    @Override
    public void close() throws IOException {
        if (null != this.workbook){
            this.workbook.close();
        }
    }

} public class SheetTemplateHandler {

    // 构建SheetTemplate
    public static SheetTemplate sheetTemplateBuilder(String templatePath)
            throws Excel4JException {
        SheetTemplate sheetTemplate = new SheetTemplate();
        try {
            // 读取模板文件
            sheetTemplate.workbook = WorkbookFactory.create(new FileInputStream(new File(templatePath)));
        } catch (Exception e) {
            // 读取模板相对文件
            try {
                sheetTemplate.workbook = WorkbookFactory.create(
                        SheetTemplateHandler.class.getResourceAsStream(templatePath)
                );
            } catch (IOException e1) {
                throw new Excel4JException(e1);
            }
        }
        return sheetTemplate;
    }

    public static SheetTemplate sheetTemplateBuilder(InputStream is)
            throws Excel4JException {
        SheetTemplate sheetTemplate = new SheetTemplate();

        // 读取模板文件
        try {
            sheetTemplate.workbook = WorkbookFactory.create(is);
        } catch (IOException e) {
            throw new Excel4JException(e);
        }
        return sheetTemplate;
    }

    /*-----------------------------------初始化模板开始-----------------------------------*/

    public static void loadTemplate(SheetTemplate template, int sheetIndex) {

        if (sheetIndex < 0) sheetIndex = 0;
        template.sheetIndex = sheetIndex;
        template.sheet = template.workbook.getSheetAt(sheetIndex);
        initModuleConfig(template);
        template.currentRowIndex = template.initRowIndex;
        template.currentColumnIndex = template.initColumnIndex;
        template.lastRowIndex = template.sheet.getLastRowNum();
    }

    /**
     * 初始化数据信息
     */
    private static void initModuleConfig(SheetTemplate template) {

        for (Row row : template.sheet) {
            for (Cell c : row) {
                if (c.getCellType() != CellType.STRING)
                    continue;
                String str = c.getStringCellValue().trim().toLowerCase();
                // 寻找序号列
                if (HandlerConstant.SERIAL_NUMBER.equals(str)) {
                    template.serialNumberColumnIndex = c.getColumnIndex();
                }
                // 寻找数据列
                if (HandlerConstant.DATA_INIT_INDEX.equals(str)) {
                    template.initColumnIndex = c.getColumnIndex();
                    template.initRowIndex = row.getRowNum();
                    template.rowHeight = row.getHeightInPoints();
                }
                // 初始化自定义模板样式
                initStyles(template, c, str);
            }
        }
    }

    /**
     * 初始化样式信息
     */
    private static void initStyles(SheetTemplate template, Cell cell, String moduleContext) {
        if (null == moduleContext || "".equals(moduleContext))
            return;
        if (!moduleContext.startsWith("&"))
            moduleContext = moduleContext.toLowerCase();
        if (HandlerConstant.DEFAULT_STYLE.equals(moduleContext)) {
            template.defaultStyle = cell.getCellStyle();
            clearCell(cell);
        }
        if (moduleContext.startsWith("&") && moduleContext.length() > 1) {
            template.classifyStyle.put(moduleContext.substring(1), cell.getCellStyle());
            clearCell(cell);
        }
        if (HandlerConstant.APPOINT_LINE_STYLE.equals(moduleContext)) {
            template.appointLineStyle.put(cell.getRowIndex(), cell.getCellStyle());
            clearCell(cell);
        }
        if (HandlerConstant.SINGLE_LINE_STYLE.equals(moduleContext)) {
            template.singleLineStyle = cell.getCellStyle();
            clearCell(cell);
        }
        if (HandlerConstant.DOUBLE_LINE_STYLE.equals(moduleContext)) {
            template.doubleLineStyle = cell.getCellStyle();
            clearCell(cell);
        }
    }

    private static void clearCell(Cell cell) {
        cell.setCellStyle(null);
        cell.setCellValue("");
    }

    /*-----------------------------------初始化模板结束-----------------------------------*/

    /*------------------------------------数据填充开始------------------------------------*/

    /**
     * 根据map替换相应的常量，通过Map中的值来替换#开头的值
     *
     * @param data 替换映射
     */
    public static void extendData(SheetTemplate template, Map<String, String> data) {
        if (data == null)
            return;
        for (Row row : template.sheet) {
            for (Cell c : row) {
                if (c.getCellType() != CellType.STRING)
                    continue;
                String str = c.getStringCellValue().trim();
                if (str.startsWith("#") && data.containsKey(str.substring(1))) {
                    c.setCellValue(data.get(str.substring(1)));
                }
            }
        }
    }

    /**
     * 创建新行，在使用时只要添加完一行，需要调用该方法创建
     */
    public static void createNewRow(SheetTemplate template) {
        if (template.lastRowIndex > template.currentRowIndex && template.currentRowIndex != template.initRowIndex) {
            template.sheet.shiftRows(template.currentRowIndex, template.lastRowIndex, 1, true, true);
            template.lastRowIndex++;
        }
        template.currentRow = template.sheet.createRow(template.currentRowIndex);
        template.currentRow.setHeightInPoints(template.rowHeight);
        template.currentRowIndex++;
        template.currentColumnIndex = template.initColumnIndex;
    }

    /**
     * 插入序号，会自动找相应的序号标示的位置完成插入
     *
     * @param styleKey 样式标识
     */
    public static void insertSerial(SheetTemplate template, String styleKey) {
        if (template.serialNumberColumnIndex < 0)
            return;
        template.serialNumber++;
        Cell c = template.currentRow.createCell(template.serialNumberColumnIndex);
        setCellStyle(template, c, styleKey);
        c.setCellValue(template.serialNumber);
    }

    /**
     * <p>设置Excel元素样式及内容</p>
     *
     * @param value    内容
     * @param styleKey 样式
     */
    public static void createCell(SheetTemplate template, Object value, String styleKey) {
        Cell cell = template.currentRow.createCell(template.currentColumnIndex);
        setCellStyle(template, cell, styleKey);
        if (null == value || "".equals(value)) {
            template.currentColumnIndex++;
            return;
        }

        if (String.class == value.getClass()) {
            cell.setCellValue((String) value);
            template.currentColumnIndex++;
            return;
        }

        if (int.class == value.getClass()) {
            cell.setCellValue((int) value);
            template.currentColumnIndex++;
            return;
        }

        if (Integer.class == value.getClass()) {
            cell.setCellValue((Integer) value);
            template.currentColumnIndex++;
            return;
        }

        if (double.class == value.getClass()) {
            cell.setCellValue((double) value);
            template.currentColumnIndex++;
            return;
        }

        if (Double.class == value.getClass()) {
            cell.setCellValue((Double) value);
            template.currentColumnIndex++;
            return;
        }

        if (Date.class == value.getClass()) {
            cell.setCellValue((Date) value);
            template.currentColumnIndex++;
            return;
        }

        if (boolean.class == value.getClass()) {
            cell.setCellValue((boolean) value);
            template.currentColumnIndex++;
            return;
        }

        if (Boolean.class == value.getClass()) {
            cell.setCellValue((Boolean) value);
            template.currentColumnIndex++;
            return;
        }

        if (Calendar.class == value.getClass()) {
            cell.setCellValue((Calendar) value);
            template.currentColumnIndex++;
            return;
        }

        if (RichTextString.class == value.getClass()) {
            cell.setCellValue((RichTextString) value);
            template.currentColumnIndex++;
            return;
        }
        // default value#toString
        cell.setCellValue(value.toString());
        template.currentColumnIndex++;
    }

    /**
     * 设置某个元素的样式
     *
     * @param cell     cell元素
     * @param styleKey 样式标识
     */
    private static void setCellStyle(SheetTemplate template, Cell cell, String styleKey) {
        if (null != styleKey && null != template.classifyStyle.get(styleKey)) {
            cell.setCellStyle(template.classifyStyle.get(styleKey));
            return;
        }

        if (null != template.appointLineStyle && template.appointLineStyle.containsKey(cell.getRowIndex())) {
            cell.setCellStyle(template.appointLineStyle.get(cell.getRowIndex()));
            return;
        }
        if (null != template.singleLineStyle && (cell.getRowIndex() % 2 != 0)) {
            cell.setCellStyle(template.singleLineStyle);
            return;
        }
        if (null != template.doubleLineStyle && (cell.getRowIndex() % 2 == 0)) {
            cell.setCellStyle(template.doubleLineStyle);
            return;
        }
        if (null != template.defaultStyle)
            cell.setCellStyle(template.defaultStyle);
    }

    /*-----------------------------------数据填充结束-----------------------------------*/

} public class MapSheetWrapper {

    /**
     * sheet序号
     */
    private int sheetIndex;

    /**
     * 表格行数据
     */
    private Map<String, List<?>> data;

    /**
     * 扩展数据
     */
    private Map<String, String> extendMap;

    /**
     * 注解的class
     */
    private Class clazz;

    /**
     * 是否写表头
     */
    private boolean isWriteHeader;

    public MapSheetWrapper() {
    }

    public MapSheetWrapper(Map<String, List<?>> data, Class clazz) {
        this.data = data;
        this.clazz = clazz;
    }

    public MapSheetWrapper(int sheetIndex, Map<String, List<?>> data, Class clazz) {
        this.sheetIndex = sheetIndex;
        this.data = data;
        this.clazz = clazz;
    }

    public MapSheetWrapper(Map<String, List<?>> data, Map<String, String> extendMap, Class clazz) {
        this.data = data;
        this.extendMap = extendMap;
        this.clazz = clazz;
    }

    public MapSheetWrapper(int sheetIndex, Map<String, List<?>> data, Map<String, String> extendMap, Class clazz,
                           boolean isWriteHeader) {
        this.sheetIndex = sheetIndex;
        this.data = data;
        this.extendMap = extendMap;
        this.clazz = clazz;
        this.isWriteHeader = isWriteHeader;
    }

    public int getSheetIndex() {
        return sheetIndex;
    }

    public void setSheetIndex(int sheetIndex) {
        this.sheetIndex = sheetIndex;
    }

    public Map<String, List<?>> getData() {
        return data;
    }

    public void setData(Map<String, List<?>> data) {
        this.data = data;
    }

    public Map<String, String> getExtendMap() {
        return extendMap;
    }

    public void setExtendMap(Map<String, String> extendMap) {
        this.extendMap = extendMap;
    }

    public Class getClazz() {
        return clazz;
    }

    public void setClazz(Class clazz) {
        this.clazz = clazz;
    }

    public boolean isWriteHeader() {
        return isWriteHeader;
    }

    public void setWriteHeader(boolean writeHeader) {
        isWriteHeader = writeHeader;
    }
} public class NoTemplateSheetWrapper {

    /**
     * 待导出行数据
     */
    private List<?> data;

    /**
     * 基于注解的class
     */
    private Class clazz;

    /**
     * 是否写入表头
     */
    private boolean isWriteHeader;

    /**
     * sheet名
     */
    private String sheetName;

    public NoTemplateSheetWrapper() {
    }

    public NoTemplateSheetWrapper(List<?> data, Class clazz) {
        this.data = data;
        this.clazz = clazz;
    }

    public NoTemplateSheetWrapper(List<?> data, Class clazz, boolean isWriteHeader) {
        this.data = data;
        this.clazz = clazz;
        this.isWriteHeader = isWriteHeader;
    }

    public NoTemplateSheetWrapper(List<?> data, Class clazz, boolean isWriteHeader, String sheetName) {
        this.data = data;
        this.clazz = clazz;
        this.isWriteHeader = isWriteHeader;
        this.sheetName = sheetName;
    }

    public List<?> getData() {
        return data;
    }

    public void setData(List<?> data) {
        this.data = data;
    }

    public Class getClazz() {
        return clazz;
    }

    public void setClazz(Class clazz) {
        this.clazz = clazz;
    }

    public boolean isWriteHeader() {
        return isWriteHeader;
    }

    public void setWriteHeader(boolean writeHeader) {
        isWriteHeader = writeHeader;
    }

    public String getSheetName() {
        return sheetName;
    }

    public void setSheetName(String sheetName) {
        this.sheetName = sheetName;
    }
} public class NormalSheetWrapper {

    /**
     * sheet的序号
     */
    private int sheetIndex;

    /**
     * 表格行数据
     */
    private List<?> data;

    /**
     * 扩张数据
     */
    private Map<String, String> extendMap;

    /**
     * 基于注解的class
     */
    private Class clazz;

    /**
     * 是否写入表头
     */
    private boolean isWriteHeader;

    public NormalSheetWrapper() {
    }

    public NormalSheetWrapper(List<?> data, Class clazz) {
        this.data = data;
        this.clazz = clazz;
    }

    public NormalSheetWrapper(int sheetIndex, List<?> data, Class clazz) {
        this.sheetIndex = sheetIndex;
        this.data = data;
        this.clazz = clazz;
    }

    public NormalSheetWrapper(int sheetIndex, List<?> data, Map<String, String> extendMap, Class clazz,
                              boolean isWriteHeader) {
        this.sheetIndex = sheetIndex;
        this.data = data;
        this.extendMap = extendMap;
        this.clazz = clazz;
        this.isWriteHeader = isWriteHeader;
    }

    public int getSheetIndex() {
        return sheetIndex;
    }

    public void setSheetIndex(int sheetIndex) {
        this.sheetIndex = sheetIndex;
    }

    public List<?> getData() {
        return data;
    }

    public void setData(List<?> data) {
        this.data = data;
    }

    public Map<String, String> getExtendMap() {
        return extendMap;
    }

    public void setExtendMap(Map<String, String> extendMap) {
        this.extendMap = extendMap;
    }

    public Class getClazz() {
        return clazz;
    }

    public void setClazz(Class clazz) {
        this.clazz = clazz;
    }

    public boolean isWriteHeader() {
        return isWriteHeader;
    }

    public void setWriteHeader(boolean writeHeader) {
        isWriteHeader = writeHeader;
    }
} public class SimpleSheetWrapper {

    /**
     * 每个sheet的列表数据
     */
    private List<?> data;

    /**
     * 每个sheet的表头
     */
    private List<String> header;

    /**
     * 每个sheet的名字
     */
    private String sheetName;

    public SimpleSheetWrapper() {
    }

    public SimpleSheetWrapper(List<?> data, List<String> header, String sheetName) {
        this.data = data;
        this.header = header;
        this.sheetName = sheetName;
    }

    public SimpleSheetWrapper(List<?> data, List<String> header) {
        this.data = data;
        this.header = header;
    }

    public SimpleSheetWrapper(List<?> data, String sheetName) {
        this.data = data;
        this.sheetName = sheetName;
    }

    public SimpleSheetWrapper(List<?> data) {
        this.data = data;
    }

    public List<?> getData() {
        return data;
    }

    public void setData(List<?> data) {
        this.data = data;
    }

    public List<String> getHeader() {
        return header;
    }

    public void setHeader(List<String> header) {
        this.header = header;
    }

    public String getSheetName() {
        return sheetName;
    }

    public void setSheetName(String sheetName) {
        this.sheetName = sheetName;
    }
} public class Utils {

    /**
     * getter或setter枚举
     */
    public enum FieldAccessType {

        GETTER, SETTER
    }

    /**
     * <p>根据JAVA对象注解获取Excel表头信息</p>
     *
     * @param clz 类型
     * @return 表头信息
     */
    public static List<ExcelHeader> getHeaderList(Class<?> clz) throws Excel4JException {

        List<ExcelHeader> headers = new ArrayList<>();
        List<Field> fields = new ArrayList<>();
        for (Class<?> clazz = clz; clazz != Object.class; clazz = clazz.getSuperclass()) {
            fields.addAll(Arrays.asList(clazz.getDeclaredFields()));
        }
        for (Field field : fields) {
            // 是否使用ExcelField注解
            if (field.isAnnotationPresent(ExcelField.class)) {
                ExcelField er = field.getAnnotation(ExcelField.class);
                try {
                    headers.add(new ExcelHeader(
                            er.title(),
                            er.order(),
                            er.writeConverter().newInstance(),
                            er.readConverter().newInstance(),
                            field.getName(),
                            field.getType()
                    ));
                } catch (InstantiationException | IllegalAccessException e) {
                    throw new Excel4JException(e);
                }
            }
        }
        Collections.sort(headers);
        return headers;
    }

    /**
     * 获取excel列表头
     *
     * @param titleRow excel行
     * @param clz      类型
     * @return ExcelHeader集合
     * @throws Excel4JException 异常
     */
    public static Map<Integer, ExcelHeader> getHeaderMap(Row titleRow, Class<?> clz)
            throws Excel4JException {

        List<ExcelHeader> headers = getHeaderList(clz);
        Map<Integer, ExcelHeader> maps = new HashMap<>();
        for (Cell c : titleRow) {
            String title = c.getStringCellValue();
            for (ExcelHeader eh : headers) {
                if (eh.getTitle().equals(title.trim())) {
                    maps.put(c.getColumnIndex(), eh);
                    break;
                }
            }
        }
        return maps;
    }

    /**
     * 获取单元格内容
     *
     * @param c 单元格
     * @return 单元格内容
     */
    public static String getCellValue(Cell c) {
        String o;
        switch (c.getCellType()) {
            case BLANK:
                o = "";
                break;
            case BOOLEAN:
                o = String.valueOf(c.getBooleanCellValue());
                break;
            case FORMULA:
                o = calculationFormula(c);
                break;
            case NUMERIC:
                if (DateUtil.isCellDateFormatted(c)) {
                    o = DateUtils.date2Str(c.getDateCellValue());
                } else {
                    o = String.valueOf(c.getNumericCellValue());
                    o = matchDoneBigDecimal(o);
                    o = RegularUtils.converNumByReg(o);
                }
                break;
            case STRING:
                o = c.getStringCellValue();
                break;
            default:
                o = null;
                break;
        }
        return o;
    }

    /**
     * 字符串转对象
     *
     * @param strField 字符串
     * @param clazz    待转类型
     * @return 转换后数据
     */
    public static Object str2TargetClass(String strField, Class<?> clazz) {
        if (null == strField || "".equals(strField))
            return null;
        if ((Long.class == clazz) || (long.class == clazz)) {
            strField = matchDoneBigDecimal(strField);
            strField = RegularUtils.converNumByReg(strField);
            return Long.parseLong(strField);
        }
        if ((Integer.class == clazz) || (int.class == clazz)) {
            strField = matchDoneBigDecimal(strField);
            strField = RegularUtils.converNumByReg(strField);
            return Integer.parseInt(strField);
        }
        if ((Float.class == clazz) || (float.class == clazz)) {
            strField = matchDoneBigDecimal(strField);
            return Float.parseFloat(strField);
        }
        if ((Double.class == clazz) || (double.class == clazz)) {
            strField = matchDoneBigDecimal(strField);
            return Double.parseDouble(strField);
        }
        if ((Character.class == clazz) || (char.class == clazz)) {
            return strField.toCharArray()[0];
        }
        if ((Boolean.class == clazz) || (boolean.class == clazz)) {
            return Boolean.parseBoolean(strField);
        }
        if (Date.class == clazz) {
            return DateUtils.str2DateUnmatch2Null(strField);
        }
        return strField;
    }

    /**
     * 科学计数法数据转换
     *
     * @param bigDecimal 科学计数法
     * @return 数据字符串
     */
    private static String matchDoneBigDecimal(String bigDecimal) {
        // 对科学计数法进行处理
        boolean flg = Pattern.matches("^-?\\d+(\\.\\d+)?(E-?\\d+)?$", bigDecimal);
        if (flg) {
            BigDecimal bd = new BigDecimal(bigDecimal);
            bigDecimal = bd.toPlainString();
        }
        return bigDecimal;
    }

    /**
     * <p>根据java对象属性{@link Field}获取该属性的getter或setter方法名，
     * 另对{@link boolean}及{@link Boolean}做了行管处理</p>
     *
     * @param clazz      操作对象
     * @param fieldName  对象属性
     * @param methodType 方法类型，getter或setter枚举
     * @return getter或setter方法
     * @throws IntrospectionException 异常
     * @author Crab2Died
     */
    public static Method getterOrSetter(Class clazz, String fieldName, FieldAccessType methodType)
            throws IntrospectionException {

        if (null == fieldName || "".equals(fieldName))
            return null;

        BeanInfo beanInfo = Introspector.getBeanInfo(clazz);
        PropertyDescriptor[] props = beanInfo.getPropertyDescriptors();
        for (PropertyDescriptor prop : props) {
            if (fieldName.equals(prop.getName())) {
                if (FieldAccessType.SETTER == methodType) {
                    return prop.getWriteMethod();
                }
                if (FieldAccessType.GETTER == methodType) {
                    return prop.getReadMethod();
                }
            }
        }
        throw new IntrospectionException("Can not get the getter or setter method");
    }

    /**
     * <p>根据对象的属性名{@code fieldName}获取某个java的属性{@link java.lang.reflect.Field}</p>
     *
     * @param clazz     java对象的class属性
     * @param fieldName 属性名
     * @return {@link java.lang.reflect.Field}   java对象的属性
     * @author Crab2Died
     */
    private static Field matchClassField(Class clazz, String fieldName) {

        List<Field> fields = new ArrayList<>();
        for (; clazz != Object.class; clazz = clazz.getSuperclass()) {
            fields.addAll(Arrays.asList(clazz.getDeclaredFields()));
        }
        for (Field field : fields) {
            if (fieldName.equals(field.getName())) {
                return field;
            }
        }
        throw new IllegalArgumentException("[" + clazz.getName() + "] can`t found field with [" + fieldName + "]");
    }

    /**
     * 根据属性名与属性类型获取字段内容
     *
     * @param bean             对象
     * @param fieldName        字段名
     * @param writeConvertible 写入转换器
     * @return 对象指定字段内容
     * @throws Excel4JException 异常
     */
    public static String getProperty(Object bean, String fieldName, WriteConvertible writeConvertible)
            throws Excel4JException {

        if (bean == null || fieldName == null)
            throw new IllegalArgumentException("Operating bean or filed class must not be null");
        Method method;
        Object object;
        try {
            method = getterOrSetter(bean.getClass(), fieldName, FieldAccessType.GETTER);
            object = method.invoke(bean);
        } catch (IntrospectionException | IllegalAccessException | InvocationTargetException e) {
            throw new Excel4JException(e);
        }
        if (null != writeConvertible && writeConvertible.getClass() != DefaultConvertible.class) {
            // 写入转换器
            object = writeConvertible.execWrite(object);
        }
        return object == null ? "" : object.toString();
    }

    /**
     * 根据属性名与属性类型获取字段内容
     *
     * @param bean  对象
     * @param name  字段名
     * @param value 字段类型
     */
    public static void copyProperty(Object bean, String name, Object value)
            throws Excel4JException {

        if (null == name || null == value)
            return;
        Field field = matchClassField(bean.getClass(), name);
        if (null == field)
            return;
        Method method;
        try {
            method = getterOrSetter(bean.getClass(), name, FieldAccessType.SETTER);

            if (value.getClass() == field.getType()) {
                method.invoke(bean, value);
            } else {
                method.invoke(bean, str2TargetClass(value.toString(), field.getType()));
            }
        } catch (IntrospectionException | IllegalAccessException | InvocationTargetException e) {
            throw new Excel4JException(e);
        }

    }

    /**
     * 计算公式结果
     *
     * @param cell 单元格类型为公式的单元格
     * @return 返回单元格计算后的值 格式化成String
     * @author QingMings
     * Email:1821063757@qq.com
     * date 2018-01-13
     */
    public static String calculationFormula(Cell cell) {

        CellValue cellValue = cell.getSheet().getWorkbook().getCreationHelper()
                .createFormulaEvaluator().evaluate(cell);
        return cellValue.formatAsString();
    }
}
import com.github.crab2died.converter.DefaultConvertible;
import com.github.crab2died.exceptions.Excel4JException;
import com.github.crab2died.exceptions.Excel4jReadException;
import com.github.crab2died.handler.ExcelHeader;
import com.github.crab2died.handler.SheetTemplate;
import com.github.crab2died.handler.SheetTemplateHandler;
import com.github.crab2died.sheet.wrapper.MapSheetWrapper;
import com.github.crab2died.sheet.wrapper.NoTemplateSheetWrapper;
import com.github.crab2died.sheet.wrapper.NormalSheetWrapper;
import com.github.crab2died.sheet.wrapper.SimpleSheetWrapper;
import com.github.crab2died.utils.Utils;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVPrinter;
import org.apache.commons.csv.CSVRecord;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.*;
import java.lang.reflect.Array;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/*
 *
 *                  Copyright 2017 Crab2Died
 *                     All rights reserved.
 *
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Browse for more information ：
 * 1) https://gitee.com/Crab2Died/Excel4J
 * 2) https://github.com/Crab2died/Excel4J
 *
 */

package com.github.crab2died;



/**
 * Excel4J的主要操作工具类
 * <p>
 * 主要包含6大操作类型,并且每个类型都配有一个私有handler：<br>
 * 1.读取Excel操作基于注解映射,handler为{@link ExcelUtils#readExcel2ObjectsHandler}<br>
 * 2.读取Excel操作无映射,handler为{@link ExcelUtils#readExcel2ObjectsHandler}<br>
 * 3.基于模板、注解导出Excel,handler为{@link ExcelUtils#exportExcelByModuleHandler}<br>
 * 4.基于模板、注解导出Map数据,handler为{@link ExcelUtils#exportExcelByModuleHandler}<br>
 * 5.无模板基于注解导出,handler为{@link ExcelUtils#exportExcelByMapHandler}<br>
 * 6.无模板无注解导出,handler为{@link ExcelUtils#exportExcelBySimpleHandler}<br>
 * 7.读取CSV操作基于注解,handler为{@link ExcelUtils#readCSVByMapHandler}
 * 8.基于注解导出CSV, handler为{@link ExcelUtils#exportCSVByMapHandler}
 * <p>
 * 另外列举了部分常用的参数格式的方法(不同参数的排列组合实在是太多,没必要完全列出)
 * 如遇没有自己需要的参数类型的方法,可通过最全的方法来自行变换<br>
 * <p>
 * 详细用法请关注: https://gitee.com/Crab2Died/Excel4J
 *
 * @author Crab2Died
 */
public final class ExcelUtils {

    /**
     * 单例模式
     * 通过{@link ExcelUtils#getInstance()}获取对象实例
     */
    private static volatile ExcelUtils excelUtils;

    private ExcelUtils() {
    }

    /**
     * 双检锁保证单例
     */
    public static ExcelUtils getInstance() {
        if (null == excelUtils) {
            synchronized (ExcelUtils.class) {
                if (null == excelUtils) {
                    excelUtils = new ExcelUtils();
                }
            }
        }
        return excelUtils;
    }

    /*---------------------------------------1.读取Excel操作基于注解映射--------------------------------------------*/
    /*  一. 操作流程 ：                                                                                            */
    /*      1) 读取表头信息,与给出的Class类注解匹配                                                                  */
    /*      2) 读取表头下面的数据内容, 按行读取, 并映射至java对象                                                      */
    /*  二. 参数说明                                                                                               */
    /*      *) excelPath        =>      目标Excel路径                                                              */
    /*      *) InputStream      =>      目标Excel文件流                                                            */
    /*      *) clazz            =>      java映射对象                                                               */
    /*      *) offsetLine       =>      开始读取行坐标(默认0)                                                       */
    /*      *) limitLine        =>      最大读取行数(默认表尾)                                                      */
    /*      *) sheetIndex       =>      Sheet索引(默认0)                                                           */

    /**
     * 读取Excel操作基于注解映射成绑定的java对象
     *
     * @param excelPath  待导出Excel的路径
     * @param clazz      待绑定的类(绑定属性注解{@link com.github.crab2died.annotation.ExcelField})
     * @param offsetLine Excel表头行(默认是0)
     * @param limitLine  最大读取行数(默认表尾)
     * @param sheetIndex Sheet索引(默认0)
     * @param <T>        绑定的数据类
     * @return 返回转换为设置绑定的java对象集合
     * @throws Excel4JException 异常
     * @throws IOException      异常
     * @author Crab2Died
     */
    public <T> List<T> readExcel2Objects(String excelPath, Class<T> clazz, int offsetLine,
                                         int limitLine, int sheetIndex)
            throws Excel4JException, IOException {

        try (Workbook workbook = WorkbookFactory.create(new FileInputStream(new File(excelPath)))) {
            return readExcel2ObjectsHandler(workbook, clazz, offsetLine, limitLine, sheetIndex);
        }
    }

    /**
     * 读取Excel操作基于注解映射成绑定的java对象
     *
     * @param is         待导出Excel的数据流
     * @param clazz      待绑定的类(绑定属性注解{@link com.github.crab2died.annotation.ExcelField})
     * @param offsetLine Excel表头行(默认是0)
     * @param limitLine  最大读取行数(默认表尾)
     * @param sheetIndex Sheet索引(默认0)
     * @param <T>        绑定的数据类
     * @return 返回转换为设置绑定的java对象集合
     * @throws Excel4JException 异常
     * @throws IOException      异常
     * @author Crab2Died
     */
    public <T> List<T> readExcel2Objects(InputStream is, Class<T> clazz, int offsetLine,
                                         int limitLine, int sheetIndex)
            throws Excel4JException, IOException {

        try (Workbook workbook = WorkbookFactory.create(is)) {
            return readExcel2ObjectsHandler(workbook, clazz, offsetLine, limitLine, sheetIndex);
        }
    }

    /**
     * 读取Excel操作基于注解映射成绑定的java对象
     *
     * @param excelPath  待导出Excel的路径
     * @param clazz      待绑定的类(绑定属性注解{@link com.github.crab2died.annotation.ExcelField})
     * @param offsetLine Excel表头行(默认是0)
     * @param sheetIndex Sheet索引(默认0)
     * @param <T>        绑定的数据类
     * @return 返回转换为设置绑定的java对象集合
     * @throws Excel4JException 异常
     * @throws IOException      异常
     * @author Crab2Died
     */
    public <T> List<T> readExcel2Objects(String excelPath, Class<T> clazz, int offsetLine, int sheetIndex)
            throws Excel4JException, IOException {
        return readExcel2Objects(excelPath, clazz, offsetLine, Integer.MAX_VALUE, sheetIndex);
    }

    /**
     * 读取Excel操作基于注解映射成绑定的java对象
     *
     * @param excelPath  待导出Excel的路径
     * @param clazz      待绑定的类(绑定属性注解{@link com.github.crab2died.annotation.ExcelField})
     * @param sheetIndex Sheet索引(默认0)
     * @param <T>        绑定的数据类
     * @return 返回转换为设置绑定的java对象集合
     * @throws Excel4JException 异常
     * @throws IOException      异常
     * @author Crab2Died
     */
    public <T> List<T> readExcel2Objects(String excelPath, Class<T> clazz, int sheetIndex)
            throws Excel4JException, IOException {
        return readExcel2Objects(excelPath, clazz, 0, Integer.MAX_VALUE, sheetIndex);
    }

    /**
     * 读取Excel操作基于注解映射成绑定的java对象
     *
     * @param excelPath 待导出Excel的路径
     * @param clazz     待绑定的类(绑定属性注解{@link com.github.crab2died.annotation.ExcelField})
     * @param <T>       绑定的数据类
     * @return 返回转换为设置绑定的java对象集合
     * @throws Excel4JException 异常
     * @throws IOException      异常
     * @author Crab2Died
     */
    public <T> List<T> readExcel2Objects(String excelPath, Class<T> clazz)
            throws Excel4JException, IOException {
        return readExcel2Objects(excelPath, clazz, 0, Integer.MAX_VALUE, 0);
    }

    /**
     * 读取Excel操作基于注解映射成绑定的java对象
     *
     * @param is         待导出Excel的数据流
     * @param clazz      待绑定的类(绑定属性注解{@link com.github.crab2died.annotation.ExcelField})
     * @param sheetIndex Sheet索引(默认0)
     * @param <T>        绑定的数据类
     * @return 返回转换为设置绑定的java对象集合
     * @throws Excel4JException 异常
     * @throws IOException      异常
     * @author Crab2Died
     */
    public <T> List<T> readExcel2Objects(InputStream is, Class<T> clazz, int sheetIndex)
            throws Excel4JException, IOException {
        return readExcel2Objects(is, clazz, 0, Integer.MAX_VALUE, sheetIndex);
    }

    /**
     * 读取Excel操作基于注解映射成绑定的java对象
     *
     * @param is    待导出Excel的数据流
     * @param clazz 待绑定的类(绑定属性注解{@link com.github.crab2died.annotation.ExcelField})
     * @param <T>   绑定的数据类
     * @return 返回转换为设置绑定的java对象集合
     * @throws Excel4JException 异常
     * @throws IOException      异常
     * @author Crab2Died
     */
    public <T> List<T> readExcel2Objects(InputStream is, Class<T> clazz)
            throws Excel4JException, IOException {
        return readExcel2Objects(is, clazz, 0, Integer.MAX_VALUE, 0);
    }

    private <T> List<T> readExcel2ObjectsHandler(Workbook workbook, Class<T> clazz, int offsetLine,
                                                 int limitLine, int sheetIndex) throws Excel4JException {

        Sheet sheet = workbook.getSheetAt(sheetIndex);
        Row row = sheet.getRow(offsetLine);
        List<T> list = new ArrayList<>();
        Map<Integer, ExcelHeader> maps = Utils.getHeaderMap(row, clazz);