[
  {
    "path": ".gitignore",
    "content": "#eclipse files\n.classpath\n.project\n\n/bin/\n/target/\n/.settings/\n\n#idea files\ntarget/\n*.iml\n*.ipr\n*.iws\n.idea/"
  },
  {
    "path": "README.md",
    "content": "# ExcelKit\n> 简单、好用且轻量级的海量Excel文件导入导出解决方案。\n\n# 注意\n## 停止维护，建议使用EasyExcel：https://github.com/alibaba/easyexcel\n\n## POM.xml\n```xml\n<dependency>\n    <groupId>com.wuwenze</groupId>\n    <artifactId>ExcelKit</artifactId>\n    <version>2.0.72</version>\n</dependency>\n```\n\n## 示例\n### 1. ExcelMapping (配置Excel与实体之间的映射关系)\n\n> 现支持两种配置方式: 注解 或者 XML\n```java\n@Excel(\"用户信息\")\npublic class User {\n\n  @ExcelField(value = \"编号\", width = 30)\n  private Integer userId;\n\n  @ExcelField(//\n      value = \"用户名\",//\n      required = true,//\n      validator = UsernameValidator.class,//\n      comment = \"请填写用户名，最大长度为12，且不能重复\"\n  )\n  private String username;\n\n  @ExcelField(value = \"密码\", required = true, maxLength = 32)\n  private String password;\n\n  @ExcelField(value = \"邮箱\", validator = UserEmailValidator.class)\n  private String email;\n\n  @ExcelField(//\n      value = \"性别\",//\n      readConverterExp = \"未知=0,男=1,女=2\",//\n      writeConverterExp = \"0=未知,1=男,2=女\",//\n      options = SexOptions.class//\n  )\n  private Integer sex;\n\n  @ExcelField(//\n      value = \"用户组\",//\n      name = \"userGroup.name\",//\n      options = UserGroupNameOptions.class\n  )\n  private UserGroup userGroup;\n\n  @ExcelField(value = \"创建时间\", dateFormat = \"yyyy/MM/dd HH:mm:ss\")\n  private Date createAt;\n\n  @ExcelField(//\n      value = \"自定义字段\",//\n      maxLength = 80,//\n      comment = \"可以乱填，但是长度不能超过80，导入时最终会转换为数字\",//\n      writeConverter = CustomizeFieldWriteConverter.class,// 写文件时，将数字转字符串\n      readConverter = CustomizeFieldReadConverter.class// 读文件时，将字符串转数字\n  )\n  private Integer customizeField;\n  \n  // Getter and Setter ..\n}\n```\n\n> XML 配置方式, 必须需将 xml 文件放置在`classpath:excel-mapping/{entityClassName}.xml`\n\n```java\npublic class User2 {\n\n  private Integer userId;\n  private String username;\n  private String password;\n  private String email;\n  private Integer sex;\n  private UserGroup userGroup;\n  private Date createAt;\n  private Integer customizeField;\n  \n  // Getter and Setter ..\n}\n```\n\n> classpath:excel-mapping/com.wuwenze.entity.User2.xml\n```xml\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<excel-mapping name=\"用户信息\">\n  <property name=\"userId\" column=\"编号\" width=\"30\"/>\n  <property\n    name=\"username\"\n    column=\"用户名\"\n    required=\"true\"\n    validator=\"com.wuwenze.validator.UsernameValidator\"\n    comment=\"请填写用户名，最大长度为12，且不能重复\"\n  />\n  <property name=\"password\" column=\"密码\" required=\"true\" maxLength=\"32\"/>\n  <property name=\"email\" column=\"邮箱\" validator=\"com.wuwenze.validator.UserEmailValidator\"/>\n  <property\n    name=\"sex\"\n    column=\"性别\"\n    readConverterExp=\"未知=0,男=1,女=2\"\n    writeConverterExp=\"0=未知,1=男,2=女\"\n    options=\"com.wuwenze.options.SexOptions\"\n  />\n  <property name=\"userGroup.name\" column=\"用户组\" options=\"com.wuwenze.options.UserGroupNameOptions\"/>\n  <property name=\"createAt\" column=\"创建时间\" dateFormat=\"yyyy/MM/dd HH:mm:ss\"/>\n  <property\n    name=\"customizeField\"\n    column=\"自定义字段\"\n    maxLength=\"80\"\n    comment=\"可以乱填，但是长度不能超过80，导入时最终会转换为数字\"\n    writeConverter=\"com.wuwenze.converter.CustomizeFieldWriteConverter\"\n    readConverter=\"com.wuwenze.converter.CustomizeFieldReadConverter\"\n  />\n</excel-mapping>\n```\n以上两种方式2选一即可, ExcelKit 会`优先装载 XML 配置文件`.\n\n### 2. 实现相关的转换器\n> 通过实现`com.wuwenze.poi.config.Options`自定义导入模板的下拉框数据源。\n\n```java\npublic class UserGroupNameOptions implements Options {\n\n  @Override\n  public String[] get() {\n    return new String[]{\"管理组\", \"普通会员组\", \"游客\"};\n  }\n}\n```\n\n> 通过实现`com.wuwenze.poi.validator.Validator`自定义单元格导入时的验证规则。\n```java\npublic class UsernameValidator implements Validator {\n  final List<String> ERROR_USERNAME_LIST = Lists.newArrayList(\n      \"admin\", \"root\", \"master\", \"administrator\", \"sb\"\n  );\n\n  @Override\n  public String valid(Object o) {\n    String username = (String) o;\n    if (username.length() > 12) {\n      return \"用户名不能超过12个字符。\";\n    }\n\n    if (ERROR_USERNAME_LIST.contains(username)) {\n      return \"用户名非法，不允许使用。\";\n    }\n    return null;\n  }\n}\n```\n\n> 实现`com.wuwenze.poi.convert.WriteConverter`以及`com.wuwenze.poi.convert.ReadConverter`单元格读写转换器。\n```java\npublic class CustomizeFieldWriteConverter implements WriteConverter {\n\n  /**\n   * 写文件时，将值进行转换（此处示例为将数值拼接为指定格式的字符串）\n   */\n  @Override\n  public String convert(Object o) throws ExcelKitWriteConverterException {\n    return (o + \"_convertedValue\");\n  }\n}\n\npublic class CustomizeFieldReadConverter implements ReadConverter {\n\n  /**\n   * 读取单元格时，将值进行转换（此处示例为计算单元格字符串char的总和）\n   */\n  @Override\n  public Object convert(Object o) throws ExcelKitReadConverterException {\n    String value = (String) o;\n\n    int convertedValue = 0;\n    for (char c : value.toCharArray()) {\n      convertedValue += Integer.valueOf(c);\n    }\n    return convertedValue;\n  }\n}\n```\n\n### 3. 一行代码构建 Excel 导入模板\n> 使用 ExcelKit 提供的API 构建导入模板, 会根据配置生成批注, 下拉框等\n``` java\n// 生成导入模板（含3条示例数据）\n@RequestMapping(value = \"/downTemplate\", method = RequestMethod.GET)\npublic void downTemplate(HttpServletResponse response) {\n  List<User> userList = DbUtil.getUserList(3);\n  ExcelKit.$Export(User.class, response).downXlsx(userList, true);\n}\n```\n![](https://cdn.nlark.com/yuque/0/2019/png/243237/1550539800515-e9714f25-e415-4e70-a4b9-2b5a229dfce0.png)  \n![](https://cdn.nlark.com/yuque/0/2019/png/243237/1550539833934-6b7b2ca8-c7a0-4872-a1a9-722fc5c403d1.png)\n\n\n### 4. 执行文件导入\n> 使用边读边处理的方式, 无需担心内存溢出, 也不用理会 Excel 文件到底有多大.\n\n``` java\n@RequestMapping(value = \"/importUser\", method = RequestMethod.POST)\npublic ResponseEntity<?> importUser(@RequestParam MultipartFile file)\n    throws IOException {\n  long beginMillis = System.currentTimeMillis();\n\n  List<User> successList = Lists.newArrayList();\n  List<Map<String, Object>> errorList = Lists.newArrayList();\n\n  ExcelKit.$Import(User.class)\n      .readXlsx(file.getInputStream(), new ExcelReadHandler<User>() {\n\n        @Override\n        public void onSuccess(int sheetIndex, int rowIndex, User entity) {\n          successList.add(entity); // 单行读取成功，加入入库队列。\n        }\n\n        @Override\n        public void onError(int sheetIndex, int rowIndex,\n            List<ExcelErrorField> errorFields) {\n          // 读取数据失败，记录了当前行所有失败的数据\n          errorList.add(MapUtil.newHashMap(//\n              \"sheetIndex\", sheetIndex,//\n              \"rowIndex\", rowIndex,//\n              \"errorFields\", errorFields//\n          ));\n        }\n      });\n\n  // TODO: 执行successList的入库操作。\n\n  return ResponseEntity.ok(MapUtil.newHashMap(\n      \"data\", successList,\n      \"haveError\", !CollectionUtil.isEmpty(errorList),\n      \"error\", errorList,\n      \"timeConsuming\", (System.currentTimeMillis() - beginMillis) / 1000L\n  ));\n}\n```\n\n> 全部导入成功示例：\n\n![](https://cdn.nlark.com/yuque/0/2019/png/243237/1550539857806-7dd5b511-4cfe-43f7-8d82-f76bd831e7af.png)\n\n> 部分导入失败示例（包含错误信息）：\n\n![](https://cdn.nlark.com/yuque/0/2019/png/243237/1550539878743-a0cc24a2-1f30-4ccc-8d14-225a3bad30f5.png)\n\n\n### 5. 一行代码执行 Excel 批量导出.\n> 基于 Apache POI SXSSF 系列API实现导出, 大幅优化导出性能.\n\n``` java\n@RequestMapping(value = \"/downXlsx\", method = RequestMethod.GET)\npublic void downXlsx(HttpServletResponse response) {\n  long beginMillis = System.currentTimeMillis();\n  List<User> userList = DbUtil.getUserList(100000);// 生成10w条测试数据\n  ExcelKit.$Export(User.class, response).downXlsx(userList, false);\n  log.info(\"#ExcelKit.$Export success, size={},timeConsuming={}s\",//\n      userList.size(), (System.currentTimeMillis() - beginMillis) / 1000L);\n}\n```\n![](https://cdn.nlark.com/yuque/0/2019/png/243237/1550539922063-7681b3df-6ccf-4f42-939b-269c84b8f8bc.png)  \n需要注意的是，虽然ExcelKit针对导出做了大量优化，但导出数据也需要量力而行。  \n![](https://cdn.nlark.com/yuque/0/2019/png/243237/1550539967120-9eff43bf-d225-4268-8685-2b441f7024d5.png)\n"
  },
  {
    "path": "pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n  xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n  <modelVersion>4.0.0</modelVersion>\n\n  <groupId>com.wuwenze</groupId>\n  <artifactId>ExcelKit</artifactId>\n  <version>2.0.72</version>\n  <packaging>jar</packaging>\n  <name>ExcelKit</name>\n  <url>http://gitee.com/wuwenze/ExcelKit</url>\n  <description>Excel导入导出工具（简单、好用且轻量级的海量Excel文件导入导出解决方案.）</description>\n  <developers>\n    <developer>\n      <name>wuwenze</name>\n      <url>https://wuwenze.com</url>\n      <email>wenzewoo@gmail.com</email>\n    </developer>\n  </developers>\n  <licenses>\n    <license>\n      <name>The Apache Software License, Version 2.0</name>\n      <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>\n    </license>\n  </licenses>\n  <scm>\n    <connection>scm:git:git@gitee.com:wuwenze/ExcelKit.git</connection>\n    <developerConnection>scm:git:git@gitee.com:wuwenze/ExcelKit.git</developerConnection>\n    <url>git@gitee.com:wuwenze/ExcelKit.git</url>\n  </scm>\n\n  <properties>\n    <encoding>UTF-8</encoding>\n    <jdk-version>1.6</jdk-version>\n    <poi-version>3.17</poi-version>\n    <dom4j-version>1.6.1</dom4j-version>\n    <jaxen-version>1.1.6</jaxen-version>\n    <xerces-version>2.11.0</xerces-version>\n    <guava-version>18.0</guava-version>\n  </properties>\n\n  <dependencies>\n    <dependency>\n      <groupId>org.apache.poi</groupId>\n      <artifactId>poi-ooxml</artifactId>\n      <version>${poi-version}</version>\n    </dependency>\n    <dependency>\n      <groupId>org.apache.poi</groupId>\n      <artifactId>poi-ooxml-schemas</artifactId>\n      <version>${poi-version}</version>\n    </dependency>\n    <dependency>\n      <groupId>dom4j</groupId>\n      <artifactId>dom4j</artifactId>\n      <version>${dom4j-version}</version>\n    </dependency>\n    <dependency>\n      <groupId>jaxen</groupId>\n      <artifactId>jaxen</artifactId>\n      <version>${jaxen-version}</version>\n    </dependency>\n    <dependency>\n      <groupId>xerces</groupId>\n      <artifactId>xercesImpl</artifactId>\n      <version>${xerces-version}</version>\n    </dependency>\n    <dependency>\n      <groupId>xml-apis</groupId>\n      <artifactId>xml-apis</artifactId>\n      <version>2.0.2</version>\n    </dependency>\n    <dependency>\n      <groupId>com.google.guava</groupId>\n      <artifactId>guava</artifactId>\n      <version>18.0</version>\n    </dependency>\n    <dependency>\n      <groupId>org.projectlombok</groupId>\n      <artifactId>lombok</artifactId>\n      <version>1.16.10</version>\n      <scope>provided</scope>\n    </dependency>\n    <dependency>\n      <groupId>javax.servlet</groupId>\n      <artifactId>servlet-api</artifactId>\n      <version>2.5</version>\n      <scope>provided</scope>\n    </dependency>\n    <dependency>\n      <groupId>commons-beanutils</groupId>\n      <artifactId>commons-beanutils</artifactId>\n      <version>1.9.3</version>\n    </dependency>\n    <dependency>\n      <groupId>junit</groupId>\n      <artifactId>junit</artifactId>\n      <version>4.12</version>\n      <scope>test</scope>\n    </dependency>\n  </dependencies>\n\n  <build>\n    <plugins>\n      <plugin>\n        <groupId>org.apache.maven.plugins</groupId>\n        <artifactId>maven-compiler-plugin</artifactId>\n        <configuration>\n          <target>${jdk-version}</target>\n          <source>${jdk-version}</source>\n          <encoding>${encoding}</encoding>\n        </configuration>\n      </plugin>\n    </plugins>\n  </build>\n\n  <profiles>\n    <profile>\n      <id>jdk-profile</id>\n      <activation>\n        <activeByDefault>true</activeByDefault>\n        <jdk>${jdk-version}</jdk>\n      </activation>\n      <properties>\n        <maven.compiler.source>${jdk-version}</maven.compiler.source>\n        <maven.compiler.target>${jdk-version}</maven.compiler.target>\n        <maven.compiler.compilerVersion>${jdk-version}</maven.compiler.compilerVersion>\n      </properties>\n    </profile>\n    <profile>\n      <id>release</id>\n      <build>\n        <plugins>\n          <plugin>\n            <groupId>org.apache.maven.plugins</groupId>\n            <artifactId>maven-source-plugin</artifactId>\n            <version>2.2.1</version>\n            <executions>\n              <execution>\n                <phase>package</phase>\n                <goals>\n                  <goal>jar-no-fork</goal>\n                </goals>\n              </execution>\n            </executions>\n          </plugin>\n          <plugin>\n            <groupId>org.apache.maven.plugins</groupId>\n            <artifactId>maven-javadoc-plugin</artifactId>\n            <version>2.9.1</version>\n            <executions>\n              <execution>\n                <phase>package</phase>\n                <goals>\n                  <goal>jar</goal>\n                </goals>\n              </execution>\n            </executions>\n          </plugin>\n          <plugin>\n            <groupId>org.apache.maven.plugins</groupId>\n            <artifactId>maven-gpg-plugin</artifactId>\n            <version>1.5</version>\n            <executions>\n              <execution>\n                <phase>verify</phase>\n                <goals>\n                  <goal>sign</goal>\n                </goals>\n              </execution>\n            </executions>\n          </plugin>\n        </plugins>\n      </build>\n      <distributionManagement>\n        <snapshotRepository>\n          <id>sonatype</id>\n          <url>https://oss.sonatype.org/content/repositories/snapshots/</url>\n        </snapshotRepository>\n        <repository>\n          <id>sonatype</id>\n          <url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>\n        </repository>\n      </distributionManagement>\n    </profile>\n  </profiles>\n\n</project>\n"
  },
  {
    "path": "src/main/java/com/wuwenze/poi/ExcelKit.java",
    "content": "/*\r\n * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com).\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *   http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage com.wuwenze.poi;\r\n\r\nimport com.wuwenze.poi.exception.ExcelKitRuntimeException;\r\nimport com.wuwenze.poi.factory.ExcelMappingFactory;\r\nimport com.wuwenze.poi.handler.ExcelReadHandler;\r\nimport com.wuwenze.poi.pojo.ExcelMapping;\r\nimport com.wuwenze.poi.util.Const;\r\nimport com.wuwenze.poi.util.POIUtil;\r\nimport com.wuwenze.poi.xlsx.ExcelXlsxReader;\r\nimport com.wuwenze.poi.xlsx.ExcelXlsxWriter;\r\nimport java.io.File;\r\nimport java.io.FileInputStream;\r\nimport java.io.InputStream;\r\nimport java.io.OutputStream;\r\nimport java.net.URLEncoder;\r\nimport java.util.List;\r\nimport javax.servlet.http.HttpServletResponse;\r\nimport lombok.AccessLevel;\r\nimport lombok.NoArgsConstructor;\r\nimport org.apache.poi.xssf.streaming.SXSSFWorkbook;\r\n\r\n/**\r\n * @author wuwenze\r\n */\r\n@NoArgsConstructor(access = AccessLevel.PROTECTED)\r\npublic class ExcelKit {\r\n\r\n  private Class<?> mClass = null;\r\n  private HttpServletResponse mResponse = null;\r\n  private OutputStream mOutputStream = null;\r\n  private Integer mMaxSheetRecords = 50000;\r\n  private String mCurrentOptionMode = ExcelKit.MODE_EXPORT;\r\n  private final static String MODE_EXPORT = \"$MODE_EXPORT$\";\r\n  private final static String MODE_BUILD = \"$MODE_BUILD$\";\r\n  private final static String MODE_IMPORT = \"$MODE_IMPORT$\";\r\n\r\n  /**\r\n   * 使用此构造器来执行浏览器导出\r\n   *\r\n   * @param clazz 导出实体对象\r\n   * @param response 原生 response 对象, 用于响应浏览器下载\r\n   * @return ExcelKit obj.\r\n   * @see ExcelKit#downXlsx(List, boolean)\r\n   */\r\n  public static ExcelKit $Export(Class<?> clazz, HttpServletResponse response) {\r\n    return new ExcelKit(clazz, response);\r\n  }\r\n\r\n  public void downXlsx(List<?> data, boolean isTemplate) {\r\n    if (!mCurrentOptionMode.equals(ExcelKit.MODE_EXPORT)) {\r\n      throw new ExcelKitRuntimeException(\r\n          \"请使用com.wuwenze.poi.ExcelKit.$Export(Class<?> clazz, HttpServletResponse response)构造器初始化参数.\");\r\n    }\r\n    try {\r\n      ExcelMapping excelMapping = ExcelMappingFactory.get(mClass);\r\n      ExcelXlsxWriter excelXlsxWriter = new ExcelXlsxWriter(excelMapping,\r\n          mMaxSheetRecords);\r\n      SXSSFWorkbook workbook = excelXlsxWriter.generateXlsxWorkbook(data, isTemplate);\r\n      String fileName = isTemplate ? (excelMapping.getName() + \"-导入模板.xlsx\")\r\n          : (excelMapping.getName() + \"-导出结果.xlsx\");\r\n      POIUtil.download(workbook, mResponse, URLEncoder.encode(fileName, Const.ENCODING));\r\n    } catch (Throwable e) {\r\n      throw new ExcelKitRuntimeException(\"downXlsx error\", e);\r\n    }\r\n  }\r\n\r\n\r\n  /**\r\n   * 使用此构造器来执行构建文件流.\r\n   *\r\n   * @param clazz 导出实体对象\r\n   * @param outputStream 输出流\r\n   * @return ExcelKit obj.\r\n   * @see ExcelKit#writeXlsx(List, boolean)\r\n   */\r\n  public static ExcelKit $Builder(Class<?> clazz, OutputStream outputStream) {\r\n    return new ExcelKit(clazz, outputStream);\r\n  }\r\n\r\n  public void writeXlsx(List<?> data, boolean isTemplate) {\r\n    if (!mCurrentOptionMode.equals(ExcelKit.MODE_BUILD)) {\r\n      throw new ExcelKitRuntimeException(\r\n          \"请使用com.wuwenze.poi.ExcelKit.$Builder(Class<?> clazz, OutputStream outputStream)构造器初始化参数.\");\r\n    }\r\n    ExcelMapping excelMapping = ExcelMappingFactory.get(mClass);\r\n    ExcelXlsxWriter excelXlsxWriter = new ExcelXlsxWriter(excelMapping,\r\n        mMaxSheetRecords);\r\n    SXSSFWorkbook workbook = excelXlsxWriter.generateXlsxWorkbook(data, isTemplate);\r\n    POIUtil.write(workbook, mOutputStream);\r\n  }\r\n\r\n  /**\r\n   * 使用此构造器来执行Excel文件导入.\r\n   *\r\n   * @param clazz 导出实体对象\r\n   * @return ExcelKit obj.\r\n   * @see ExcelKit#readXlsx(File, Integer, ExcelReadHandler)\r\n   * @see ExcelKit#readXlsx(InputStream, Integer, ExcelReadHandler)\r\n   * @see ExcelKit#readXlsx(File, ExcelReadHandler)\r\n   * @see ExcelKit#readXlsx(InputStream, ExcelReadHandler)\r\n   */\r\n  public static ExcelKit $Import(Class<?> clazz) {\r\n    return new ExcelKit(clazz);\r\n  }\r\n\r\n\r\n  public void readXlsx(File excelFile, ExcelReadHandler<?> excelReadHandler) {\r\n    readXlsx(excelFile, -1, excelReadHandler);\r\n  }\r\n\r\n  public void readXlsx(File excelFile, Integer sheetIndex,\r\n      ExcelReadHandler<?> excelReadHandler) {\r\n    try {\r\n      InputStream inputStream = new FileInputStream(excelFile);\r\n      readXlsx(inputStream, sheetIndex, excelReadHandler);\r\n    } catch (Throwable e) {\r\n      throw new ExcelKitRuntimeException(\"readXlsx error\", e);\r\n    }\r\n  }\r\n\r\n  public void readXlsx(InputStream inputStream, ExcelReadHandler<?> excelReadHandler) {\r\n    readXlsx(inputStream, -1, excelReadHandler);\r\n  }\r\n\r\n  public void readXlsx(InputStream inputStream, Integer sheetIndex,\r\n      ExcelReadHandler<?> excelReadHandler) {\r\n    if (!mCurrentOptionMode.equals(ExcelKit.MODE_IMPORT)) {\r\n      throw new ExcelKitRuntimeException(\r\n          \"请使用com.wuwenze.poi.ExcelKit.$Import(Class<?> clazz)构造器初始化参数.\");\r\n    }\r\n    ExcelMapping excelMapping = ExcelMappingFactory.get(mClass);\r\n    ExcelXlsxReader excelXlsxReader = new ExcelXlsxReader(mClass, excelMapping,\r\n        excelReadHandler);\r\n    if (sheetIndex >= 0) {\r\n      excelXlsxReader.process(inputStream, sheetIndex);\r\n      return;\r\n    }\r\n    excelXlsxReader.process(inputStream);\r\n  }\r\n\r\n  public ExcelKit setMaxSheetRecords(Integer mMaxSheetRecords) {\r\n    this.mMaxSheetRecords = mMaxSheetRecords;\r\n    return this;\r\n  }\r\n\r\n  protected ExcelKit(Class<?> clazz) {\r\n    this(clazz, null, null);\r\n    mCurrentOptionMode = ExcelKit.MODE_IMPORT;\r\n  }\r\n\r\n  protected ExcelKit(Class<?> clazz, OutputStream outputStream) {\r\n    this(clazz, outputStream, null);\r\n    mCurrentOptionMode = ExcelKit.MODE_BUILD;\r\n  }\r\n\r\n  protected ExcelKit(Class<?> clazz, HttpServletResponse response) {\r\n    this(clazz, null, response);\r\n    mCurrentOptionMode = ExcelKit.MODE_EXPORT;\r\n  }\r\n\r\n  protected ExcelKit(\r\n      Class<?> clazz, OutputStream outputStream, HttpServletResponse response) {\r\n    mClass = clazz;\r\n    mOutputStream = outputStream;\r\n    mResponse = response;\r\n  }\r\n}\r\n"
  },
  {
    "path": "src/main/java/com/wuwenze/poi/annotation/Excel.java",
    "content": "/*\r\n * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com).\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *   http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage com.wuwenze.poi.annotation;\r\n\r\nimport java.lang.annotation.ElementType;\r\nimport java.lang.annotation.Retention;\r\nimport java.lang.annotation.RetentionPolicy;\r\nimport java.lang.annotation.Target;\r\n\r\n/**\r\n * @author wuwenze\r\n */\r\n@Retention(RetentionPolicy.RUNTIME)\r\n@Target(ElementType.TYPE)\r\npublic @interface Excel {\r\n\r\n  String value() default \"\";\r\n}\r\n"
  },
  {
    "path": "src/main/java/com/wuwenze/poi/annotation/ExcelField.java",
    "content": "/*\r\n * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com).\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *   http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage com.wuwenze.poi.annotation;\r\n\r\nimport com.wuwenze.poi.config.Options;\r\nimport com.wuwenze.poi.convert.ReadConverter;\r\nimport com.wuwenze.poi.convert.WriteConverter;\r\nimport com.wuwenze.poi.validator.Validator;\r\nimport java.lang.annotation.ElementType;\r\nimport java.lang.annotation.Retention;\r\nimport java.lang.annotation.RetentionPolicy;\r\nimport java.lang.annotation.Target;\r\n\r\n/**\r\n * @author wuwenze\r\n */\r\n@Retention(RetentionPolicy.RUNTIME)\r\n@Target(ElementType.FIELD)\r\npublic @interface ExcelField {\r\n\r\n  /**\r\n   * @return 单元格名称(如 : id字段显示为 ' 编号 ') 默认为字段名\r\n   */\r\n  String value() default \"\";\r\n\r\n  /**\r\n   * 属性名, 仅在复杂数据类型时配置.\r\n   * <pre>\r\n   *   (At)ExcelField(name=\"user.name\");\r\n   *   private User user;\r\n   * </pre>\r\n   *\r\n   * @return 属性名\r\n   */\r\n  String name() default \"\";\r\n\r\n  /**\r\n   * @return 单元格宽度[仅限表头] 默认-1(自动计算列宽)\r\n   */\r\n  short width() default -1;\r\n\r\n  /**\r\n   * @return 是否必填\r\n   */\r\n  boolean required() default false;\r\n\r\n  /**\r\n   * @return 批注信息, 生成模板时生效\r\n   */\r\n  String comment() default \"\";\r\n\r\n  /**\r\n   * @return 最大长度, 读取时生效, 默认不限制\r\n   */\r\n  int maxLength() default -1;\r\n\r\n  /**\r\n   * 日期格式, 如: yyyy/MM/dd\r\n   *\r\n   * @return 日期格式\r\n   */\r\n  String dateFormat() default \"\";\r\n\r\n  /**\r\n   * @return 下拉框数据源, 生成模板和验证数据时生效\r\n   */\r\n  Class<? extends Options> options() default Void.class;\r\n\r\n  /**\r\n   * 写入内容转换表达式 (如: 1=男,2=女), 与 writeConverter 二选一(优先级0)\r\n   *\r\n   * @return 写入内容转换表达式\r\n   * @see ExcelField#writeConverter()\r\n   */\r\n  String writeConverterExp() default \"\";\r\n\r\n  /**\r\n   * 写入内容转换器, 与 writeConverterExp 二选一(优先级1)\r\n   *\r\n   * @return 写入内容转换器\r\n   * @see ExcelField#writeConverterExp()\r\n   */\r\n  Class<? extends WriteConverter> writeConverter() default Void.class;\r\n\r\n  /**\r\n   * 读取内容转表达式 (如: 男=1,女=2), 与 readConverter 二选一(优先级0)\r\n   *\r\n   * @return 读取内容转表达式\r\n   * @see ExcelField#readConverter()\r\n   */\r\n  String readConverterExp() default \"\";\r\n\r\n  /**\r\n   * 读取内容转换器, 与 readConverterExp 二选一(优先级1)\r\n   *\r\n   * @return 读取内容转换器\r\n   * @see ExcelField#readConverterExp()\r\n   */\r\n  Class<? extends ReadConverter> readConverter() default Void.class;\r\n\r\n  /**\r\n   * 正则表达式, 读取时生效, 与 validator 二选一(优先级0)\r\n   *\r\n   * @return 正则表达式\r\n   * @see ExcelField#validator()\r\n   */\r\n  String regularExp() default \"\";\r\n\r\n  /**\r\n   * 正则表达式验证失败时的错误消息, regularExp 配置后生效\r\n   *\r\n   * @return 正则表达式验证失败时的错误消息\r\n   * @see ExcelField#regularExp()\r\n   */\r\n  String regularExpMessage() default \"\";\r\n\r\n  /**\r\n   * 自定义验证器, 读取时生效, 与 regularExp 二选一(优先级1)\r\n   *\r\n   * @return 自定义验证器\r\n   * @see ExcelField#regularExp()\r\n   */\r\n  Class<? extends Validator> validator() default Void.class;\r\n\r\n  class Void implements Options, ReadConverter, WriteConverter, Validator {\r\n\r\n    @Override\r\n    public String[] get() {\r\n      return new String[0];\r\n    }\r\n\r\n    @Override\r\n    public String convert(Object value) {\r\n      return null;\r\n    }\r\n\r\n    @Override\r\n    public String valid(Object value) {\r\n      return null;\r\n    }\r\n  }\r\n}\r\n"
  },
  {
    "path": "src/main/java/com/wuwenze/poi/config/Options.java",
    "content": "/*\r\n * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com).\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *   http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage com.wuwenze.poi.config;\r\n\r\n/**\r\n * @author wuwenze\r\n */\r\npublic interface Options {\r\n\r\n  /**\r\n   * 指定excel单元格的下拉框数据源, 用于规范生成Excel模板的数据校验\r\n   *\r\n   * @return [\"option1\", \"option2\"]\r\n   */\r\n  String[] get();\r\n}\r\n"
  },
  {
    "path": "src/main/java/com/wuwenze/poi/convert/ReadConverter.java",
    "content": "/*\r\n * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com).\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *   http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage com.wuwenze.poi.convert;\r\n\r\nimport com.wuwenze.poi.exception.ExcelKitReadConverterException;\r\n\r\n/**\r\n * @author wuwenze\r\n */\r\npublic interface ReadConverter {\r\n\r\n  /**\r\n   * 将value转换成指定的值, 读取时映射到实体中\r\n   *\r\n   * @param value 当前单元格的值\r\n   * @return 转换后的值\r\n   */\r\n  Object convert(Object value) throws ExcelKitReadConverterException;\r\n}\r\n"
  },
  {
    "path": "src/main/java/com/wuwenze/poi/convert/WriteConverter.java",
    "content": "/*\r\n * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com).\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *   http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage com.wuwenze.poi.convert;\r\n\r\nimport com.wuwenze.poi.exception.ExcelKitWriteConverterException;\r\n\r\n/**\r\n * @author wuwenze\r\n */\r\npublic interface WriteConverter {\r\n\r\n  /**\r\n   * 将value转换成指定的值, 用于写入excel表格中\r\n   *\r\n   * @param value 当前单元格的值\r\n   * @return 转换后的值\r\n   */\r\n  String convert(Object value) throws ExcelKitWriteConverterException;\r\n}\r\n"
  },
  {
    "path": "src/main/java/com/wuwenze/poi/exception/ExcelKitAnnotationAnalyzeException.java",
    "content": "/*\r\n * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com).\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *   http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage com.wuwenze.poi.exception;\r\n\r\n/**\r\n * @author wuwenze\r\n */\r\npublic class ExcelKitAnnotationAnalyzeException extends ExcelKitRuntimeException {\r\n\r\n  private static final long serialVersionUID = -7412071094510468644L;\r\n\r\n  public ExcelKitAnnotationAnalyzeException(String message) {\r\n    super(message);\r\n  }\r\n}\r\n"
  },
  {
    "path": "src/main/java/com/wuwenze/poi/exception/ExcelKitConfigAnalyzeFailureException.java",
    "content": "/*\n * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com).\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.wuwenze.poi.exception;\n\n/**\n * @author wuwenze\n */\npublic class ExcelKitConfigAnalyzeFailureException extends ExcelKitRuntimeException {\n\n  private static final long serialVersionUID = -6037535579660494996L;\n\n  public ExcelKitConfigAnalyzeFailureException(String message) {\n    super(message);\n  }\n\n  public ExcelKitConfigAnalyzeFailureException(Throwable cause) {\n    super(cause);\n  }\n}\n"
  },
  {
    "path": "src/main/java/com/wuwenze/poi/exception/ExcelKitConfigFileNotFoundException.java",
    "content": "/*\r\n * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com).\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *   http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage com.wuwenze.poi.exception;\r\n\r\n/**\r\n * @author wuwenze\r\n */\r\npublic class ExcelKitConfigFileNotFoundException extends ExcelKitRuntimeException {\r\n\r\n  private static final long serialVersionUID = -5061679627805728661L;\r\n\r\n  public ExcelKitConfigFileNotFoundException(String message) {\r\n    super(message);\r\n  }\r\n}\r\n"
  },
  {
    "path": "src/main/java/com/wuwenze/poi/exception/ExcelKitEncounterNoNeedXmlException.java",
    "content": "/*\n * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com).\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.wuwenze.poi.exception;\n\n/**\n * @author wuwenze\n */\npublic class ExcelKitEncounterNoNeedXmlException extends ExcelKitRuntimeException {\n\n  private static final long serialVersionUID = 4096057792690617204L;\n}\n"
  },
  {
    "path": "src/main/java/com/wuwenze/poi/exception/ExcelKitReadConverterException.java",
    "content": "/*\n * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com).\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.wuwenze.poi.exception;\n\n/**\n * @author wuwenze\n */\npublic class ExcelKitReadConverterException extends ExcelKitRuntimeException {\n\n  private static final long serialVersionUID = 2832313288988433562L;\n\n  public ExcelKitReadConverterException(String message) {\n    super(message);\n  }\n}\n"
  },
  {
    "path": "src/main/java/com/wuwenze/poi/exception/ExcelKitRuntimeException.java",
    "content": "/*\n * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com).\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.wuwenze.poi.exception;\n\n/**\n * @author wuwenze\n */\npublic class ExcelKitRuntimeException extends RuntimeException {\n\n  private static final long serialVersionUID = 1059413765208343152L;\n\n  public ExcelKitRuntimeException() {\n  }\n\n  public ExcelKitRuntimeException(String message) {\n    super(message);\n  }\n\n  public ExcelKitRuntimeException(String message, Throwable cause) {\n    super(message, cause);\n  }\n\n  public ExcelKitRuntimeException(Throwable cause) {\n    super(cause);\n  }\n}\n"
  },
  {
    "path": "src/main/java/com/wuwenze/poi/exception/ExcelKitWriteConverterException.java",
    "content": "/*\n * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com).\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.wuwenze.poi.exception;\n\n/**\n * @author wuwenze\n */\npublic class ExcelKitWriteConverterException extends ExcelKitRuntimeException {\n\n  private static final long serialVersionUID = 7989007279778794434L;\n\n  public ExcelKitWriteConverterException(String message) {\n    super(message);\n  }\n}\n"
  },
  {
    "path": "src/main/java/com/wuwenze/poi/exception/ExcelKitXmlAnalyzeException.java",
    "content": "/*\r\n * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com).\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *   http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage com.wuwenze.poi.exception;\r\n\r\n/**\r\n * @author wuwenze\r\n */\r\npublic class ExcelKitXmlAnalyzeException extends ExcelKitRuntimeException {\r\n\r\n  private static final long serialVersionUID = 2433077029852196265L;\r\n\r\n  public ExcelKitXmlAnalyzeException(String message) {\r\n    super(message);\r\n  }\r\n}\r\n"
  },
  {
    "path": "src/main/java/com/wuwenze/poi/factory/ExcelMappingFactory.java",
    "content": "/*\r\n * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com).\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *    http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage com.wuwenze.poi.factory;\r\n\r\nimport com.google.common.cache.CacheBuilder;\r\nimport com.google.common.cache.CacheLoader;\r\nimport com.google.common.cache.LoadingCache;\r\nimport com.google.common.collect.Lists;\r\nimport com.wuwenze.poi.annotation.Excel;\r\nimport com.wuwenze.poi.annotation.ExcelField;\r\nimport com.wuwenze.poi.exception.ExcelKitAnnotationAnalyzeException;\r\nimport com.wuwenze.poi.exception.ExcelKitConfigAnalyzeFailureException;\r\nimport com.wuwenze.poi.exception.ExcelKitConfigFileNotFoundException;\r\nimport com.wuwenze.poi.exception.ExcelKitXmlAnalyzeException;\r\nimport com.wuwenze.poi.pojo.ExcelMapping;\r\nimport com.wuwenze.poi.pojo.ExcelProperty;\r\nimport com.wuwenze.poi.util.BeanUtil;\r\nimport com.wuwenze.poi.util.PathUtil;\r\nimport com.wuwenze.poi.util.ValidatorUtil;\r\nimport java.io.File;\r\nimport java.lang.reflect.Field;\r\nimport java.util.Iterator;\r\nimport java.util.List;\r\nimport lombok.AccessLevel;\r\nimport lombok.NoArgsConstructor;\r\nimport org.dom4j.Attribute;\r\nimport org.dom4j.Document;\r\nimport org.dom4j.Element;\r\nimport org.dom4j.io.SAXReader;\r\n\r\n/**\r\n * @author wuwenze\r\n */\r\n@NoArgsConstructor(access = AccessLevel.PRIVATE)\r\npublic class ExcelMappingFactory {\r\n\r\n  private final static LoadingCache<Class<?>, ExcelMapping> mExcelMappingLoadingCache =\r\n      CacheBuilder.newBuilder()\r\n          .maximumSize(100)\r\n          .build(new CacheLoader<Class<?>, ExcelMapping>() {\r\n            @Override\r\n            public ExcelMapping load(Class<?> key) {\r\n              return ExcelMappingFactory.loadExcelMappingByClass(key);\r\n            }\r\n          });\r\n  private final static List<String> mClazzFields = Lists\r\n      .newArrayList(\"options\", \"writeConverter\", \"readConverter\", \"validator\");\r\n  private final static List<String> mRequeridAttrs = Lists.newArrayList(\"name\");\r\n\r\n  /**\r\n   * 获取指定实体的Excel映射信息\r\n   *\r\n   * @param clazz 实体\r\n   * @return ExcelMapping映射对象\r\n   */\r\n  public static ExcelMapping get(Class<?> clazz) {\r\n    try {\r\n      return ExcelMappingFactory.mExcelMappingLoadingCache.get(clazz);\r\n    } catch (Exception e) {\r\n      throw new ExcelKitConfigAnalyzeFailureException(e);\r\n    }\r\n  }\r\n\r\n  private static ExcelMapping loadExcelMappingByClass(Class<?> clazz) {\r\n    // 1. 从配置文件加载 (classpath:excel-mapping/className.xml)\r\n    ExcelMapping excelMapping = null;\r\n    boolean xmlConfigFileNotFound = false;\r\n    String loadExcelMappingFailedMessage = null;\r\n    try {\r\n      excelMapping = ExcelMappingFactory.loadExcelMappingByXml(clazz.getName());\r\n    } catch (Exception e) {\r\n      xmlConfigFileNotFound = e instanceof ExcelKitConfigFileNotFoundException;\r\n      loadExcelMappingFailedMessage = e.getMessage();\r\n    }\r\n    // 2. 从注解加载配置信息 (当配置文件未找到时)\r\n    if (null == excelMapping && xmlConfigFileNotFound) {\r\n      try {\r\n        excelMapping = ExcelMappingFactory.loadExcelMappingByAnnotation(clazz);\r\n      } catch (Exception e) {\r\n        loadExcelMappingFailedMessage = e.getMessage();\r\n      }\r\n    }\r\n    // 3. 加载配置信息失败.\r\n    if (null == excelMapping && null != loadExcelMappingFailedMessage) {\r\n      throw new ExcelKitConfigAnalyzeFailureException(loadExcelMappingFailedMessage);\r\n    }\r\n    return excelMapping;\r\n  }\r\n\r\n  private static ExcelMapping loadExcelMappingByAnnotation(Class<?> clazz)\r\n      throws IllegalAccessException, InstantiationException {\r\n    ExcelMapping excelMapping = new ExcelMapping();\r\n    Excel excel = clazz.getAnnotation(Excel.class);\r\n    if (null == excel) {\r\n      throw new ExcelKitAnnotationAnalyzeException(\r\n          \"[\" + clazz.getName() + \"] @Excel annotations not found.\");\r\n    }\r\n    excelMapping.setName(excel.value());\r\n    ExcelProperty excelMappingProperty;\r\n    Field[] fields = clazz.getDeclaredFields();\r\n    List<ExcelProperty> propertyList = Lists.newArrayList();\r\n    for (Field field : fields) {\r\n      ExcelField excelField = field.getAnnotation(ExcelField.class);\r\n      if (null != excelField) {\r\n        Class<?> emptyClazz = ExcelField.Void.class;\r\n        excelMappingProperty = ExcelProperty.builder()\r\n            .name(ValidatorUtil.isEmpty(excelField.name()) ? field.getName() : excelField.name())\r\n            .required(excelField.required())\r\n            .column(\r\n                ValidatorUtil.isEmpty(excelField.value()) ? field.getName() : excelField.value())\r\n            .comment(excelField.comment())\r\n            .maxLength(excelField.maxLength())\r\n            .width(excelField.width())\r\n            .dateFormat(excelField.dateFormat())\r\n            .options(excelField.options() != emptyClazz ? excelField.options().newInstance() : null)\r\n            .writeConverterExp(excelField.writeConverterExp())\r\n            .writeConverter(excelField.writeConverter() != emptyClazz ? excelField.writeConverter()\r\n                .newInstance() : null)\r\n            .readConverterExp(excelField.readConverterExp())\r\n            .readConverter(\r\n                excelField.readConverter() != emptyClazz ? excelField.readConverter().newInstance()\r\n                    : null)\r\n            .regularExp(excelField.regularExp())\r\n            .regularExpMessage(excelField.regularExpMessage())\r\n            .validator(\r\n                excelField.validator() != emptyClazz ? excelField.validator().newInstance() : null)\r\n            .build();\r\n        propertyList.add(excelMappingProperty);\r\n      }\r\n    }\r\n    if (propertyList.isEmpty()) {\r\n      throw new ExcelKitAnnotationAnalyzeException(\r\n          \"[\" + clazz.getName() + \"] @ExcelField annotations not found.\");\r\n    }\r\n    excelMapping.setPropertyList(propertyList);\r\n    return excelMapping;\r\n  }\r\n\r\n  private static ExcelMapping loadExcelMappingByXml(String clazzName) throws Exception {\r\n    ExcelMapping excelMapping = new ExcelMapping();\r\n    File config = PathUtil\r\n        .getFileByClasspath(String.format(\"excel-mapping/%s.xml\", clazzName));\r\n    String configFile = \"classpath:excel-mapping/\" + config.getName();\r\n    if (!config.exists()) {\r\n      throw new ExcelKitConfigFileNotFoundException(\r\n          \"[\" + configFile + \"] not found.\");\r\n    }\r\n    SAXReader reader = new SAXReader();\r\n    Document document = reader.read(config);\r\n    Element rootElement = document.getRootElement();\r\n    if (!\"excel-mapping\".equals(rootElement.getName())) {\r\n      throw new ExcelKitXmlAnalyzeException(\r\n          \"[\" + configFile + \"] <excel-mapping /> not found.\");\r\n    }\r\n    Attribute nameAttr = rootElement.attribute(\"name\");\r\n    if (null == nameAttr) {\r\n      throw new ExcelKitXmlAnalyzeException(\r\n          \"[\" + configFile + \"] <excel-mapping> attribute \\\"name\\\"  not found.\");\r\n    }\r\n    excelMapping.setName(nameAttr.getValue());\r\n    List<ExcelProperty> propertyList = Lists.newArrayList();\r\n    Iterator<Element> elementIterator = rootElement.elementIterator();\r\n    while (elementIterator.hasNext()) {\r\n      Element element = elementIterator.next();\r\n      if (\"property\".equals(element.getName())) {\r\n        List<Attribute> attributes = element.attributes();\r\n        ExcelMappingFactory.checkXmlPropertyRequiredAttr(configFile, attributes);\r\n\r\n        ExcelProperty excelMappingProperty = null;\r\n        for (Attribute attribute : attributes) {\r\n          if (null == excelMappingProperty) {\r\n            excelMappingProperty = new ExcelProperty();\r\n          }\r\n          String name = attribute.getName();\r\n          String value = attribute.getValue();\r\n          BeanUtil.setComplexProperty(excelMappingProperty, name,\r\n              ExcelMappingFactory.validAndGetPropertyValue(configFile, name, value));\r\n        }\r\n        if (null != excelMappingProperty) {\r\n          propertyList.add(excelMappingProperty);\r\n        }\r\n      }\r\n    }\r\n    if (propertyList.isEmpty()) {\r\n      throw new ExcelKitXmlAnalyzeException(\r\n          \"[\" + configFile + \"] <property /> not found.\");\r\n    }\r\n    excelMapping.setPropertyList(propertyList);\r\n    return excelMapping;\r\n  }\r\n\r\n  private static void checkXmlPropertyRequiredAttr(String configFile,\r\n      List<Attribute> attributes) {\r\n    Integer containsCount = 0;\r\n    for (Attribute attr : attributes) {\r\n      if (ExcelMappingFactory.mRequeridAttrs.contains(attr.getName())) {\r\n        containsCount++;\r\n      }\r\n    }\r\n    if (containsCount != ExcelMappingFactory.mRequeridAttrs.size()) {\r\n      throw new ExcelKitXmlAnalyzeException(\r\n          \"[\" + configFile + \"] <property /> missing required attributes: \"\r\n              + ExcelMappingFactory.mRequeridAttrs\r\n              .toString());\r\n    }\r\n  }\r\n\r\n  private static Object validAndGetPropertyValue(\r\n      String configFile, String name, String value) {\r\n    String messageTemplate = String\r\n        .format(\"[%s] <property %s=\\\"%s\\\"/> Analyze failed: \", configFile, name, value);\r\n    if (ExcelMappingFactory.mClazzFields.contains(name)) {\r\n      try {\r\n        return Class.forName(value).newInstance();\r\n      } catch (Exception e) {\r\n        throw new ExcelKitXmlAnalyzeException(messageTemplate + e.getMessage());\r\n      }\r\n    }\r\n    if (\"writeConverterExp\".equals(name) || \"readConverterExp\".equals(name)) {\r\n      for (String item : value.split(\",\")) {\r\n        if (!item.contains(\"=\")) {\r\n          throw new ExcelKitXmlAnalyzeException(messageTemplate\r\n              + \"Converter Expression error, Reference:[\\\"1=男,2=女\\\" or \\\"男=1,女=2\\\"].\");\r\n        }\r\n      }\r\n    }\r\n    return value;\r\n  }\r\n}\r\n"
  },
  {
    "path": "src/main/java/com/wuwenze/poi/handler/ExcelReadHandler.java",
    "content": "/*\r\n * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com).\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *   http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage com.wuwenze.poi.handler;\r\n\r\nimport com.wuwenze.poi.pojo.ExcelErrorField;\r\nimport java.util.List;\r\n\r\n/**\r\n * @author wuwenze\r\n */\r\npublic interface ExcelReadHandler<T> {\r\n\r\n  void onSuccess(int sheetIndex, int rowIndex, T entity);\r\n\r\n  void onError(int sheetIndex, int rowIndex, List<ExcelErrorField> errorFields);\r\n}\r\n"
  },
  {
    "path": "src/main/java/com/wuwenze/poi/pojo/ExcelErrorField.java",
    "content": "/*\n * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com).\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.wuwenze.poi.pojo;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.ToString;\n\n/**\n * @author wuwenze\n */\n@Data\n@Builder\n@ToString\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ExcelErrorField {\n\n  private Integer cellIndex;\n  private String name;\n  private String column;\n  private String errorMessage;\n}\n"
  },
  {
    "path": "src/main/java/com/wuwenze/poi/pojo/ExcelMapping.java",
    "content": "/*\r\n * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com).\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *   http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage com.wuwenze.poi.pojo;\r\n\r\nimport java.util.List;\r\nimport lombok.AllArgsConstructor;\r\nimport lombok.Builder;\r\nimport lombok.Data;\r\nimport lombok.NoArgsConstructor;\r\nimport lombok.ToString;\r\n\r\n/**\r\n * @author wuwenze\r\n */\r\n@Data\r\n@Builder\r\n@ToString\r\n@NoArgsConstructor\r\n@AllArgsConstructor\r\npublic class ExcelMapping {\r\n\r\n  private String name;\r\n  private List<ExcelProperty> propertyList;\r\n}\r\n"
  },
  {
    "path": "src/main/java/com/wuwenze/poi/pojo/ExcelProperty.java",
    "content": "/*\r\n * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com).\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *   http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage com.wuwenze.poi.pojo;\r\n\r\nimport com.wuwenze.poi.config.Options;\r\nimport com.wuwenze.poi.convert.ReadConverter;\r\nimport com.wuwenze.poi.convert.WriteConverter;\r\nimport com.wuwenze.poi.validator.Validator;\r\nimport lombok.AllArgsConstructor;\r\nimport lombok.Builder;\r\nimport lombok.Data;\r\nimport lombok.NoArgsConstructor;\r\nimport lombok.ToString;\r\n\r\n/**\r\n * @author wuwenze\r\n */\r\n@Data\r\n@Builder\r\n@ToString\r\n@NoArgsConstructor\r\n@AllArgsConstructor\r\npublic class ExcelProperty {\r\n\r\n  private String name;\r\n  private String column;\r\n  private Boolean required;\r\n  private Short width;\r\n  private String comment;\r\n  private Integer maxLength;\r\n  private String dateFormat;\r\n  private Options options;\r\n  private String writeConverterExp;\r\n  private WriteConverter writeConverter;\r\n  private String readConverterExp;\r\n  private ReadConverter readConverter;\r\n  private String regularExp;\r\n  private String regularExpMessage;\r\n  private Validator validator;\r\n}\r\n"
  },
  {
    "path": "src/main/java/com/wuwenze/poi/util/BeanUtil.java",
    "content": "/*\n * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com).\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.wuwenze.poi.util;\n\nimport lombok.AccessLevel;\nimport lombok.NoArgsConstructor;\nimport org.apache.commons.beanutils.ConvertUtils;\nimport org.apache.commons.beanutils.PropertyUtilsBean;\nimport org.apache.commons.beanutils.converters.DateConverter;\n\nimport java.lang.reflect.InvocationTargetException;\n\n/**\n * @author wuwenze\n */\n@NoArgsConstructor(access = AccessLevel.PRIVATE)\npublic class BeanUtil extends org.apache.commons.beanutils.BeanUtils {\n\n  public static void setComplexProperty(Object bean, String name, Object value)\n      throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, InstantiationException {\n    //修复日期为空时bug\n    ConvertUtils.register(new DateConverter(null), java.util.Date.class);\n    if (!name.contains(\".\")) {\n      BeanUtil.setProperty(bean, name, value);\n      return;\n    }\n    PropertyUtilsBean propertyUtilsBean = new PropertyUtilsBean();\n    String[] propertyLevels = name.split(\"\\\\.\");\n    String parentPropertyName = \"\";\n    for (int i = 0; i < propertyLevels.length; i++) {\n      String p = propertyLevels[i];\n      parentPropertyName = (parentPropertyName.equals(\"\") ? p : parentPropertyName + \".\" + p);\n      if (i < (propertyLevels.length - 1) &&\n          propertyUtilsBean.getProperty(bean, parentPropertyName) != null) {\n        continue;\n      }\n      Class<?> parentClass = propertyUtilsBean.getPropertyType(bean, parentPropertyName);\n      if (i < (propertyLevels.length - 1)) {\n        BeanUtil.setProperty(bean,\n            parentPropertyName, parentClass.getConstructor().newInstance());\n      } else {\n        BeanUtil.setProperty(bean, parentPropertyName,\n            parentClass.getConstructor(String.class).newInstance(value));\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/com/wuwenze/poi/util/Const.java",
    "content": "/*\r\n * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com).\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *    http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage com.wuwenze.poi.util;\r\n\r\nimport lombok.AccessLevel;\r\nimport lombok.NoArgsConstructor;\r\n\r\n/**\r\n * @author wuwenze\r\n */\r\n@NoArgsConstructor(access = AccessLevel.PRIVATE)\r\npublic final class Const {\r\n\r\n  public static final String ENCODING = \"UTF-8\";\r\n  public static final String XLSX_SUFFIX = \".xlsx\";\r\n  public static final String XLSX_CONTENT_TYPE = \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\";\r\n  public static final String XLSX_HEADER_KEY = \"Content-disposition\";\r\n  public static final String XLSX_HEADER_VALUE_TEMPLATE = \"attachment; filename=%s\";\r\n  public static final String XLSX_DEFAULT_EMPTY_CELL_VALUE = \"$EMPTY_CELL$\";\r\n  public static final Integer XLSX_DEFAULT_BEGIN_READ_ROW_INDEX = 1;\r\n  public static final String SAX_PARSER_CLASS = \"org.apache.xerces.parsers.SAXParser\";\r\n  public static final String SAX_C_ELEMENT = \"c\";\r\n  public static final String SAX_R_ATTR = \"r\";\r\n  public static final String SAX_T_ELEMENT = \"t\";\r\n  public static final String SAX_S_ATTR_VALUE = \"s\";\r\n  public static final String SAX_RID_PREFIX = \"rId\";\r\n  public static final String SAX_ROW_ELEMENT = \"row\";\r\n}\r\n"
  },
  {
    "path": "src/main/java/com/wuwenze/poi/util/DateUtil.java",
    "content": "/*\n * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com).\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.wuwenze.poi.util;\n\nimport com.google.common.cache.CacheBuilder;\nimport com.google.common.cache.CacheLoader;\nimport com.google.common.cache.LoadingCache;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport java.util.Locale;\nimport java.util.concurrent.ExecutionException;\nimport lombok.AccessLevel;\nimport lombok.NoArgsConstructor;\n\n\n/**\n * @author wuwenze\n */\n@NoArgsConstructor(access = AccessLevel.PRIVATE)\npublic class DateUtil {\n\n  public final static SimpleDateFormat ENGLISH_LOCAL_DF = new SimpleDateFormat(\n      \"EEE MMM dd HH:mm:ss z yyyy\", Locale.ENGLISH);\n  private final static LoadingCache<String, SimpleDateFormat> mDateFormatLoadingCache =\n      CacheBuilder.newBuilder()\n          .maximumSize(5)\n          .build(new CacheLoader<String, SimpleDateFormat>() {\n\n            @Override\n            public SimpleDateFormat load(String pattern) {\n              SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern);\n              simpleDateFormat.setLenient(true);\n              return simpleDateFormat;\n            }\n          });\n\n  public static Date parse(String pattern, Object value) throws Exception {\n    String valueString = (String) value;\n    return DateUtil.mDateFormatLoadingCache.get(pattern).parse(valueString);\n  }\n\n  public static String format(String pattern, Date value) {\n    try {\n      return DateUtil.mDateFormatLoadingCache.get(pattern).format(value);\n    } catch (ExecutionException e) {\n      e.printStackTrace();\n    }\n    return value.toString();\n  }\n}\n"
  },
  {
    "path": "src/main/java/com/wuwenze/poi/util/POIUtil.java",
    "content": "/*\r\n * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com).\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *    http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage com.wuwenze.poi.util;\r\n\r\nimport com.wuwenze.poi.config.Options;\r\nimport com.wuwenze.poi.exception.ExcelKitRuntimeException;\r\nimport java.io.File;\r\nimport java.io.IOException;\r\nimport java.io.OutputStream;\r\nimport javax.servlet.http.HttpServletResponse;\r\nimport lombok.AccessLevel;\r\nimport lombok.NoArgsConstructor;\r\nimport org.apache.poi.ss.usermodel.DataValidation;\r\nimport org.apache.poi.ss.usermodel.DataValidationConstraint;\r\nimport org.apache.poi.ss.usermodel.DataValidationHelper;\r\nimport org.apache.poi.ss.util.CellRangeAddressList;\r\nimport org.apache.poi.xssf.streaming.SXSSFCell;\r\nimport org.apache.poi.xssf.streaming.SXSSFRow;\r\nimport org.apache.poi.xssf.streaming.SXSSFSheet;\r\nimport org.apache.poi.xssf.streaming.SXSSFWorkbook;\r\n\r\n/**\r\n * @author wuwenze\r\n */\r\n@NoArgsConstructor(access = AccessLevel.PRIVATE)\r\npublic class POIUtil {\r\n\r\n  private static final int mDefaultRowAccessWindowSize = 100;\r\n\r\n\r\n  private static SXSSFWorkbook newSXSSFWorkbook(int rowAccessWindowSize) {\r\n    return new SXSSFWorkbook(rowAccessWindowSize);\r\n  }\r\n\r\n  public static SXSSFWorkbook newSXSSFWorkbook() {\r\n    return POIUtil.newSXSSFWorkbook(POIUtil.mDefaultRowAccessWindowSize);\r\n  }\r\n\r\n  public static SXSSFSheet newSXSSFSheet(SXSSFWorkbook wb, String sheetName) {\r\n    return wb.createSheet(sheetName);\r\n  }\r\n\r\n  public static SXSSFRow newSXSSFRow(SXSSFSheet sheet, int index) {\r\n    return sheet.createRow(index);\r\n  }\r\n\r\n  public static SXSSFCell newSXSSFCell(SXSSFRow row, int index) {\r\n    return row.createCell(index);\r\n  }\r\n\r\n  public static void setColumnWidth(\r\n      SXSSFSheet sheet, int index, Short width, String value) {\r\n    boolean widthNotHaveConfig = (null == width || width == -1);\r\n    if (widthNotHaveConfig && !ValidatorUtil.isEmpty(value)) {\r\n      sheet.setColumnWidth(index, (short) (value.length() * 2048));\r\n    } else {\r\n      width = widthNotHaveConfig ? 200 : width;\r\n      sheet.setColumnWidth(index, (short) (width * 35.7));\r\n    }\r\n  }\r\n\r\n  public static void setColumnCellRange(SXSSFSheet sheet, Options options,\r\n      int firstRow, int endRow,\r\n      int firstCell, int endCell) {\r\n    if (null != options) {\r\n      String[] datasource = options.get();\r\n      if (null != datasource && datasource.length > 0) {\r\n        if (datasource.length > 100) {\r\n          throw new ExcelKitRuntimeException(\"Options item too much.\");\r\n        }\r\n\r\n        DataValidationHelper validationHelper = sheet.getDataValidationHelper();\r\n        DataValidationConstraint explicitListConstraint = validationHelper\r\n            .createExplicitListConstraint(datasource);\r\n        CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCell,\r\n            endCell);\r\n        DataValidation validation = validationHelper\r\n            .createValidation(explicitListConstraint, regions);\r\n        validation.setSuppressDropDownArrow(true);\r\n        validation.createErrorBox(\"提示\", \"请从下拉列表选取\");\r\n        validation.setShowErrorBox(true);\r\n        sheet.addValidationData(validation);\r\n      }\r\n    }\r\n  }\r\n\r\n  public static void write(SXSSFWorkbook wb, OutputStream out) {\r\n    try {\r\n      if (null != out) {\r\n        wb.write(out);\r\n        out.flush();\r\n      }\r\n    } catch (IOException e) {\r\n      e.printStackTrace();\r\n    } finally {\r\n      if (null != out) {\r\n        try {\r\n          out.close();\r\n        } catch (IOException e) {\r\n          e.printStackTrace();\r\n        }\r\n      }\r\n    }\r\n  }\r\n\r\n  public static void download(\r\n      SXSSFWorkbook wb, HttpServletResponse response, String filename) {\r\n    try {\r\n      OutputStream out = response.getOutputStream();\r\n      response.setContentType(Const.XLSX_CONTENT_TYPE);\r\n      response.setHeader(Const.XLSX_HEADER_KEY,\r\n          String.format(Const.XLSX_HEADER_VALUE_TEMPLATE, filename));\r\n      POIUtil.write(wb, out);\r\n    } catch (IOException e) {\r\n      e.printStackTrace();\r\n    }\r\n  }\r\n\r\n  public static Object convertByExp(Object propertyValue, String converterExp)\r\n      throws Exception {\r\n    try {\r\n      String[] convertSource = converterExp.split(\",\");\r\n      for (String item : convertSource) {\r\n        String[] itemArray = item.split(\"=\");\r\n        if (itemArray[0].equals(propertyValue)) {\r\n          return itemArray[1];\r\n        }\r\n      }\r\n    } catch (Exception e) {\r\n      throw e;\r\n    }\r\n    return propertyValue;\r\n  }\r\n\r\n  public static int countNullCell(String ref, String ref2) {\r\n    // excel2007最大行数是1048576，最大列数是16384，最后一列列名是XFD\r\n    String xfd = ref.replaceAll(\"\\\\d+\", \"\");\r\n    String xfd_1 = ref2.replaceAll(\"\\\\d+\", \"\");\r\n\r\n    xfd = POIUtil.fillChar(xfd, 3, '@', true);\r\n    xfd_1 = POIUtil.fillChar(xfd_1, 3, '@', true);\r\n\r\n    char[] letter = xfd.toCharArray();\r\n    char[] letter_1 = xfd_1.toCharArray();\r\n    int res =\r\n        (letter[0] - letter_1[0]) * 26 * 26 + (letter[1] - letter_1[1]) * 26 + (letter[2]\r\n            - letter_1[2]);\r\n    return res - 1;\r\n  }\r\n\r\n  private static String fillChar(String str, int len, char let, boolean isPre) {\r\n    int len_1 = str.length();\r\n    if (len_1 < len) {\r\n      if (isPre) {\r\n        for (int i = 0; i < (len - len_1); i++) {\r\n          str = let + str;\r\n        }\r\n      } else {\r\n        for (int i = 0; i < (len - len_1); i++) {\r\n          str = str + let;\r\n        }\r\n      }\r\n    }\r\n    return str;\r\n  }\r\n\r\n  public static void checkExcelFile(File file) {\r\n    String filename = null != file ? file.getAbsolutePath() : null;\r\n    if (null == filename || !file.exists()) {\r\n      throw new ExcelKitRuntimeException(\"Excel file[\" + filename + \"] does not exist.\");\r\n    }\r\n    if (!filename.endsWith(Const.XLSX_SUFFIX)) {\r\n      throw new ExcelKitRuntimeException(\r\n          \"[\" + filename + \"]Only .xlsx formatted files are supported.\");\r\n    }\r\n  }\r\n\r\n\r\n}\r\n"
  },
  {
    "path": "src/main/java/com/wuwenze/poi/util/PathUtil.java",
    "content": "/*\r\n * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com).\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *   http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage com.wuwenze.poi.util;\r\n\r\nimport java.io.File;\r\nimport lombok.AccessLevel;\r\nimport lombok.NoArgsConstructor;\r\n\r\n/**\r\n * @author wuwenze\r\n */\r\n@NoArgsConstructor(access = AccessLevel.PRIVATE)\r\npublic class PathUtil {\r\n\r\n  public static String getClasspath() {\r\n    return PathUtil.class.getResource(\"/\").getPath();\r\n  }\r\n\r\n  public static String getFilePathByClasspath(String name) {\r\n    return PathUtil.getClasspath() + name;\r\n  }\r\n\r\n  public static File getFileByClasspath(String name) {\r\n    return new File(PathUtil.getFilePathByClasspath(name));\r\n  }\r\n}\r\n"
  },
  {
    "path": "src/main/java/com/wuwenze/poi/util/RegexUtil.java",
    "content": "/*\n * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com).\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.wuwenze.poi.util;\n\nimport com.google.common.cache.CacheBuilder;\nimport com.google.common.cache.CacheLoader;\nimport com.google.common.cache.LoadingCache;\nimport java.util.concurrent.ExecutionException;\nimport java.util.regex.Pattern;\nimport lombok.AccessLevel;\nimport lombok.NoArgsConstructor;\n\n/**\n * @author wuwenze\n */\n@NoArgsConstructor(access = AccessLevel.PRIVATE)\npublic class RegexUtil {\n\n  private final static LoadingCache<String, Pattern> mRegexPatternLoadingCache =\n      CacheBuilder.newBuilder()\n          .maximumSize(5)\n          .build(new CacheLoader<String, Pattern>() {\n\n            @Override\n            public Pattern load(String pattern) {\n              return Pattern.compile(pattern);\n            }\n          });\n\n  public static Boolean isMatches(String pattern, Object value) {\n    try {\n      String valueString = (String) value;\n      return RegexUtil.mRegexPatternLoadingCache.get(pattern).matcher(valueString).matches();\n    } catch (ExecutionException e) {\n      e.printStackTrace();\n    }\n    return false;\n  }\n}\n"
  },
  {
    "path": "src/main/java/com/wuwenze/poi/util/ValidatorUtil.java",
    "content": "/*\r\n * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com).\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *   http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage com.wuwenze.poi.util;\r\n\r\nimport java.util.regex.Pattern;\r\nimport lombok.AccessLevel;\r\nimport lombok.NoArgsConstructor;\r\n\r\n/**\r\n * @author wuwenze\r\n */\r\n@NoArgsConstructor(access = AccessLevel.PRIVATE)\r\npublic class ValidatorUtil {\r\n\r\n  private static final Pattern PATTERN_NUMERIC = Pattern.compile(\"^(-?\\\\d+)(\\\\.\\\\d+)?$\");\r\n  private static final Pattern PATTERN_EMAIL = Pattern\r\n      .compile(\"^[\\\\w-]+(\\\\.[\\\\w-]+)*@[\\\\w-]+(\\\\.[\\\\w-]+)+$\");\r\n  private static final Pattern PATTERN_MOBILE_PHONE = Pattern.compile(\"^(1)\\\\d{10}$\");\r\n\r\n  public static boolean match(String value, String regularExp) {\r\n    return ValidatorUtil.isEmpty(value) ? false\r\n        : Pattern.compile(regularExp).matcher(value).matches();\r\n  }\r\n\r\n  /**\r\n   * 判断是否为浮点数或者整数\r\n   *\r\n   * @param value 字符串\r\n   * @return true Or false\r\n   */\r\n  public static boolean isNumeric(String value) {\r\n    return ValidatorUtil.isEmpty(value) ? false\r\n        : ValidatorUtil.PATTERN_NUMERIC.matcher(value).matches();\r\n  }\r\n\r\n  /**\r\n   * 判断是否为正确的邮件格式\r\n   *\r\n   * @param value 字符串\r\n   * @return true Or false\r\n   */\r\n  public static boolean isEmail(String value) {\r\n    return ValidatorUtil.isEmpty(value) ? false\r\n        : ValidatorUtil.PATTERN_EMAIL.matcher(value).matches();\r\n  }\r\n\r\n  /**\r\n   * 判断字符串是否为合法手机号\r\n   *\r\n   * @param value 字符串\r\n   * @return true Or false\r\n   */\r\n  public static boolean isMobile(String value) {\r\n    return ValidatorUtil.isEmpty(value) ? false\r\n        : ValidatorUtil.PATTERN_MOBILE_PHONE.matcher(value).matches();\r\n  }\r\n\r\n  /**\r\n   * 判断是否为数字\r\n   *\r\n   * @param value 字符串\r\n   * @return true Or false\r\n   */\r\n  public static boolean isNumber(String value) {\r\n    try {\r\n      Integer.parseInt(value);\r\n    } catch (Exception ex) {\r\n      return false;\r\n    }\r\n    return true;\r\n  }\r\n\r\n  /**\r\n   * 判断字符串是否为空\r\n   *\r\n   * @param value 字符串\r\n   * @return true Or false\r\n   */\r\n  public static boolean isEmpty(String value) {\r\n    return null == value || \"\".equals(value.trim()) || \"null\".equalsIgnoreCase(value.trim());\r\n  }\r\n}\r\n"
  },
  {
    "path": "src/main/java/com/wuwenze/poi/validator/EmailValidator.java",
    "content": "/*\r\n * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com).\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *   http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage com.wuwenze.poi.validator;\r\n\r\nimport com.wuwenze.poi.util.ValidatorUtil;\r\n\r\n/**\r\n * @author wuwenze\r\n */\r\npublic class EmailValidator implements Validator {\r\n\r\n  @Override\r\n  public String valid(Object value) {\r\n    String valueString = (String) value;\r\n    return ValidatorUtil.isEmail(valueString) ? null : \"[\" + valueString + \"]不是正确的EMail.\";\r\n  }\r\n}\r\n"
  },
  {
    "path": "src/main/java/com/wuwenze/poi/validator/MobileValidator.java",
    "content": "/*\r\n * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com).\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *   http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage com.wuwenze.poi.validator;\r\n\r\nimport com.wuwenze.poi.util.ValidatorUtil;\r\n\r\n/**\r\n * @author wuwenze\r\n */\r\npublic class MobileValidator implements Validator {\r\n\r\n  @Override\r\n  public String valid(Object value) {\r\n    String valueString = (String) value;\r\n    return ValidatorUtil.isMobile(valueString) ? null : \"[\" + valueString + \"]不是正确的手机号码.\";\r\n  }\r\n}\r\n"
  },
  {
    "path": "src/main/java/com/wuwenze/poi/validator/Validator.java",
    "content": "/*\r\n * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com).\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *   http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage com.wuwenze.poi.validator;\r\n\r\n/**\r\n * @author wuwenze\r\n */\r\npublic interface Validator {\r\n\r\n  /**\r\n   * 验证单元格的值, 若验证失败, 请返回错误消息.\r\n   *\r\n   * @param value 当前单元格的值\r\n   * @return null or errorMessage\r\n   */\r\n  String valid(Object value);\r\n}\r\n"
  },
  {
    "path": "src/main/java/com/wuwenze/poi/xlsx/ExcelXlsxReader.java",
    "content": "/*\n * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com).\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.wuwenze.poi.xlsx;\n\nimport com.google.common.collect.Lists;\nimport com.google.common.collect.Maps;\nimport com.wuwenze.poi.config.Options;\nimport com.wuwenze.poi.convert.ReadConverter;\nimport com.wuwenze.poi.exception.ExcelKitEncounterNoNeedXmlException;\nimport com.wuwenze.poi.exception.ExcelKitReadConverterException;\nimport com.wuwenze.poi.exception.ExcelKitRuntimeException;\nimport com.wuwenze.poi.handler.ExcelReadHandler;\nimport com.wuwenze.poi.pojo.ExcelErrorField;\nimport com.wuwenze.poi.pojo.ExcelMapping;\nimport com.wuwenze.poi.pojo.ExcelProperty;\nimport com.wuwenze.poi.util.*;\nimport com.wuwenze.poi.validator.Validator;\nimport org.apache.poi.openxml4j.exceptions.OpenXML4JException;\nimport org.apache.poi.openxml4j.opc.OPCPackage;\nimport org.apache.poi.ss.usermodel.BuiltinFormats;\nimport org.apache.poi.ss.usermodel.DataFormatter;\nimport org.apache.poi.xssf.eventusermodel.XSSFReader;\nimport org.apache.poi.xssf.model.SharedStringsTable;\nimport org.apache.poi.xssf.model.StylesTable;\nimport org.apache.poi.xssf.usermodel.XSSFCellStyle;\nimport org.apache.poi.xssf.usermodel.XSSFRichTextString;\nimport org.xml.sax.Attributes;\nimport org.xml.sax.InputSource;\nimport org.xml.sax.SAXException;\nimport org.xml.sax.XMLReader;\nimport org.xml.sax.helpers.DefaultHandler;\nimport org.xml.sax.helpers.XMLReaderFactory;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.Date;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * @author wuwenze\n */\npublic class ExcelXlsxReader extends DefaultHandler {\n\n  private Integer mCurrentSheetIndex = -1, mCurrentRowIndex = 0, mCurrentCellIndex = 0;\n  private ExcelCellType mNextCellType = ExcelCellType.STRING;\n  private String mCurrentCellRef, mPreviousCellRef, mMaxCellRef;\n  private SharedStringsTable mSharedStringsTable;\n  private String mPreviousCellValue;\n  private StylesTable mStylesTable;\n  private Boolean mNextIsString = false;\n  private Short mFormatIndex;\n  private String mFormatString;\n\n  private final ExcelMapping mExcelMapping;\n  private final ExcelReadHandler mExcelReadHandler;\n  private final Class<? extends Object> mEntityClass;\n  private final List<Object> mExcelRowObjectData = Lists.newArrayList();\n  private Integer mBeginReadRowIndex = Const.XLSX_DEFAULT_BEGIN_READ_ROW_INDEX;\n  private final Object mEmptyCellValue = Const.XLSX_DEFAULT_EMPTY_CELL_VALUE;\n\n  private final DataFormatter formatter = new DataFormatter();\n\n\n  public ExcelXlsxReader(Class<? extends Object> entityClass,//\n      ExcelMapping excelMapping, //\n      ExcelReadHandler excelReadHandler) {\n    this(entityClass, excelMapping, null, excelReadHandler);\n  }\n\n  public ExcelXlsxReader(Class<? extends Object> entityClass,//\n      ExcelMapping excelMapping, //\n      Integer beginReadRowIndex,//\n      ExcelReadHandler excelReadHandler) {\n    mEntityClass = entityClass;\n    mExcelMapping = excelMapping;\n    if (null != beginReadRowIndex) {\n      mBeginReadRowIndex = beginReadRowIndex;\n    }\n    mExcelReadHandler = excelReadHandler;\n  }\n\n  public void process(String fileName) throws ExcelKitRuntimeException {\n    try {\n      processAll(OPCPackage.open(fileName));\n    } catch (Exception e) {\n      throw new ExcelKitRuntimeException(\"Only .xlsx formatted files are supported.\", e);\n    }\n  }\n\n  public void process(InputStream in) throws ExcelKitRuntimeException {\n    try {\n      processAll(OPCPackage.open(in));\n    } catch (Exception e) {\n      throw new ExcelKitRuntimeException(\"Only .xlsx formatted files are supported.\", e);\n    }\n  }\n\n  private void processAll(OPCPackage pkg)\n      throws IOException, OpenXML4JException, SAXException {\n    XSSFReader xssfReader = new XSSFReader(pkg);\n    mStylesTable = xssfReader.getStylesTable();\n    SharedStringsTable sst = xssfReader.getSharedStringsTable();\n    XMLReader parser = this.fetchSheetParser(sst);\n    Iterator<InputStream> sheets = xssfReader.getSheetsData();\n    while (sheets.hasNext()) {\n      mCurrentRowIndex = 0;\n      mCurrentSheetIndex++;\n      InputStream sheet = sheets.next();\n      InputSource sheetSource = new InputSource(sheet);\n      parser.parse(sheetSource);\n      sheet.close();\n    }\n    pkg.close();\n  }\n\n  public void process(String fileName, int sheetIndex) throws ExcelKitRuntimeException {\n    try {\n      this.processBySheet(sheetIndex, OPCPackage.open(fileName));\n    } catch (Exception e) {\n      throw new ExcelKitRuntimeException(\"Only .xlsx formatted files are supported.\", e);\n    }\n  }\n\n  public void process(InputStream in, int sheetIndex) throws ExcelKitRuntimeException {\n    try {\n      this.processBySheet(sheetIndex, OPCPackage.open(in));\n    } catch (Exception e) {\n      throw new ExcelKitRuntimeException(\"Only .xlsx formatted files are supported.\", e);\n    }\n  }\n\n  private void processBySheet(int sheetIndex, OPCPackage pkg)\n      throws IOException, OpenXML4JException, SAXException {\n    XSSFReader r = new XSSFReader(pkg);\n    SharedStringsTable sst = r.getSharedStringsTable();\n\n    XMLReader parser = fetchSheetParser(sst);\n\n    // 根据 rId# 或 rSheet# 查找sheet\n    InputStream sheet = r.getSheet(Const.SAX_RID_PREFIX + (sheetIndex + 1));\n    mCurrentSheetIndex++;\n    InputSource sheetSource = new InputSource(sheet);\n    try {\n      parser.parse(sheetSource);\n    } catch (ExcelKitEncounterNoNeedXmlException e) {\n      sheet = r.getSheet(Const.SAX_RID_PREFIX + (sheetIndex + 3));\n      sheetSource = new InputSource(sheet);\n      parser.parse(sheetSource);\n    }\n    sheet.close();\n    pkg.close();\n  }\n\n  @Override\n  public void startElement(\n      String uri, String localName, String name, Attributes attributes) {\n    if (\"sst\".equals(name) || \"styleSheet\".equals(name)) {\n      throw new ExcelKitEncounterNoNeedXmlException();\n    }\n    // c => 单元格\n    if (Const.SAX_C_ELEMENT.equals(name)) {\n      String ref = attributes.getValue(Const.SAX_R_ATTR);\n      // 前一个单元格的位置\n      mPreviousCellRef = null == mPreviousCellRef ? ref : mCurrentCellRef;\n      // 当前单元格的位置\n      mCurrentCellRef = ref;\n      // Figure out if the value is an index in the SST\n      String cellType = attributes.getValue(Const.SAX_T_ELEMENT);\n      String cellStyleStr = attributes.getValue(Const.SAX_S_ATTR_VALUE);\n      mNextIsString = (null != cellType && cellType.equals(Const.SAX_S_ATTR_VALUE));\n      // 设定单元格类型\n      this.setNextCellType(cellType, cellStyleStr);\n    }\n    mPreviousCellValue = \"\";\n  }\n\n\n  @Override\n  public void endElement(String uri, String localName, String name) {\n    // Process the last contents as required.\n    // Do now, as characters() may be called more than once\n    if (mNextIsString) {\n      int index = Integer.parseInt(mPreviousCellValue);\n      mPreviousCellValue = new XSSFRichTextString(mSharedStringsTable.getEntryAt(index))\n          .toString();\n      mNextIsString = false;\n    }\n\n    // 处理单元格数据\n    if (Const.SAX_C_ELEMENT.equals(name)) {\n      String value = this.getCellValue(mPreviousCellValue.trim());\n\n      // 空值补齐(中)\n      if (!mCurrentCellRef.equals(mPreviousCellRef)) {\n        for (int i = 0; i < POIUtil.countNullCell(mCurrentCellRef, mPreviousCellRef);\n            i++) {\n          mExcelRowObjectData.add(mCurrentCellIndex, mEmptyCellValue);\n          mCurrentCellIndex++;\n        }\n      }\n      mExcelRowObjectData.add(mCurrentCellIndex, value);\n      mCurrentCellIndex++;\n    }\n    // 如果标签名称为 row ，这说明已到行尾，通知回调处理当前行的数据\n    else if (Const.SAX_ROW_ELEMENT.equals(name)) {\n      if (mCurrentRowIndex == 0) {\n        mMaxCellRef = mCurrentCellRef;\n      }\n      // 空值补齐(后)\n      if (null != mMaxCellRef) {\n        for (int i = 0; i <= POIUtil.countNullCell(mMaxCellRef, mCurrentCellRef); i++) {\n          mExcelRowObjectData.add(mCurrentCellIndex, mEmptyCellValue);\n          mCurrentCellIndex++;\n        }\n      }\n      try {\n        this.performVerificationAndProcessFlowRow();\n      } catch (Exception e) {\n        e.printStackTrace();\n      } finally {\n        mExcelRowObjectData.clear();\n        mCurrentRowIndex++;\n        mCurrentCellIndex = 0;\n        mPreviousCellRef = null;\n        mCurrentCellRef = null;\n      }\n    }\n  }\n\n  @Override\n  public void characters(char[] chars, int start, int length) {\n    mPreviousCellValue = mPreviousCellValue.concat(new String(chars, start, length));\n  }\n\n  private XMLReader fetchSheetParser(SharedStringsTable sst) throws SAXException {\n    XMLReader parser = XMLReaderFactory.createXMLReader(Const.SAX_PARSER_CLASS);\n    mSharedStringsTable = sst;\n    parser.setContentHandler(this);\n    return parser;\n  }\n\n  enum ExcelCellType {\n    BOOL, ERROR, FORMULA, INLINESTR, STRING, NUMBER, DATE, NULL\n  }\n\n  private void setNextCellType(String cellType, String cellStyleStr) {\n    mNextCellType = ExcelCellType.STRING;\n    mFormatIndex = -1;\n    mFormatString = null;\n\n    if (\"b\".equals(cellType)) {\n      mNextCellType = ExcelCellType.BOOL;\n    } else if (\"e\".equals(cellType)) {\n      mNextCellType = ExcelCellType.ERROR;\n    } else if (\"inlineStr\".equals(cellType)) {\n      mNextCellType = ExcelCellType.INLINESTR;\n    } else if (\"s\".equals(cellType)) {\n      mNextCellType = ExcelCellType.STRING;\n    } else if (\"str\".equals(cellType)) {\n      mNextCellType = ExcelCellType.FORMULA;\n    }\n    if (null != cellStyleStr) {\n      int styleIndex = Integer.parseInt(cellStyleStr);\n      XSSFCellStyle style = mStylesTable.getStyleAt(styleIndex);\n      mFormatIndex = style.getDataFormat();\n      mFormatString = style.getDataFormatString();\n      if (\"m/d/yy\".equals(mFormatString)) {\n        mNextCellType = mNextCellType.DATE;\n        mFormatString = \"yyyy-MM-dd hh:mm:ss.SSS\";\n      }\n      if (null == mFormatString) {\n        mNextCellType = mNextCellType.NULL;\n        mFormatString = BuiltinFormats.getBuiltinFormat(mFormatIndex);\n      }\n    }\n  }\n\n  private String getCellValue(String value) {\n    String thisStr;\n    switch (mNextCellType) {\n      case BOOL:\n        return value.charAt(0) == '0' ? \"FALSE\" : \"TRUE\";\n      case ERROR:\n        return \"\\\"ERROR:\" + value + '\"';\n      case FORMULA:\n        return '\"' + value + '\"';\n      case INLINESTR:\n        return new XSSFRichTextString(value).toString();\n      case STRING:\n        return String.valueOf(value);\n      case NUMBER:\n        if (mFormatString != null) {\n          thisStr = formatter.formatRawCellContents(Double.parseDouble(value), mFormatIndex, mFormatString).trim();\n        } else {\n          thisStr = value;\n        }\n\n        thisStr = thisStr.replace(\"_\", \"\").trim();\n        break;\n      case DATE:\n        thisStr = formatter.formatRawCellContents(Double.parseDouble(value), mFormatIndex, mFormatString);\n        // 对日期字符串作特殊处理\n        thisStr = thisStr.replace(\" \", \"T\");\n        break;\n      default:\n        thisStr = \"\";\n        break;\n    }\n    return thisStr;\n  }\n\n  private final static String CHECK_MAP_KEY_OF_VALUE = \"CELL_VALUE\";\n  private final static String CHECK_MAP_KEY_OF_ERROR = \"CELL_ERROR\";\n\n  private void performVerificationAndProcessFlowRow() throws Exception {\n    if (mCurrentRowIndex >= mBeginReadRowIndex) {\n      List<ExcelProperty> propertyList = mExcelMapping.getPropertyList();\n      Integer excelRowDataSize = mExcelRowObjectData.size();\n      Integer excelMappingPropertySize = propertyList.size();\n      // 空值补齐(前)\n      for (int i = 0; i < excelMappingPropertySize - excelRowDataSize; i++) {\n        mExcelRowObjectData.add(i, mEmptyCellValue);\n      }\n\n      if (!this.rowObjectDataIsAllEmptyCellValue()) {\n        Object entity = mEntityClass.newInstance();\n        List<ExcelErrorField> errorFields = Lists.newArrayList();\n        for (int i = 0; i < propertyList.size(); i++) {\n          ExcelProperty property = propertyList.get(i);\n          Map<String, Object> checkAndConvertPropertyRetMap = this.checkAndConvertProperty(i, property, mExcelRowObjectData.get(i));\n          Object errorFieldObject = checkAndConvertPropertyRetMap.get(\n              ExcelXlsxReader.CHECK_MAP_KEY_OF_ERROR);\n          if (null != errorFieldObject) {\n            errorFields.add((ExcelErrorField) errorFieldObject);\n          }\n          if (errorFields.isEmpty()) {\n            Object propertyValue = checkAndConvertPropertyRetMap.get(\n                ExcelXlsxReader.CHECK_MAP_KEY_OF_VALUE);\n            BeanUtil.setComplexProperty(entity, property.getName(), propertyValue);\n          }\n        }\n        if (errorFields.isEmpty()) {\n          mExcelReadHandler.onSuccess(mCurrentSheetIndex, mCurrentRowIndex, entity);\n          return;\n        }\n        mExcelReadHandler.onError(mCurrentSheetIndex, mCurrentRowIndex, errorFields);\n      }\n    }\n  }\n\n  private boolean rowObjectDataIsAllEmptyCellValue() {\n    int emptyObjectCount = 0;\n    for (Object excelRowObjectData : mExcelRowObjectData) {\n      if ((null == excelRowObjectData) //\n          || excelRowObjectData.equals(mEmptyCellValue) //\n          || ValidatorUtil.isEmpty((String) excelRowObjectData)) {\n        emptyObjectCount++;\n      }\n    }\n    return emptyObjectCount == mExcelRowObjectData.size();\n  }\n\n  private Map<String, Object> checkAndConvertProperty(Integer cellIndex,\n      ExcelProperty property,\n      Object propertyValue) {\n\n     if(null == propertyValue || ValidatorUtil.isEmpty((String) propertyValue) || Const.XLSX_DEFAULT_EMPTY_CELL_VALUE.equals(propertyValue)) {\n        // required\n        Boolean required = property.getRequired();\n        if (null != required && required) {\n            return this.buildCheckAndConvertPropertyRetMap(cellIndex, property, propertyValue, \"单元格的值必须填写\");\n        }\n\n        // empty cell doesn't need to check anymore\n        return this.buildCheckAndConvertPropertyRetMap(cellIndex, property, null, null);\n    }\n\n    // maxLength\n    Integer maxLength = property.getMaxLength();\n    if (-1 != maxLength) {\n      if (String.valueOf(propertyValue).length() > maxLength) {\n        return this.buildCheckAndConvertPropertyRetMap(cellIndex, property, propertyValue, \"超过最大长度: \" + maxLength);\n      }\n    }\n\n    // dateFormat\n    String dateFormat = property.getDateFormat();\n    if (!ValidatorUtil.isEmpty(dateFormat)) {\n      try {\n        // 时间格式转换后，直接返回。\n        Date parseDateValue = DateUtil.parse(dateFormat, propertyValue);\n        return this.buildCheckAndConvertPropertyRetMap(cellIndex, property, parseDateValue, null);\n      } catch (Exception e) {\n        return this.buildCheckAndConvertPropertyRetMap(//\n            cellIndex, property, propertyValue, \"时间格式解析失败 [\" + dateFormat + \"]\");\n      }\n    }\n\n    // options\n    Options options = property.getOptions();\n    if (null != options) {\n      Object[] values = options.get();\n      if (null != values && values.length > 0) {\n        boolean containInOptions = false;\n        for (Object value : values) {\n          if (propertyValue.equals(value)) {\n            containInOptions = true;\n            break;\n          }\n        }\n        if (!containInOptions) {\n          return this.buildCheckAndConvertPropertyRetMap(//\n              cellIndex, property, propertyValue, \"[\" + propertyValue + \"]不是规定的下拉框的值\");\n        }\n      }\n    }\n\n    // regularExp\n    String regularExp = property.getRegularExp();\n    if (!ValidatorUtil.isEmpty(regularExp)) {\n      if (!RegexUtil.isMatches(regularExp, propertyValue)) {\n        String regularExpMessage = property.getRegularExpMessage();\n        String validErrorMessage = !ValidatorUtil.isEmpty(regularExpMessage) ?\n            regularExpMessage : \"正则表达式校验失败 [\" + regularExp + \"]\";\n        return this.buildCheckAndConvertPropertyRetMap(//\n            cellIndex, property, propertyValue, validErrorMessage);\n      }\n    }\n\n    // validator\n    Validator validator = property.getValidator();\n    if (null != validator) {\n      String validErrorMessage = validator.valid(propertyValue);\n      if (null != validErrorMessage) {\n        return this.buildCheckAndConvertPropertyRetMap(//\n            cellIndex, property, propertyValue, validErrorMessage);\n      }\n    }\n\n    // readConverterExp && readConverter (按照优先级处理)\n    String readConverterExp = property.getReadConverterExp();\n    ReadConverter readConverter = property.getReadConverter();\n    if (!ValidatorUtil.isEmpty(readConverterExp)) {\n      try {\n        Object convertPropertyValue = POIUtil.convertByExp(propertyValue, readConverterExp);\n        return this.buildCheckAndConvertPropertyRetMap(//\n            cellIndex, property, convertPropertyValue, null);\n      } catch (Exception e) {\n        return this.buildCheckAndConvertPropertyRetMap(//\n            cellIndex, property, propertyValue, \"由于readConverterExp表达式的值不规范导致转换失败\");\n      }\n    } else if (null != readConverter) {\n      try {\n        return this.buildCheckAndConvertPropertyRetMap(//\n            cellIndex, property, readConverter.convert(propertyValue), null);\n      } catch (ExcelKitReadConverterException e) {\n        return this.buildCheckAndConvertPropertyRetMap(//\n            cellIndex, property, propertyValue, e.getMessage());\n      }\n    }\n    return this.buildCheckAndConvertPropertyRetMap(cellIndex, property, propertyValue, null);\n  }\n\n  private Map<String, Object> buildCheckAndConvertPropertyRetMap(Integer cellIndex,\n      ExcelProperty property,//\n      Object propertyValue, String validErrorMessage) {\n    Map<String, Object> resultMap = Maps.newHashMap();\n    resultMap.put(ExcelXlsxReader.CHECK_MAP_KEY_OF_VALUE, propertyValue);\n    if (null != validErrorMessage) {\n      resultMap.put(ExcelXlsxReader.CHECK_MAP_KEY_OF_ERROR, ExcelErrorField.builder()//\n          .cellIndex(cellIndex)//\n          .column(property.getColumn())//\n          .name(property.getName())//\n          .errorMessage(validErrorMessage)//\n          .build());\n    }\n    return resultMap;\n  }\n}\n"
  },
  {
    "path": "src/main/java/com/wuwenze/poi/xlsx/ExcelXlsxWriter.java",
    "content": "/*\n * Copyright (c) 2018, 吴汶泽 (wenzewoo@gmail.com).\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.wuwenze.poi.xlsx;\n\nimport com.wuwenze.poi.convert.WriteConverter;\nimport com.wuwenze.poi.exception.ExcelKitRuntimeException;\nimport com.wuwenze.poi.pojo.ExcelMapping;\nimport com.wuwenze.poi.pojo.ExcelProperty;\nimport com.wuwenze.poi.util.DateUtil;\nimport com.wuwenze.poi.util.POIUtil;\nimport com.wuwenze.poi.util.ValidatorUtil;\nimport java.text.ParseException;\nimport java.util.Date;\nimport java.util.List;\nimport org.apache.commons.beanutils.BeanUtils;\nimport org.apache.poi.hssf.util.HSSFColor;\nimport org.apache.poi.ss.usermodel.BorderStyle;\nimport org.apache.poi.ss.usermodel.CellStyle;\nimport org.apache.poi.ss.usermodel.Comment;\nimport org.apache.poi.ss.usermodel.DataFormat;\nimport org.apache.poi.ss.usermodel.FillPatternType;\nimport org.apache.poi.ss.usermodel.Font;\nimport org.apache.poi.ss.usermodel.HorizontalAlignment;\nimport org.apache.poi.xssf.streaming.SXSSFCell;\nimport org.apache.poi.xssf.streaming.SXSSFDrawing;\nimport org.apache.poi.xssf.streaming.SXSSFRow;\nimport org.apache.poi.xssf.streaming.SXSSFSheet;\nimport org.apache.poi.xssf.streaming.SXSSFWorkbook;\nimport org.apache.poi.xssf.usermodel.XSSFClientAnchor;\nimport org.apache.poi.xssf.usermodel.XSSFRichTextString;\n\n/**\n * @author wuwenze\n */\npublic class ExcelXlsxWriter {\n\n  private final ExcelMapping mExcelMapping;\n  private final Integer mMaxSheetRecords;\n\n  public ExcelXlsxWriter(ExcelMapping excelMapping, Integer maxSheetRecords) {\n    mExcelMapping = excelMapping;\n    mMaxSheetRecords = maxSheetRecords;\n  }\n\n  /**\n   * 构建xlsxWorkbook对象\n   *\n   * @param data 数据集\n   * @param isTemplate 是否是导出模板\n   * @return SXSSFWorkbook\n   */\n  public SXSSFWorkbook generateXlsxWorkbook(List<?> data, boolean isTemplate) {\n    SXSSFWorkbook workbook = POIUtil.newSXSSFWorkbook();\n    List<ExcelProperty> propertyList = mExcelMapping.getPropertyList();\n    double sheetNo = Math.ceil(data.size() / (double) mMaxSheetRecords);\n    for (int index = 0; index <= (sheetNo == 0.0 ? sheetNo : sheetNo - 1); index++) {\n      String sheetName = mExcelMapping.getName() + (index == 0 ? \"\" : \"_\" + index);\n      SXSSFSheet sheet = generateXlsxHeader(workbook, propertyList, sheetName, isTemplate);\n      if (null != data && data.size() > 0) {\n        int startNo = index * mMaxSheetRecords;\n        int endNo = Math.min(startNo + mMaxSheetRecords, data.size());\n        for (int i = startNo; i < endNo; i++) {\n          SXSSFRow bodyRow = POIUtil.newSXSSFRow(sheet, i + 1 - startNo);\n          for (int j = 0; j < propertyList.size(); j++) {\n            SXSSFCell cell = POIUtil.newSXSSFCell(bodyRow, j);\n            ExcelXlsxWriter.buildCellValueByExcelProperty(cell, data.get(i), propertyList.get(j));\n          }\n        }\n      }\n    }\n    return workbook;\n  }\n\n  private SXSSFSheet generateXlsxHeader(SXSSFWorkbook workbook,\n      List<ExcelProperty> propertyList,\n      String sheetName, boolean isTemplate) {\n    SXSSFDrawing sxssfDrawing = null;\n    SXSSFSheet sheet = POIUtil.newSXSSFSheet(workbook, sheetName);\n    SXSSFRow headerRow = POIUtil.newSXSSFRow(sheet, 0);\n\n    for (int i = 0; i < propertyList.size(); i++) {\n      ExcelProperty property = propertyList.get(i);\n      SXSSFCell cell = POIUtil.newSXSSFCell(headerRow, i);\n      POIUtil.setColumnWidth(sheet, i, property.getWidth(), property.getColumn());\n      if (isTemplate) {\n        // cell range\n        POIUtil.setColumnCellRange(sheet, property.getOptions(), 1, mMaxSheetRecords, i, i);\n        // cell comment.\n        if (null == sxssfDrawing) {\n          sxssfDrawing = sheet.createDrawingPatriarch();\n        }\n        if (!ValidatorUtil.isEmpty(property.getComment())) {\n          // int col1, int row1, int col2, int row2\n          Comment cellComment = sxssfDrawing.createCellComment(//\n              new XSSFClientAnchor(0, 0, 0, 0, i, 0,i+2, i+2));\n          XSSFRichTextString xssfRichTextString = new XSSFRichTextString(\n              property.getComment());\n          Font commentFormatter = workbook.createFont();\n          xssfRichTextString.applyFont(commentFormatter);\n          cellComment.setString(xssfRichTextString);\n          cell.setCellComment(cellComment);\n        }\n      }\n      cell.setCellStyle(getHeaderCellStyle(workbook));\n      String headerColumnValue = property.getColumn();\n      if (isTemplate && null != property.getRequired() && property.getRequired()) {\n        headerColumnValue = (headerColumnValue + \"[*]\");\n      }\n      cell.setCellValue(headerColumnValue);\n    }\n    return sheet;\n  }\n\n  private static void buildCellValueByExcelProperty(SXSSFCell cell, Object entity,\n      ExcelProperty property) {\n    Object cellValue;\n    try {\n      cellValue = BeanUtils.getProperty(entity, property.getName());\n    } catch (Throwable e) {\n      throw new ExcelKitRuntimeException(e);\n    }\n    if (null != cellValue) {\n      String dateFormat = property.getDateFormat();\n      if (!ValidatorUtil.isEmpty(dateFormat)) {\n        if (cellValue instanceof Date) {\n          cell.setCellValue(DateUtil.format(dateFormat, (Date) cellValue));\n        } else if (cellValue instanceof String) {\n          try {\n            Date parse = DateUtil.ENGLISH_LOCAL_DF.parse((String) cellValue);\n            cell.setCellValue(DateUtil.format(dateFormat, parse));\n          } catch (ParseException e) {\n            e.printStackTrace();\n          }\n          return;\n        }\n      }\n      // writeConverterExp && writeConverter\n      String writeConverterExp = property.getWriteConverterExp();\n      WriteConverter writeConverter = property.getWriteConverter();\n      if (!ValidatorUtil.isEmpty(writeConverterExp)) {\n        try {\n          cellValue = POIUtil.convertByExp(cellValue, writeConverterExp);\n        } catch (Throwable e) {\n          throw new ExcelKitRuntimeException(e);\n        }\n      } else if (null != writeConverter) {\n        cell.setCellValue(writeConverter.convert(cellValue));\n        return;\n      }\n      cell.setCellValue(String.valueOf(cellValue));\n    }\n  }\n\n  private CellStyle mHeaderCellStyle = null;\n\n\n  public CellStyle getHeaderCellStyle(SXSSFWorkbook wb) {\n    if (null == mHeaderCellStyle) {\n      mHeaderCellStyle = wb.createCellStyle();\n      Font font = wb.createFont();\n      mHeaderCellStyle.setFillForegroundColor((short) 12);\n      mHeaderCellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);\n      mHeaderCellStyle.setBorderTop(BorderStyle.DOTTED);\n      mHeaderCellStyle.setBorderRight(BorderStyle.DOTTED);\n      mHeaderCellStyle.setBorderBottom(BorderStyle.DOTTED);\n      mHeaderCellStyle.setBorderLeft(BorderStyle.DOTTED);\n      mHeaderCellStyle.setAlignment(HorizontalAlignment.LEFT);// 对齐\n      mHeaderCellStyle.setFillForegroundColor(HSSFColor.HSSFColorPredefined.GREEN.getIndex());\n      mHeaderCellStyle.setFillBackgroundColor(HSSFColor.HSSFColorPredefined.GREEN.getIndex());\n      font.setColor(HSSFColor.HSSFColorPredefined.WHITE.getIndex());\n      // 应用标题字体到标题样式\n      mHeaderCellStyle.setFont(font);\n      //设置单元格文本形式\n      DataFormat dataFormat = wb.createDataFormat();\n      mHeaderCellStyle.setDataFormat(dataFormat.getFormat(\"@\"));\n    }\n    return mHeaderCellStyle;\n  }\n}\n"
  }
]