Browse Source

Merge branch '2.1.x' into 2.1.x

2.1.x
Jiaju Zhuang 5 years ago committed by GitHub
parent
commit
6559d8c52b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 13
      README.md
  2. BIN
      img/readme/quickstart/fill/complexFill.png
  3. BIN
      img/readme/quickstart/fill/complexFillTemplate.png
  4. BIN
      img/readme/quickstart/fill/complexFillWithTable.png
  5. BIN
      img/readme/quickstart/fill/complexFillWithTableTemplate.png
  6. BIN
      img/readme/quickstart/fill/horizontalFill.png
  7. BIN
      img/readme/quickstart/fill/horizontalFillTemplate.png
  8. BIN
      img/readme/quickstart/fill/listFill.png
  9. BIN
      img/readme/quickstart/fill/listFillTemplate.png
  10. BIN
      img/readme/quickstart/fill/simpleFill.png
  11. BIN
      img/readme/quickstart/fill/simpleFillTemplate.png
  12. BIN
      img/readme/quickstart/read/demo.png
  13. BIN
      img/readme/quickstart/write/complexHeadWrite.png
  14. BIN
      img/readme/quickstart/write/converterWrite.png
  15. BIN
      img/readme/quickstart/write/customHandlerWrite.png
  16. BIN
      img/readme/quickstart/write/dynamicHeadWrite.png
  17. BIN
      img/readme/quickstart/write/imageWrite.png
  18. BIN
      img/readme/quickstart/write/indexWrite.png
  19. BIN
      img/readme/quickstart/write/longestMatchColumnWidthWrite.png
  20. BIN
      img/readme/quickstart/write/mergeWrite.png
  21. BIN
      img/readme/quickstart/write/repeatedWrite.png
  22. BIN
      img/readme/quickstart/write/simpleWrite.png
  23. BIN
      img/readme/quickstart/write/styleWrite.png
  24. BIN
      img/readme/quickstart/write/tableWrite.png
  25. BIN
      img/readme/quickstart/write/templateWrite.png
  26. BIN
      img/readme/quickstart/write/widthAndHeightWrite.png
  27. BIN
      img/readme/wechat.png
  28. 18
      pom.xml
  29. 21
      src/main/java/com/alibaba/excel/ExcelReader.java
  30. 20
      src/main/java/com/alibaba/excel/ExcelWriter.java
  31. 40
      src/main/java/com/alibaba/excel/analysis/ExcelAnalyserImpl.java
  32. 12
      src/main/java/com/alibaba/excel/analysis/v03/XlsSaxAnalyser.java
  33. 2
      src/main/java/com/alibaba/excel/analysis/v03/handlers/BofRecordHandler.java
  34. 42
      src/main/java/com/alibaba/excel/analysis/v03/handlers/IndexRecordHandler.java
  35. 13
      src/main/java/com/alibaba/excel/analysis/v03/handlers/NumberRecordHandler.java
  36. 9
      src/main/java/com/alibaba/excel/analysis/v07/XlsxSaxAnalyser.java
  37. 2
      src/main/java/com/alibaba/excel/analysis/v07/handlers/CountRowCellHandler.java
  38. 17
      src/main/java/com/alibaba/excel/analysis/v07/handlers/DefaultCellHandler.java
  39. 379
      src/main/java/com/alibaba/excel/constant/BuiltinFormats.java
  40. 1
      src/main/java/com/alibaba/excel/context/AnalysisContext.java
  41. 5
      src/main/java/com/alibaba/excel/context/WriteContext.java
  42. 87
      src/main/java/com/alibaba/excel/context/WriteContextImpl.java
  43. 2
      src/main/java/com/alibaba/excel/converters/DefaultConverterLoader.java
  44. 2
      src/main/java/com/alibaba/excel/converters/byteconverter/ByteNumberConverter.java
  45. 2
      src/main/java/com/alibaba/excel/converters/floatconverter/FloatNumberConverter.java
  46. 2
      src/main/java/com/alibaba/excel/converters/integer/IntegerNumberConverter.java
  47. 2
      src/main/java/com/alibaba/excel/converters/shortconverter/ShortNumberConverter.java
  48. 12
      src/main/java/com/alibaba/excel/converters/string/StringNumberConverter.java
  49. 52
      src/main/java/com/alibaba/excel/converters/url/UrlImageConverter.java
  50. 2
      src/main/java/com/alibaba/excel/event/AnalysisEventListener.java
  51. 35
      src/main/java/com/alibaba/excel/exception/ExcelDataConvertException.java
  52. 12
      src/main/java/com/alibaba/excel/metadata/AbstractHolder.java
  53. 93
      src/main/java/com/alibaba/excel/metadata/AbstractParameterBuilder.java
  54. 13
      src/main/java/com/alibaba/excel/metadata/BasicParameter.java
  55. 11
      src/main/java/com/alibaba/excel/metadata/CellData.java
  56. 786
      src/main/java/com/alibaba/excel/metadata/DataFormatter.java
  57. 15
      src/main/java/com/alibaba/excel/metadata/GlobalConfiguration.java
  58. 95
      src/main/java/com/alibaba/excel/metadata/property/ExcelHeadProperty.java
  59. 47
      src/main/java/com/alibaba/excel/read/builder/AbstractExcelReaderParameterBuilder.java
  60. 115
      src/main/java/com/alibaba/excel/read/builder/ExcelReaderBuilder.java
  61. 105
      src/main/java/com/alibaba/excel/read/builder/ExcelReaderSheetBuilder.java
  62. 6
      src/main/java/com/alibaba/excel/read/listener/ModelBuildEventListener.java
  63. 36
      src/main/java/com/alibaba/excel/read/metadata/ReadWorkbook.java
  64. 23
      src/main/java/com/alibaba/excel/read/metadata/holder/AbstractReadHolder.java
  65. 28
      src/main/java/com/alibaba/excel/read/metadata/holder/ReadSheetHolder.java
  66. 22
      src/main/java/com/alibaba/excel/read/metadata/holder/ReadWorkbookHolder.java
  67. 5
      src/main/java/com/alibaba/excel/read/metadata/property/ExcelReadHeadProperty.java
  68. 150
      src/main/java/com/alibaba/excel/util/ClassUtils.java
  69. 30
      src/main/java/com/alibaba/excel/util/ConverterUtils.java
  70. 218
      src/main/java/com/alibaba/excel/util/DateUtils.java
  71. 110
      src/main/java/com/alibaba/excel/util/FileUtils.java
  72. 46
      src/main/java/com/alibaba/excel/util/NumberDataFormatterUtils.java
  73. 17
      src/main/java/com/alibaba/excel/util/WorkBookUtil.java
  74. 5
      src/main/java/com/alibaba/excel/write/ExcelBuilder.java
  75. 31
      src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java
  76. 104
      src/main/java/com/alibaba/excel/write/builder/AbstractExcelWriterParameterBuilder.java
  77. 103
      src/main/java/com/alibaba/excel/write/builder/ExcelWriterBuilder.java
  78. 92
      src/main/java/com/alibaba/excel/write/builder/ExcelWriterSheetBuilder.java
  79. 91
      src/main/java/com/alibaba/excel/write/builder/ExcelWriterTableBuilder.java
  80. 23
      src/main/java/com/alibaba/excel/write/executor/AbstractExcelWriteExecutor.java
  81. 26
      src/main/java/com/alibaba/excel/write/executor/ExcelWriteAddExecutor.java
  82. 164
      src/main/java/com/alibaba/excel/write/executor/ExcelWriteFillExecutor.java
  83. 2
      src/main/java/com/alibaba/excel/write/merge/AbstractMergeStrategy.java
  84. 29
      src/main/java/com/alibaba/excel/write/merge/LoopMergeStrategy.java
  85. 4
      src/main/java/com/alibaba/excel/write/merge/OnceAbsoluteMergeStrategy.java
  86. 62
      src/main/java/com/alibaba/excel/write/metadata/WriteBasicParameter.java
  87. 28
      src/main/java/com/alibaba/excel/write/metadata/WriteWorkbook.java
  88. 9
      src/main/java/com/alibaba/excel/write/metadata/fill/AnalysisCell.java
  89. 125
      src/main/java/com/alibaba/excel/write/metadata/holder/AbstractWriteHolder.java
  90. 16
      src/main/java/com/alibaba/excel/write/metadata/holder/WriteHolder.java
  91. 5
      src/main/java/com/alibaba/excel/write/metadata/holder/WriteSheetHolder.java
  92. 43
      src/main/java/com/alibaba/excel/write/metadata/holder/WriteWorkbookHolder.java
  93. 5
      src/main/java/com/alibaba/excel/write/property/ExcelWriteHeadProperty.java
  94. 8
      src/main/java/com/alibaba/excel/write/style/column/AbstractHeadColumnWidthStyleStrategy.java
  95. 9
      src/main/java/com/alibaba/excel/write/style/column/LongestMatchColumnWidthStyleStrategy.java
  96. 2
      src/main/java/com/alibaba/excel/write/style/column/SimpleColumnWidthStyleStrategy.java
  97. 16
      src/test/java/com/alibaba/easyexcel/test/core/dataformat/DateFormatData.java
  98. 61
      src/test/java/com/alibaba/easyexcel/test/core/dataformat/DateFormatTest.java
  99. 3
      src/test/java/com/alibaba/easyexcel/test/core/fill/FillData.java
  100. 18
      src/test/java/com/alibaba/easyexcel/test/core/fill/FillDataTest.java
  101. Some files were not shown because too many files have changed in this diff Show More

13
README.md

@ -30,7 +30,7 @@ DEMO代码地址:[https://github.com/alibaba/easyexcel/blob/master/src/test/ja
/** /**
* 最简单的读 * 最简单的读
* <p>1. 创建excel对应的实体对象 参照{@link DemoData} * <p>1. 创建excel对应的实体对象 参照{@link DemoData}
* <p>2. 由于默认异步读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoDataListener} * <p>2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoDataListener}
* <p>3. 直接读即可 * <p>3. 直接读即可
*/ */
@Test @Test
@ -62,29 +62,32 @@ DEMO代码地址:[https://github.com/alibaba/easyexcel/blob/master/src/test/ja
DEMO代码地址:[https://github.com/alibaba/easyexcel/blob/master/src/test/java/com/alibaba/easyexcel/test/demo/web/WebTest.java](/src/test/java/com/alibaba/easyexcel/test/demo/web/WebTest.java) DEMO代码地址:[https://github.com/alibaba/easyexcel/blob/master/src/test/java/com/alibaba/easyexcel/test/demo/web/WebTest.java](/src/test/java/com/alibaba/easyexcel/test/demo/web/WebTest.java)
```java ```java
/** /**
* 文件下载 * 文件下载(失败了会返回一个有部分数据的Excel)
* <p>1. 创建excel对应的实体对象 参照{@link DownloadData} * <p>1. 创建excel对应的实体对象 参照{@link DownloadData}
* <p>2. 设置返回的 参数 * <p>2. 设置返回的 参数
* <p>3. 直接写,这里注意,finish的时候会自动关闭OutputStream,当然你外面再关闭流问题不大 * <p>3. 直接写,这里注意,finish的时候会自动关闭OutputStream,当然你外面再关闭流问题不大
*/ */
@GetMapping("download") @GetMapping("download")
public void download(HttpServletResponse response) throws IOException { public void download(HttpServletResponse response) throws IOException {
// 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman
response.setContentType("application/vnd.ms-excel"); response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8"); response.setCharacterEncoding("utf-8");
response.setHeader("Content-disposition", "attachment;filename=demo.xlsx"); // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
String fileName = URLEncoder.encode("测试", "UTF-8");
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
EasyExcel.write(response.getOutputStream(), DownloadData.class).sheet("模板").doWrite(data()); EasyExcel.write(response.getOutputStream(), DownloadData.class).sheet("模板").doWrite(data());
} }
/** /**
* 文件上传 * 文件上传
* <p>1. 创建excel对应的实体对象 参照{@link UploadData} * <p>1. 创建excel对应的实体对象 参照{@link UploadData}
* <p>2. 由于默认异步读取excel,所以需要创建excel一行一行的回调监听器,参照{@link UploadDataListener} * <p>2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link UploadDataListener}
* <p>3. 直接读即可 * <p>3. 直接读即可
*/ */
@PostMapping("upload") @PostMapping("upload")
@ResponseBody @ResponseBody
public String upload(MultipartFile file) throws IOException { public String upload(MultipartFile file) throws IOException {
EasyExcel.read(file.getInputStream(), UploadData.class, new UploadDataListener()).sheet().doRead(); EasyExcel.read(file.getInputStream(), UploadData.class, new UploadDataListener(uploadDAO)).sheet().doRead();
return "success"; return "success";
} }
``` ```

BIN
img/readme/quickstart/fill/complexFill.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

BIN
img/readme/quickstart/fill/complexFillTemplate.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

BIN
img/readme/quickstart/fill/complexFillWithTable.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

BIN
img/readme/quickstart/fill/complexFillWithTableTemplate.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

BIN
img/readme/quickstart/fill/horizontalFill.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

BIN
img/readme/quickstart/fill/horizontalFillTemplate.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

BIN
img/readme/quickstart/fill/listFill.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

BIN
img/readme/quickstart/fill/listFillTemplate.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 811 B

BIN
img/readme/quickstart/fill/simpleFill.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

BIN
img/readme/quickstart/fill/simpleFillTemplate.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

BIN
img/readme/quickstart/read/demo.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

BIN
img/readme/quickstart/write/complexHeadWrite.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

BIN
img/readme/quickstart/write/converterWrite.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

BIN
img/readme/quickstart/write/customHandlerWrite.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

BIN
img/readme/quickstart/write/dynamicHeadWrite.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

BIN
img/readme/quickstart/write/imageWrite.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

BIN
img/readme/quickstart/write/indexWrite.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

BIN
img/readme/quickstart/write/longestMatchColumnWidthWrite.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

BIN
img/readme/quickstart/write/mergeWrite.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

BIN
img/readme/quickstart/write/repeatedWrite.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

BIN
img/readme/quickstart/write/simpleWrite.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

BIN
img/readme/quickstart/write/styleWrite.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

BIN
img/readme/quickstart/write/tableWrite.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

BIN
img/readme/quickstart/write/templateWrite.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

BIN
img/readme/quickstart/write/widthAndHeightWrite.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

BIN
img/readme/wechat.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

18
pom.xml

@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>com.alibaba</groupId> <groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId> <artifactId>easyexcel</artifactId>
<version>2.1.0-beta1</version> <version>2.1.4</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>easyexcel</name> <name>easyexcel</name>
@ -17,7 +17,7 @@
<properties> <properties>
<!--<gpg.skip>true</gpg.skip>--> <!--<gpg.skip>true</gpg.skip>-->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<jdk.version>1.7</jdk.version> <jdk.version>1.6</jdk.version>
</properties> </properties>
<scm> <scm>
@ -66,6 +66,11 @@
<artifactId>poi-ooxml</artifactId> <artifactId>poi-ooxml</artifactId>
<version>3.17</version> <version>3.17</version>
</dependency> </dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-schemas</artifactId>
<version>3.17</version>
</dependency>
<dependency> <dependency>
<groupId>cglib</groupId> <groupId>cglib</groupId>
<artifactId>cglib</artifactId> <artifactId>cglib</artifactId>
@ -79,7 +84,7 @@
<dependency> <dependency>
<groupId>org.ehcache</groupId> <groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId> <artifactId>ehcache</artifactId>
<version>3.7.1</version> <version>3.4.0</version>
</dependency> </dependency>
<!--test--> <!--test-->
<dependency> <dependency>
@ -132,11 +137,11 @@
<build> <build>
<plugins> <plugins>
<!-- 用pmd校验阿里的p3c规范 --> <!-- code style -->
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-pmd-plugin</artifactId> <artifactId>maven-pmd-plugin</artifactId>
<version>3.12.0</version> <version>3.8</version>
<configuration> <configuration>
<printFailingErrors>true</printFailingErrors> <printFailingErrors>true</printFailingErrors>
<verbose>true</verbose> <verbose>true</verbose>
@ -157,7 +162,6 @@
</excludes> </excludes>
</configuration> </configuration>
<executions> <executions>
<!-- 绑定pmd:check到verify生命周期 -->
<execution> <execution>
<id>pmd-check-verify</id> <id>pmd-check-verify</id>
<phase>validate</phase> <phase>validate</phase>
@ -170,7 +174,7 @@
<dependency> <dependency>
<groupId>com.alibaba.p3c</groupId> <groupId>com.alibaba.p3c</groupId>
<artifactId>p3c-pmd</artifactId> <artifactId>p3c-pmd</artifactId>
<version>2.0.0</version> <version>1.3.6</version>
</dependency> </dependency>
</dependencies> </dependencies>
</plugin> </plugin>

21
src/main/java/com/alibaba/excel/ExcelReader.java

@ -14,7 +14,6 @@ import com.alibaba.excel.analysis.ExcelReadExecutor;
import com.alibaba.excel.cache.MapCache; import com.alibaba.excel.cache.MapCache;
import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener; import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelAnalysisException;
import com.alibaba.excel.metadata.Sheet; import com.alibaba.excel.metadata.Sheet;
import com.alibaba.excel.parameter.AnalysisParam; import com.alibaba.excel.parameter.AnalysisParam;
import com.alibaba.excel.read.listener.ReadListener; import com.alibaba.excel.read.listener.ReadListener;
@ -35,8 +34,6 @@ public class ExcelReader {
*/ */
private ExcelAnalyser excelAnalyser; private ExcelAnalyser excelAnalyser;
private boolean finished = false;
/** /**
* Create new reader * Create new reader
* *
@ -160,7 +157,6 @@ public class ExcelReader {
* Parse all sheet content by default * Parse all sheet content by default
*/ */
public void readAll() { public void readAll() {
checkFinished();
excelAnalyser.analysis(null, Boolean.TRUE); excelAnalyser.analysis(null, Boolean.TRUE);
} }
@ -181,7 +177,6 @@ public class ExcelReader {
* @return * @return
*/ */
public ExcelReader read(List<ReadSheet> readSheetList) { public ExcelReader read(List<ReadSheet> readSheetList) {
checkFinished();
excelAnalyser.analysis(readSheetList, Boolean.FALSE); excelAnalyser.analysis(readSheetList, Boolean.FALSE);
return this; return this;
} }
@ -231,7 +226,6 @@ public class ExcelReader {
* @return * @return
*/ */
public AnalysisContext analysisContext() { public AnalysisContext analysisContext() {
checkFinished();
return excelAnalyser.analysisContext(); return excelAnalyser.analysisContext();
} }
@ -241,7 +235,6 @@ public class ExcelReader {
* @return * @return
*/ */
public ExcelReadExecutor excelExecutor() { public ExcelReadExecutor excelExecutor() {
checkFinished();
return excelAnalyser.excelExecutor(); return excelAnalyser.excelExecutor();
} }
@ -281,10 +274,6 @@ public class ExcelReader {
* Complete the entire read file.Release the cache and close stream. * Complete the entire read file.Release the cache and close stream.
*/ */
public void finish() { public void finish() {
if (finished) {
return;
}
finished = true;
excelAnalyser.finish(); excelAnalyser.finish();
} }
@ -294,19 +283,11 @@ public class ExcelReader {
*/ */
@Override @Override
protected void finalize() { protected void finalize() {
if (finished) {
return;
}
try { try {
excelAnalyser.finish(); finish();
} catch (Throwable e) { } catch (Throwable e) {
LOGGER.warn("Destroy object failed", e); LOGGER.warn("Destroy object failed", e);
} }
} }
private void checkFinished() {
if (finished) {
throw new ExcelAnalysisException("Can not use a finished reader.");
}
}
} }

20
src/main/java/com/alibaba/excel/ExcelWriter.java

@ -5,6 +5,9 @@ import java.io.OutputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.excel.context.WriteContext; import com.alibaba.excel.context.WriteContext;
import com.alibaba.excel.metadata.Sheet; import com.alibaba.excel.metadata.Sheet;
import com.alibaba.excel.metadata.Table; import com.alibaba.excel.metadata.Table;
@ -31,6 +34,8 @@ import com.alibaba.excel.write.metadata.fill.FillConfig;
* @author jipengfei * @author jipengfei
*/ */
public class ExcelWriter { public class ExcelWriter {
private static final Logger LOGGER = LoggerFactory.getLogger(ExcelWriter.class);
private ExcelBuilder excelBuilder; private ExcelBuilder excelBuilder;
/** /**
@ -320,7 +325,20 @@ public class ExcelWriter {
* Close IO * Close IO
*/ */
public void finish() { public void finish() {
excelBuilder.finish(); excelBuilder.finish(false);
}
/**
* Prevents calls to {@link #finish} from freeing the cache
*
*/
@Override
protected void finalize() {
try {
finish();
} catch (Throwable e) {
LOGGER.warn("Destroy object failed", e);
}
} }
/** /**

40
src/main/java/com/alibaba/excel/analysis/ExcelAnalyserImpl.java

@ -1,13 +1,10 @@
package com.alibaba.excel.analysis; package com.alibaba.excel.analysis;
import java.io.FileInputStream;
import java.io.InputStream; import java.io.InputStream;
import java.util.List; import java.util.List;
import org.apache.poi.hssf.record.crypto.Biff8EncryptionKey; import org.apache.poi.hssf.record.crypto.Biff8EncryptionKey;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.poifs.crypt.Decryptor; import org.apache.poi.poifs.crypt.Decryptor;
import org.apache.poi.poifs.crypt.EncryptionInfo;
import org.apache.poi.poifs.filesystem.DocumentFactoryHelper; import org.apache.poi.poifs.filesystem.DocumentFactoryHelper;
import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.util.IOUtils; import org.apache.poi.util.IOUtils;
@ -25,7 +22,9 @@ import com.alibaba.excel.read.metadata.ReadWorkbook;
import com.alibaba.excel.read.metadata.holder.ReadWorkbookHolder; import com.alibaba.excel.read.metadata.holder.ReadWorkbookHolder;
import com.alibaba.excel.support.ExcelTypeEnum; import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.util.CollectionUtils; import com.alibaba.excel.util.CollectionUtils;
import com.alibaba.excel.util.DateUtils;
import com.alibaba.excel.util.FileUtils; import com.alibaba.excel.util.FileUtils;
import com.alibaba.excel.util.NumberDataFormatterUtils;
import com.alibaba.excel.util.StringUtils; import com.alibaba.excel.util.StringUtils;
/** /**
@ -37,6 +36,10 @@ public class ExcelAnalyserImpl implements ExcelAnalyser {
private AnalysisContext analysisContext; private AnalysisContext analysisContext;
private ExcelReadExecutor excelReadExecutor; private ExcelReadExecutor excelReadExecutor;
/**
* Prevent multiple shutdowns
*/
private boolean finished = false;
public ExcelAnalyserImpl(ReadWorkbook readWorkbook) { public ExcelAnalyserImpl(ReadWorkbook readWorkbook) {
try { try {
@ -124,31 +127,37 @@ public class ExcelAnalyserImpl implements ExcelAnalyser {
@Override @Override
public void finish() { public void finish() {
if (finished) {
return;
}
finished = true;
if (analysisContext == null || analysisContext.readWorkbookHolder() == null) { if (analysisContext == null || analysisContext.readWorkbookHolder() == null) {
return; return;
} }
ReadWorkbookHolder readWorkbookHolder = analysisContext.readWorkbookHolder(); ReadWorkbookHolder readWorkbookHolder = analysisContext.readWorkbookHolder();
Throwable throwable = null;
try { try {
if (readWorkbookHolder.getReadCache() != null) { if (readWorkbookHolder.getReadCache() != null) {
readWorkbookHolder.getReadCache().destroy(); readWorkbookHolder.getReadCache().destroy();
} }
} catch (Throwable t) { } catch (Throwable t) {
throwCanNotCloseIo(t); throwable = t;
} }
try { try {
if (readWorkbookHolder.getOpcPackage() != null) { if (readWorkbookHolder.getOpcPackage() != null) {
readWorkbookHolder.getOpcPackage().revert(); readWorkbookHolder.getOpcPackage().revert();
} }
} catch (Throwable t) { } catch (Throwable t) {
throwCanNotCloseIo(t); throwable = t;
} }
try { try {
if (readWorkbookHolder.getPoifsFileSystem() != null) { if (readWorkbookHolder.getPoifsFileSystem() != null) {
readWorkbookHolder.getPoifsFileSystem().close(); readWorkbookHolder.getPoifsFileSystem().close();
} }
} catch (Throwable t) { } catch (Throwable t) {
throwCanNotCloseIo(t); throwable = t;
} }
try { try {
if (analysisContext.readWorkbookHolder().getAutoCloseStream() if (analysisContext.readWorkbookHolder().getAutoCloseStream()
@ -156,17 +165,28 @@ public class ExcelAnalyserImpl implements ExcelAnalyser {
readWorkbookHolder.getInputStream().close(); readWorkbookHolder.getInputStream().close();
} }
} catch (Throwable t) { } catch (Throwable t) {
throwCanNotCloseIo(t); throwable = t;
} }
try { try {
if (readWorkbookHolder.getTempFile() != null) { if (readWorkbookHolder.getTempFile() != null) {
FileUtils.delete(readWorkbookHolder.getTempFile()); FileUtils.delete(readWorkbookHolder.getTempFile());
} }
} catch (Throwable t) { } catch (Throwable t) {
throwCanNotCloseIo(t); throwable = t;
} }
clearEncrypt03(); clearEncrypt03();
removeThreadLocalCache();
if (throwable != null) {
throw new ExcelAnalysisException("Can not close IO.", throwable);
}
}
private void removeThreadLocalCache() {
NumberDataFormatterUtils.removeThreadLocalCache();
DateUtils.removeThreadLocalCache();
} }
private void clearEncrypt03() { private void clearEncrypt03() {
@ -177,10 +197,6 @@ public class ExcelAnalyserImpl implements ExcelAnalyser {
Biff8EncryptionKey.setCurrentUserPassword(null); Biff8EncryptionKey.setCurrentUserPassword(null);
} }
private void throwCanNotCloseIo(Throwable t) {
throw new ExcelAnalysisException("Can not close IO", t);
}
@Override @Override
public ExcelReadExecutor excelExecutor() { public ExcelReadExecutor excelExecutor() {
return excelReadExecutor; return excelReadExecutor;

12
src/main/java/com/alibaba/excel/analysis/v03/XlsSaxAnalyser.java

@ -4,9 +4,9 @@ import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.TreeMap;
import com.alibaba.excel.util.StringUtils; import com.alibaba.excel.util.StringUtils;
import org.apache.poi.hssf.eventusermodel.EventWorkbookBuilder; import org.apache.poi.hssf.eventusermodel.EventWorkbookBuilder;
@ -33,6 +33,7 @@ import com.alibaba.excel.analysis.ExcelReadExecutor;
import com.alibaba.excel.analysis.v03.handlers.BlankOrErrorRecordHandler; import com.alibaba.excel.analysis.v03.handlers.BlankOrErrorRecordHandler;
import com.alibaba.excel.analysis.v03.handlers.BofRecordHandler; import com.alibaba.excel.analysis.v03.handlers.BofRecordHandler;
import com.alibaba.excel.analysis.v03.handlers.FormulaRecordHandler; import com.alibaba.excel.analysis.v03.handlers.FormulaRecordHandler;
import com.alibaba.excel.analysis.v03.handlers.IndexRecordHandler;
import com.alibaba.excel.analysis.v03.handlers.LabelRecordHandler; import com.alibaba.excel.analysis.v03.handlers.LabelRecordHandler;
import com.alibaba.excel.analysis.v03.handlers.MissingCellDummyRecordHandler; import com.alibaba.excel.analysis.v03.handlers.MissingCellDummyRecordHandler;
import com.alibaba.excel.analysis.v03.handlers.NoteRecordHandler; import com.alibaba.excel.analysis.v03.handlers.NoteRecordHandler;
@ -86,7 +87,7 @@ public class XlsSaxAnalyser implements HSSFListener, ExcelReadExecutor {
public XlsSaxAnalyser(AnalysisContext context, POIFSFileSystem poifsFileSystem) { public XlsSaxAnalyser(AnalysisContext context, POIFSFileSystem poifsFileSystem) {
this.analysisContext = context; this.analysisContext = context;
this.records = new TreeMap<Integer, CellData>(); this.records = new LinkedHashMap<Integer, CellData>();
this.poifsFileSystem = poifsFileSystem; this.poifsFileSystem = poifsFileSystem;
try { try {
this.poiWorkbook = WorkbookFactory.create(poifsFileSystem); this.poiWorkbook = WorkbookFactory.create(poifsFileSystem);
@ -134,7 +135,7 @@ public class XlsSaxAnalyser implements HSSFListener, ExcelReadExecutor {
private void init() { private void init() {
lastRowNumber = 0; lastRowNumber = 0;
lastColumnNumber = 0; lastColumnNumber = 0;
records = new TreeMap<Integer, CellData>(); records = new LinkedHashMap<Integer, CellData>();
buildXlsRecordHandlers(); buildXlsRecordHandlers();
} }
@ -227,7 +228,7 @@ public class XlsSaxAnalyser implements HSSFListener, ExcelReadExecutor {
analysisContext.readRowHolder().setRowComments(rowComments); analysisContext.readRowHolder().setRowComments(rowComments);
} }
analysisContext.readSheetHolder().notifyEndOneRow(new EachRowAnalysisFinishEvent(records), analysisContext); analysisContext.readSheetHolder().notifyEndOneRow(new EachRowAnalysisFinishEvent(records), analysisContext);
records.clear(); records = new HashMap<Integer, CellData>();
lastColumnNumber = -1; lastColumnNumber = -1;
} }
@ -244,10 +245,11 @@ public class XlsSaxAnalyser implements HSSFListener, ExcelReadExecutor {
recordHandlers.add(new FormulaRecordHandler(stubWorkbook, formatListener)); recordHandlers.add(new FormulaRecordHandler(stubWorkbook, formatListener));
recordHandlers.add(new LabelRecordHandler()); recordHandlers.add(new LabelRecordHandler());
recordHandlers.add(new NoteRecordHandler()); recordHandlers.add(new NoteRecordHandler());
recordHandlers.add(new NumberRecordHandler(formatListener)); recordHandlers.add(new NumberRecordHandler(analysisContext, formatListener));
recordHandlers.add(new RkRecordHandler()); recordHandlers.add(new RkRecordHandler());
recordHandlers.add(new SstRecordHandler()); recordHandlers.add(new SstRecordHandler());
recordHandlers.add(new MissingCellDummyRecordHandler()); recordHandlers.add(new MissingCellDummyRecordHandler());
recordHandlers.add(new IndexRecordHandler(analysisContext));
Collections.sort(recordHandlers); Collections.sort(recordHandlers);
} }

2
src/main/java/com/alibaba/excel/analysis/v03/handlers/BofRecordHandler.java

@ -73,7 +73,7 @@ public class BofRecordHandler extends AbstractXlsRecordHandler {
readSheet = SheetUtils.match(readSheet, readSheetList, readAll, readSheet = SheetUtils.match(readSheet, readSheetList, readAll,
context.readWorkbookHolder().getGlobalConfiguration()); context.readWorkbookHolder().getGlobalConfiguration());
if (readSheet != null) { if (readSheet != null) {
if (readSheet.getSheetNo() != 0) { if (readSheet.getSheetNo() != 0 && context.readSheetHolder() != null) {
// Prompt for the end of the previous form read // Prompt for the end of the previous form read
context.readSheetHolder().notifyAfterAllAnalysed(context); context.readSheetHolder().notifyAfterAllAnalysed(context);
} }

42
src/main/java/com/alibaba/excel/analysis/v03/handlers/IndexRecordHandler.java

@ -0,0 +1,42 @@
package com.alibaba.excel.analysis.v03.handlers;
import org.apache.poi.hssf.record.IndexRecord;
import org.apache.poi.hssf.record.Record;
import com.alibaba.excel.analysis.v03.AbstractXlsRecordHandler;
import com.alibaba.excel.context.AnalysisContext;
/**
* Record handler
*
* @author Jiaju Zhuang
*/
public class IndexRecordHandler extends AbstractXlsRecordHandler {
private AnalysisContext context;
public IndexRecordHandler(AnalysisContext context) {
this.context = context;
}
@Override
public boolean support(Record record) {
return record instanceof IndexRecord;
}
@Override
public void init() {}
@Override
public void processRecord(Record record) {
if (context.readSheetHolder() == null) {
return;
}
context.readSheetHolder().setApproximateTotalRowNumber(((IndexRecord)record).getLastRowAdd1());
}
@Override
public int getOrder() {
return 1;
}
}

13
src/main/java/com/alibaba/excel/analysis/v03/handlers/NumberRecordHandler.java

@ -7,6 +7,8 @@ import org.apache.poi.hssf.record.NumberRecord;
import org.apache.poi.hssf.record.Record; import org.apache.poi.hssf.record.Record;
import com.alibaba.excel.analysis.v03.AbstractXlsRecordHandler; import com.alibaba.excel.analysis.v03.AbstractXlsRecordHandler;
import com.alibaba.excel.constant.BuiltinFormats;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.metadata.CellData; import com.alibaba.excel.metadata.CellData;
/** /**
@ -17,7 +19,10 @@ import com.alibaba.excel.metadata.CellData;
public class NumberRecordHandler extends AbstractXlsRecordHandler { public class NumberRecordHandler extends AbstractXlsRecordHandler {
private FormatTrackingHSSFListener formatListener; private FormatTrackingHSSFListener formatListener;
public NumberRecordHandler(FormatTrackingHSSFListener formatListener) { private AnalysisContext context;
public NumberRecordHandler(AnalysisContext context, FormatTrackingHSSFListener formatListener) {
this.context = context;
this.formatListener = formatListener; this.formatListener = formatListener;
} }
@ -32,8 +37,10 @@ public class NumberRecordHandler extends AbstractXlsRecordHandler {
this.row = numrec.getRow(); this.row = numrec.getRow();
this.column = numrec.getColumn(); this.column = numrec.getColumn();
this.cellData = new CellData(BigDecimal.valueOf(numrec.getValue())); this.cellData = new CellData(BigDecimal.valueOf(numrec.getValue()));
this.cellData.setDataFormat(formatListener.getFormatIndex(numrec)); int dataFormat = formatListener.getFormatIndex(numrec);
this.cellData.setDataFormatString(formatListener.getFormatString(numrec)); this.cellData.setDataFormat(dataFormat);
this.cellData.setDataFormatString(BuiltinFormats.getBuiltinFormat(dataFormat,
formatListener.getFormatString(numrec), context.readSheetHolder().getGlobalConfiguration().getLocale()));
} }
@Override @Override

9
src/main/java/com/alibaba/excel/analysis/v07/XlsxSaxAnalyser.java

@ -15,7 +15,6 @@ import javax.xml.parsers.SAXParserFactory;
import org.apache.poi.openxml4j.opc.OPCPackage; import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.openxml4j.opc.PackageAccess; import org.apache.poi.openxml4j.opc.PackageAccess;
import org.apache.poi.openxml4j.opc.PackagePart; import org.apache.poi.openxml4j.opc.PackagePart;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.eventusermodel.XSSFReader; import org.apache.poi.xssf.eventusermodel.XSSFReader;
import org.apache.poi.xssf.model.CommentsTable; import org.apache.poi.xssf.model.CommentsTable;
import org.apache.poi.xssf.model.StylesTable; import org.apache.poi.xssf.model.StylesTable;
@ -164,7 +163,13 @@ public class XlsxSaxAnalyser implements ExcelReadExecutor {
private void parseXmlSource(InputStream inputStream, ContentHandler handler) { private void parseXmlSource(InputStream inputStream, ContentHandler handler) {
InputSource inputSource = new InputSource(inputStream); InputSource inputSource = new InputSource(inputStream);
try { try {
SAXParserFactory saxFactory = SAXParserFactory.newInstance(); SAXParserFactory saxFactory;
String xlsxSAXParserFactoryName = analysisContext.readWorkbookHolder().getXlsxSAXParserFactoryName();
if (StringUtils.isEmpty(xlsxSAXParserFactoryName)) {
saxFactory = SAXParserFactory.newInstance();
} else {
saxFactory = SAXParserFactory.newInstance(xlsxSAXParserFactoryName, null);
}
saxFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); saxFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
saxFactory.setFeature("http://xml.org/sax/features/external-general-entities", false); saxFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
saxFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); saxFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);

2
src/main/java/com/alibaba/excel/analysis/v07/handlers/CountRowCellHandler.java

@ -31,7 +31,7 @@ public class CountRowCellHandler implements XlsxCellHandler {
String d = attributes.getValue(DIMENSION_REF); String d = attributes.getValue(DIMENSION_REF);
String totalStr = d.substring(d.indexOf(":") + 1, d.length()); String totalStr = d.substring(d.indexOf(":") + 1, d.length());
String c = totalStr.toUpperCase().replaceAll("[A-Z]", ""); String c = totalStr.toUpperCase().replaceAll("[A-Z]", "");
analysisContext.readSheetHolder().setTotal(Integer.parseInt(c)); analysisContext.readSheetHolder().setApproximateTotalRowNumber(Integer.parseInt(c));
} }
@Override @Override

17
src/main/java/com/alibaba/excel/analysis/v07/handlers/DefaultCellHandler.java

@ -9,11 +9,10 @@ import static com.alibaba.excel.constant.ExcelXmlConstants.CELL_VALUE_TYPE_TAG;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Deque; import java.util.Deque;
import java.util.LinkedHashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.Map; import java.util.Map;
import java.util.TreeMap;
import org.apache.poi.ss.usermodel.BuiltinFormats;
import org.apache.poi.xssf.model.StylesTable; import org.apache.poi.xssf.model.StylesTable;
import org.apache.poi.xssf.usermodel.XSSFCellStyle; import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFRichTextString; import org.apache.poi.xssf.usermodel.XSSFRichTextString;
@ -21,6 +20,7 @@ import org.xml.sax.Attributes;
import com.alibaba.excel.analysis.v07.XlsxCellHandler; import com.alibaba.excel.analysis.v07.XlsxCellHandler;
import com.alibaba.excel.analysis.v07.XlsxRowResultHolder; import com.alibaba.excel.analysis.v07.XlsxRowResultHolder;
import com.alibaba.excel.constant.BuiltinFormats;
import com.alibaba.excel.constant.ExcelXmlConstants; import com.alibaba.excel.constant.ExcelXmlConstants;
import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.enums.CellDataTypeEnum; import com.alibaba.excel.enums.CellDataTypeEnum;
@ -37,7 +37,7 @@ public class DefaultCellHandler implements XlsxCellHandler, XlsxRowResultHolder
private final AnalysisContext analysisContext; private final AnalysisContext analysisContext;
private Deque<String> currentTagDeque = new LinkedList<String>(); private Deque<String> currentTagDeque = new LinkedList<String>();
private int curCol; private int curCol;
private Map<Integer, CellData> curRowContent = new TreeMap<Integer, CellData>(); private Map<Integer, CellData> curRowContent = new LinkedHashMap<Integer, CellData>();
private CellData currentCellData; private CellData currentCellData;
private StringBuilder dataStringBuilder; private StringBuilder dataStringBuilder;
private StringBuilder formulaStringBuilder; private StringBuilder formulaStringBuilder;
@ -54,7 +54,7 @@ public class DefaultCellHandler implements XlsxCellHandler, XlsxRowResultHolder
@Override @Override
public void clearResult() { public void clearResult() {
curRowContent = new TreeMap<Integer, CellData>(); curRowContent = new LinkedHashMap<Integer, CellData>();
} }
@Override @Override
@ -87,13 +87,10 @@ public class DefaultCellHandler implements XlsxCellHandler, XlsxRowResultHolder
int dateFormatIndexInteger = Integer.parseInt(dateFormatIndex); int dateFormatIndexInteger = Integer.parseInt(dateFormatIndex);
XSSFCellStyle xssfCellStyle = stylesTable.getStyleAt(dateFormatIndexInteger); XSSFCellStyle xssfCellStyle = stylesTable.getStyleAt(dateFormatIndexInteger);
int dataFormat = xssfCellStyle.getDataFormat(); int dataFormat = xssfCellStyle.getDataFormat();
String dataFormatString = xssfCellStyle.getDataFormatString();
currentCellData.setDataFormat(dataFormat); currentCellData.setDataFormat(dataFormat);
if (dataFormatString == null) { currentCellData.setDataFormatString(
currentCellData.setDataFormatString(BuiltinFormats.getBuiltinFormat(dataFormat)); BuiltinFormats.getBuiltinFormat(dataFormat, xssfCellStyle.getDataFormatString(),
} else { analysisContext.readSheetHolder().getGlobalConfiguration().getLocale()));
currentCellData.setDataFormatString(dataFormatString);
}
} }
} }
// cell is formula // cell is formula

379
src/main/java/com/alibaba/excel/constant/BuiltinFormats.java

@ -0,0 +1,379 @@
package com.alibaba.excel.constant;
import java.util.Locale;
/**
* Excel's built-in format conversion.Currently only supports Chinese.
*
* <p>
* If it is not Chinese, it is recommended to directly modify the builtinFormats, which will better support
* internationalization in the future.
*
* <p>
* Specific correspondence please see:
* https://docs.microsoft.com/en-us/dotnet/api/documentformat.openxml.spreadsheet.numberingformat?view=openxml-2.8.1
*
* @author Jiaju Zhuang
**/
public class BuiltinFormats {
private static final String[] BUILTIN_FORMATS_CN = {
// 0
"General",
// 1
"0",
// 2
"0.00",
// 3
"#,##0",
// 4
"#,##0.00",
// 5
"\"¥\"#,##0_);(\"¥\"#,##0)",
// 6
"\"¥\"#,##0_);[Red](\"¥\"#,##0)",
// 7
"\"¥\"#,##0.00_);(\"¥\"#,##0.00)",
// 8
"\"¥\"#,##0.00_);[Red](\"¥\"#,##0.00)",
// 9
"0%",
// 10
"0.00%",
// 11
"0.00E+00",
// 12
"# ?/?",
// 13
"# ??/??",
// 14
// The official documentation shows "m/d/yy", but the actual test is "yyyy/m/d".
"yyyy/m/d",
// 15
"d-mmm-yy",
// 16
"d-mmm",
// 17
"mmm-yy",
// 18
"h:mm AM/PM",
// 19
"h:mm:ss AM/PM",
// 20
"h:mm",
// 21
"h:mm:ss",
// 22
// The official documentation shows "m/d/yy h:mm", but the actual test is "yyyy/m/d h:mm".
"yyyy/m/d h:mm",
// 23-26 No specific correspondence found in the official documentation.
// 23
null,
// 24
null,
// 25
null,
// 26
null,
// 27
"yyyy\"年\"m\"月\"",
// 28
"m\"月\"d\"日\"",
// 29
"m\"月\"d\"日\"",
// 30
"m-d-yy",
// 31
"yyyy\"年\"m\"月\"d\"日\"",
// 32
"h\"时\"mm\"分\"",
// 33
"h\"时\"mm\"分\"ss\"秒\"",
// 34
"上午/下午h\"时\"mm\"分\"",
// 35
"上午/下午h\"时\"mm\"分\"ss\"秒\"",
// 36
"yyyy\"年\"m\"月\"",
// 37
"#,##0_);(#,##0)",
// 38
"#,##0_);[Red](#,##0)",
// 39
"#,##0.00_);(#,##0.00)",
// 40
"#,##0.00_);[Red](#,##0.00)",
// 41
"_(* #,##0_);_(* (#,##0);_(* \"-\"_);_(@_)",
// 42
"_(\"¥\"* #,##0_);_(\"¥\"* (#,##0);_(\"¥\"* \"-\"_);_(@_)",
// 43
"_(* #,##0.00_);_(* (#,##0.00);_(* \"-\"??_);_(@_)",
// 44
"_(\"¥\"* #,##0.00_);_(\"¥\"* (#,##0.00);_(\"¥\"* \"-\"??_);_(@_)",
// 45
"mm:ss",
// 46
"[h]:mm:ss",
// 47
"mm:ss.0",
// 48
"##0.0E+0",
// 49
"@",
// 50
"yyyy\"年\"m\"月\"",
// 51
"m\"月\"d\"日\"",
// 52
"yyyy\"年\"m\"月\"",
// 53
"m\"月\"d\"日\"",
// 54
"m\"月\"d\"日\"",
// 55
"上午/下午h\"时\"mm\"分\"",
// 56
"上午/下午h\"时\"mm\"分\"ss\"秒\"",
// 57
"yyyy\"年\"m\"月\"",
// 58
"m\"月\"d\"日\"",
// 59
"t0",
// 60
"t0.00",
// 61
"t#,##0",
// 62
"t#,##0.00",
// 63-66 No specific correspondence found in the official documentation.
// 63
null,
// 64
null,
// 65
null,
// 66
null,
// 67
"t0%",
// 68
"t0.00%",
// 69
"t# ?/?",
// 70
"t# ??/??",
// 71
"ว/ด/ปปปป",
// 72
"ว-ดดด-ปป",
// 73
"ว-ดดด",
// 74
"ดดด-ปป",
// 75
"ช:นน",
// 76
"ช:นน:ทท",
// 77
"ว/ด/ปปปป ช:นน",
// 78
"นน:ทท",
// 79
"[ช]:นน:ทท",
// 80
"นน:ทท.0",
// 81
"d/m/bb",
// end
};
private static final String[] BUILTIN_FORMATS_US = {
// 0
"General",
// 1
"0",
// 2
"0.00",
// 3
"#,##0",
// 4
"#,##0.00",
// 5
"\"$\"#,##0_);(\"$\"#,##0)",
// 6
"\"$\"#,##0_);[Red](\"$\"#,##0)",
// 7
"\"$\"#,##0.00_);(\"$\"#,##0.00)",
// 8
"\"$\"#,##0.00_);[Red](\"$\"#,##0.00)",
// 9
"0%",
// 10
"0.00%",
// 11
"0.00E+00",
// 12
"# ?/?",
// 13
"# ??/??",
// 14
// The official documentation shows "m/d/yy", but the actual test is "yyyy/m/d".
"yyyy/m/d",
// 15
"d-mmm-yy",
// 16
"d-mmm",
// 17
"mmm-yy",
// 18
"h:mm AM/PM",
// 19
"h:mm:ss AM/PM",
// 20
"h:mm",
// 21
"h:mm:ss",
// 22
// The official documentation shows "m/d/yy h:mm", but the actual test is "yyyy/m/d h:mm".
"yyyy/m/d h:mm",
// 23-26 No specific correspondence found in the official documentation.
// 23
null,
// 24
null,
// 25
null,
// 26
null,
// 27
"yyyy\"年\"m\"月\"",
// 28
"m\"月\"d\"日\"",
// 29
"m\"月\"d\"日\"",
// 30
"m-d-yy",
// 31
"yyyy\"年\"m\"月\"d\"日\"",
// 32
"h\"时\"mm\"分\"",
// 33
"h\"时\"mm\"分\"ss\"秒\"",
// 34
"上午/下午h\"时\"mm\"分\"",
// 35
"上午/下午h\"时\"mm\"分\"ss\"秒\"",
// 36
"yyyy\"年\"m\"月\"",
// 37
"#,##0_);(#,##0)",
// 38
"#,##0_);[Red](#,##0)",
// 39
"#,##0.00_);(#,##0.00)",
// 40
"#,##0.00_);[Red](#,##0.00)",
// 41
"_(* #,##0_);_(* (#,##0);_(* \"-\"_);_(@_)",
// 42
"_(\"$\"* #,##0_);_(\"$\"* (#,##0);_(\"$\"* \"-\"_);_(@_)",
// 43
"_(* #,##0.00_);_(* (#,##0.00);_(* \"-\"??_);_(@_)",
// 44
"_(\"$\"* #,##0.00_);_(\"$\"* (#,##0.00);_(\"$\"* \"-\"??_);_(@_)",
// 45
"mm:ss",
// 46
"[h]:mm:ss",
// 47
"mm:ss.0",
// 48
"##0.0E+0",
// 49
"@",
// 50
"yyyy\"年\"m\"月\"",
// 51
"m\"月\"d\"日\"",
// 52
"yyyy\"年\"m\"月\"",
// 53
"m\"月\"d\"日\"",
// 54
"m\"月\"d\"日\"",
// 55
"上午/下午h\"时\"mm\"分\"",
// 56
"上午/下午h\"时\"mm\"分\"ss\"秒\"",
// 57
"yyyy\"年\"m\"月\"",
// 58
"m\"月\"d\"日\"",
// 59
"t0",
// 60
"t0.00",
// 61
"t#,##0",
// 62
"t#,##0.00",
// 63-66 No specific correspondence found in the official documentation.
// 63
null,
// 64
null,
// 65
null,
// 66
null,
// 67
"t0%",
// 68
"t0.00%",
// 69
"t# ?/?",
// 70
"t# ??/??",
// 71
"ว/ด/ปปปป",
// 72
"ว-ดดด-ปป",
// 73
"ว-ดดด",
// 74
"ดดด-ปป",
// 75
"ช:นน",
// 76
"ช:นน:ทท",
// 77
"ว/ด/ปปปป ช:นน",
// 78
"นน:ทท",
// 79
"[ช]:นน:ทท",
// 80
"นน:ทท.0",
// 81
"d/m/bb",
// end
};
public static String getBuiltinFormat(Integer index, String defaultFormat, Locale locale) {
String[] builtinFormat = switchBuiltinFormats(locale);
if (index == null || index < 0 || index >= builtinFormat.length) {
return defaultFormat;
}
return builtinFormat[index];
}
private static String[] switchBuiltinFormats(Locale locale) {
if (locale != null && Locale.US.getCountry().equals(locale.getCountry())) {
return BUILTIN_FORMATS_US;
}
return BUILTIN_FORMATS_CN;
}
}

1
src/main/java/com/alibaba/excel/context/AnalysisContext.java

@ -2,7 +2,6 @@ package com.alibaba.excel.context;
import java.io.InputStream; import java.io.InputStream;
import com.alibaba.excel.analysis.ExcelReadExecutor;
import com.alibaba.excel.event.AnalysisEventListener; import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.metadata.Sheet; import com.alibaba.excel.metadata.Sheet;
import com.alibaba.excel.read.metadata.ReadSheet; import com.alibaba.excel.read.metadata.ReadSheet;

5
src/main/java/com/alibaba/excel/context/WriteContext.java

@ -66,9 +66,10 @@ public interface WriteContext {
/** /**
* close * close
*
* @param onException
*/ */
void finish(); void finish(boolean onException);
/** /**
* Current sheet * Current sheet

87
src/main/java/com/alibaba/excel/context/WriteContextImpl.java

@ -1,27 +1,17 @@
package com.alibaba.excel.context; package com.alibaba.excel.context;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.util.FileUtils;
import com.alibaba.excel.util.StringUtils;
import java.io.File; import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.security.GeneralSecurityException;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
import org.apache.poi.hssf.record.crypto.Biff8EncryptionKey; import org.apache.poi.hssf.record.crypto.Biff8EncryptionKey;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.openxml4j.opc.OPCPackage; import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.openxml4j.opc.PackageAccess; import org.apache.poi.openxml4j.opc.PackageAccess;
import org.apache.poi.poifs.crypt.Decryptor;
import org.apache.poi.poifs.crypt.EncryptionInfo; import org.apache.poi.poifs.crypt.EncryptionInfo;
import org.apache.poi.poifs.crypt.EncryptionMode; import org.apache.poi.poifs.crypt.EncryptionMode;
import org.apache.poi.poifs.crypt.Encryptor; import org.apache.poi.poifs.crypt.Encryptor;
import org.apache.poi.poifs.filesystem.DocumentOutputStream;
import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Row;
@ -36,6 +26,11 @@ import com.alibaba.excel.enums.WriteTypeEnum;
import com.alibaba.excel.exception.ExcelGenerateException; import com.alibaba.excel.exception.ExcelGenerateException;
import com.alibaba.excel.metadata.CellData; import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.Head; import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.util.DateUtils;
import com.alibaba.excel.util.FileUtils;
import com.alibaba.excel.util.NumberDataFormatterUtils;
import com.alibaba.excel.util.StringUtils;
import com.alibaba.excel.util.WorkBookUtil; import com.alibaba.excel.util.WorkBookUtil;
import com.alibaba.excel.util.WriteHandlerUtils; import com.alibaba.excel.util.WriteHandlerUtils;
import com.alibaba.excel.write.metadata.WriteSheet; import com.alibaba.excel.write.metadata.WriteSheet;
@ -72,6 +67,10 @@ public class WriteContextImpl implements WriteContext {
* Configuration of currently operated cell * Configuration of currently operated cell
*/ */
private WriteHolder currentWriteHolder; private WriteHolder currentWriteHolder;
/**
* Prevent multiple shutdowns
*/
private boolean finished = false;
public WriteContextImpl(WriteWorkbook writeWorkbook) { public WriteContextImpl(WriteWorkbook writeWorkbook) {
if (writeWorkbook == null) { if (writeWorkbook == null) {
@ -172,20 +171,24 @@ public class WriteContextImpl implements WriteContext {
int newRowIndex = writeSheetHolder.getNewRowIndexAndStartDoWrite(); int newRowIndex = writeSheetHolder.getNewRowIndexAndStartDoWrite();
newRowIndex += currentWriteHolder.relativeHeadRowIndex(); newRowIndex += currentWriteHolder.relativeHeadRowIndex();
// Combined head // Combined head
addMergedRegionToCurrentSheet(excelWriteHeadProperty, newRowIndex); if (currentWriteHolder.automaticMergeHead()) {
addMergedRegionToCurrentSheet(excelWriteHeadProperty, newRowIndex);
}
for (int relativeRowIndex = 0, i = newRowIndex; i < excelWriteHeadProperty.getHeadRowNumber() + newRowIndex; for (int relativeRowIndex = 0, i = newRowIndex; i < excelWriteHeadProperty.getHeadRowNumber() + newRowIndex;
i++, relativeRowIndex++) { i++, relativeRowIndex++) {
WriteHandlerUtils.beforeRowCreate(this, newRowIndex, relativeRowIndex, Boolean.TRUE); WriteHandlerUtils.beforeRowCreate(this, newRowIndex, relativeRowIndex, Boolean.TRUE);
Row row = WorkBookUtil.createRow(writeSheetHolder.getSheet(), i); Row row = WorkBookUtil.createRow(writeSheetHolder.getSheet(), i);
WriteHandlerUtils.afterRowCreate(this, row, relativeRowIndex, Boolean.TRUE); WriteHandlerUtils.afterRowCreate(this, row, relativeRowIndex, Boolean.TRUE);
addOneRowOfHeadDataToExcel(row, excelWriteHeadProperty.getHeadMap(), relativeRowIndex); addOneRowOfHeadDataToExcel(row, excelWriteHeadProperty.getHeadMap(), relativeRowIndex);
WriteHandlerUtils.afterRowDispose(this, row, relativeRowIndex, Boolean.TRUE);
} }
} }
private void addMergedRegionToCurrentSheet(ExcelWriteHeadProperty excelWriteHeadProperty, int rowIndex) { private void addMergedRegionToCurrentSheet(ExcelWriteHeadProperty excelWriteHeadProperty, int rowIndex) {
for (com.alibaba.excel.metadata.CellRange cellRangeModel : excelWriteHeadProperty.headCellRangeList()) { for (com.alibaba.excel.metadata.CellRange cellRangeModel : excelWriteHeadProperty.headCellRangeList()) {
writeSheetHolder.getSheet().addMergedRegion(new CellRangeAddress(cellRangeModel.getFirstRow() + rowIndex, writeSheetHolder.getSheet()
cellRangeModel.getLastRow() + rowIndex, cellRangeModel.getFirstCol(), cellRangeModel.getLastCol())); .addMergedRegionUnsafe(new CellRangeAddress(cellRangeModel.getFirstRow() + rowIndex,
cellRangeModel.getLastRow() + rowIndex, cellRangeModel.getFirstCol(), cellRangeModel.getLastCol()));
} }
} }
@ -197,8 +200,7 @@ public class WriteContextImpl implements WriteContext {
Cell cell = row.createCell(columnIndex); Cell cell = row.createCell(columnIndex);
WriteHandlerUtils.afterCellCreate(this, cell, head, relativeRowIndex, Boolean.TRUE); WriteHandlerUtils.afterCellCreate(this, cell, head, relativeRowIndex, Boolean.TRUE);
cell.setCellValue(head.getHeadNameList().get(relativeRowIndex)); cell.setCellValue(head.getHeadNameList().get(relativeRowIndex));
CellData cellData = null; WriteHandlerUtils.afterCellDispose(this, (CellData)null, cell, head, relativeRowIndex, Boolean.TRUE);
WriteHandlerUtils.afterCellDispose(this, cellData, cell, head, relativeRowIndex, Boolean.TRUE);
} }
} }
@ -256,25 +258,39 @@ public class WriteContextImpl implements WriteContext {
} }
@Override @Override
public void finish() { public void finish(boolean onException) {
if (finished) {
return;
}
finished = true;
WriteHandlerUtils.afterWorkbookDispose(this); WriteHandlerUtils.afterWorkbookDispose(this);
if (writeWorkbookHolder == null) { if (writeWorkbookHolder == null) {
return; return;
} }
Throwable throwable = null;
boolean isOutputStreamEncrypt = false; boolean isOutputStreamEncrypt = false;
try { // Determine if you need to write excel
isOutputStreamEncrypt = doOutputStreamEncrypt07(); boolean writeExcel = !onException;
} catch (Throwable t) { if (writeWorkbookHolder.getWriteExcelOnException()) {
throwCanNotCloseIo(t); writeExcel = Boolean.TRUE;
}
// No data is written if an exception is thrown
if (writeExcel) {
try {
isOutputStreamEncrypt = doOutputStreamEncrypt07();
} catch (Throwable t) {
throwable = t;
}
} }
if (!isOutputStreamEncrypt) { if (!isOutputStreamEncrypt) {
try { try {
writeWorkbookHolder.getWorkbook().write(writeWorkbookHolder.getOutputStream()); if (writeExcel) {
writeWorkbookHolder.getWorkbook().write(writeWorkbookHolder.getOutputStream());
}
writeWorkbookHolder.getWorkbook().close(); writeWorkbookHolder.getWorkbook().close();
} catch (Throwable t) { } catch (Throwable t) {
throwCanNotCloseIo(t); throwable = t;
} }
} }
@ -284,7 +300,7 @@ public class WriteContextImpl implements WriteContext {
((SXSSFWorkbook)workbook).dispose(); ((SXSSFWorkbook)workbook).dispose();
} }
} catch (Throwable t) { } catch (Throwable t) {
throwCanNotCloseIo(t); throwable = t;
} }
try { try {
@ -292,14 +308,14 @@ public class WriteContextImpl implements WriteContext {
writeWorkbookHolder.getOutputStream().close(); writeWorkbookHolder.getOutputStream().close();
} }
} catch (Throwable t) { } catch (Throwable t) {
throwCanNotCloseIo(t); throwable = t;
} }
if (!isOutputStreamEncrypt) { if (writeExcel && !isOutputStreamEncrypt) {
try { try {
doFileEncrypt07(); doFileEncrypt07();
} catch (Throwable t) { } catch (Throwable t) {
throwCanNotCloseIo(t); throwable = t;
} }
} }
@ -308,18 +324,25 @@ public class WriteContextImpl implements WriteContext {
writeWorkbookHolder.getTempTemplateInputStream().close(); writeWorkbookHolder.getTempTemplateInputStream().close();
} }
} catch (Throwable t) { } catch (Throwable t) {
throwCanNotCloseIo(t); throwable = t;
} }
clearEncrypt03(); clearEncrypt03();
removeThreadLocalCache();
if (throwable != null) {
throw new ExcelGenerateException("Can not close IO.", throwable);
}
if (LOGGER.isDebugEnabled()) { if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Finished write."); LOGGER.debug("Finished write.");
} }
} }
private void throwCanNotCloseIo(Throwable t) { private void removeThreadLocalCache() {
throw new ExcelGenerateException("Can not close IO", t); NumberDataFormatterUtils.removeThreadLocalCache();
DateUtils.removeThreadLocalCache();
} }
@Override @Override
@ -428,7 +451,9 @@ public class WriteContextImpl implements WriteContext {
OutputStream outputStream = encryptor.getDataStream(fileSystem); OutputStream outputStream = encryptor.getDataStream(fileSystem);
opcPackage.save(outputStream); opcPackage.save(outputStream);
} finally { } finally {
opcPackage.close(); if (opcPackage != null) {
opcPackage.close();
}
} }
return fileSystem; return fileSystem;
} }

2
src/main/java/com/alibaba/excel/converters/DefaultConverterLoader.java

@ -37,6 +37,7 @@ import com.alibaba.excel.converters.string.StringBooleanConverter;
import com.alibaba.excel.converters.string.StringErrorConverter; import com.alibaba.excel.converters.string.StringErrorConverter;
import com.alibaba.excel.converters.string.StringNumberConverter; import com.alibaba.excel.converters.string.StringNumberConverter;
import com.alibaba.excel.converters.string.StringStringConverter; import com.alibaba.excel.converters.string.StringStringConverter;
import com.alibaba.excel.converters.url.UrlImageConverter;
/** /**
* Load default handler * Load default handler
@ -71,6 +72,7 @@ public class DefaultConverterLoader {
putWriteConverter(new InputStreamImageConverter()); putWriteConverter(new InputStreamImageConverter());
putWriteConverter(new ByteArrayImageConverter()); putWriteConverter(new ByteArrayImageConverter());
putWriteConverter(new BoxingByteArrayImageConverter()); putWriteConverter(new BoxingByteArrayImageConverter());
putWriteConverter(new UrlImageConverter());
return defaultWriteConverter; return defaultWriteConverter;
} }

2
src/main/java/com/alibaba/excel/converters/byteconverter/ByteNumberConverter.java

@ -34,7 +34,7 @@ public class ByteNumberConverter implements Converter<Byte> {
@Override @Override
public CellData convertToExcelData(Byte value, ExcelContentProperty contentProperty, public CellData convertToExcelData(Byte value, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) { GlobalConfiguration globalConfiguration) {
return new CellData(BigDecimal.valueOf(value)); return new CellData(new BigDecimal(Byte.toString(value)));
} }
} }

2
src/main/java/com/alibaba/excel/converters/floatconverter/FloatNumberConverter.java

@ -34,7 +34,7 @@ public class FloatNumberConverter implements Converter<Float> {
@Override @Override
public CellData convertToExcelData(Float value, ExcelContentProperty contentProperty, public CellData convertToExcelData(Float value, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) { GlobalConfiguration globalConfiguration) {
return new CellData(BigDecimal.valueOf(value)); return new CellData(new BigDecimal(Float.toString(value)));
} }
} }

2
src/main/java/com/alibaba/excel/converters/integer/IntegerNumberConverter.java

@ -34,7 +34,7 @@ public class IntegerNumberConverter implements Converter<Integer> {
@Override @Override
public CellData convertToExcelData(Integer value, ExcelContentProperty contentProperty, public CellData convertToExcelData(Integer value, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) { GlobalConfiguration globalConfiguration) {
return new CellData(BigDecimal.valueOf(value)); return new CellData(new BigDecimal(Integer.toString(value)));
} }
} }

2
src/main/java/com/alibaba/excel/converters/shortconverter/ShortNumberConverter.java

@ -34,7 +34,7 @@ public class ShortNumberConverter implements Converter<Short> {
@Override @Override
public CellData convertToExcelData(Short value, ExcelContentProperty contentProperty, public CellData convertToExcelData(Short value, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) { GlobalConfiguration globalConfiguration) {
return new CellData(BigDecimal.valueOf(value)); return new CellData(new BigDecimal(Short.toString(value)));
} }
} }

12
src/main/java/com/alibaba/excel/converters/string/StringNumberConverter.java

@ -10,7 +10,9 @@ import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.GlobalConfiguration; import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.property.ExcelContentProperty; import com.alibaba.excel.metadata.property.ExcelContentProperty;
import com.alibaba.excel.util.DateUtils; import com.alibaba.excel.util.DateUtils;
import com.alibaba.excel.util.NumberDataFormatterUtils;
import com.alibaba.excel.util.NumberUtils; import com.alibaba.excel.util.NumberUtils;
import com.alibaba.excel.util.StringUtils;
/** /**
* String and number converter * String and number converter
@ -44,13 +46,9 @@ public class StringNumberConverter implements Converter<String> {
return NumberUtils.format(cellData.getNumberValue(), contentProperty); return NumberUtils.format(cellData.getNumberValue(), contentProperty);
} }
// Excel defines formatting // Excel defines formatting
if (cellData.getDataFormat() != null) { if (cellData.getDataFormat() != null && !StringUtils.isEmpty(cellData.getDataFormatString())) {
if (DateUtil.isADateFormat(cellData.getDataFormat(), cellData.getDataFormatString())) { return NumberDataFormatterUtils.format(cellData.getNumberValue().doubleValue(), cellData.getDataFormat(),
return DateUtils.format(DateUtil.getJavaDate(cellData.getNumberValue().doubleValue(), cellData.getDataFormatString(), globalConfiguration);
globalConfiguration.getUse1904windowing(), null));
} else {
return NumberUtils.format(cellData.getNumberValue(), contentProperty);
}
} }
// Default conversion number // Default conversion number
return NumberUtils.format(cellData.getNumberValue(), contentProperty); return NumberUtils.format(cellData.getNumberValue(), contentProperty);

52
src/main/java/com/alibaba/excel/converters/url/UrlImageConverter.java

@ -0,0 +1,52 @@
package com.alibaba.excel.converters.url;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import com.alibaba.excel.util.IoUtils;
/**
* Url and image converter
*
* @since 2.1.1
* @author Jiaju Zhuang
*/
public class UrlImageConverter implements Converter<URL> {
@Override
public Class supportJavaTypeKey() {
return URL.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.IMAGE;
}
@Override
public URL convertToJavaData(CellData cellData, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) {
throw new UnsupportedOperationException("Cannot convert images to url.");
}
@Override
public CellData convertToExcelData(URL value, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) throws IOException {
InputStream inputStream = null;
try {
inputStream = value.openStream();
byte[] bytes = IoUtils.toByteArray(inputStream);
return new CellData(bytes);
} finally {
if (inputStream != null) {
inputStream.close();
}
}
}
}

2
src/main/java/com/alibaba/excel/event/AnalysisEventListener.java

@ -16,7 +16,7 @@ public abstract class AnalysisEventListener<T> implements ReadListener<T> {
@Override @Override
public void invokeHead(Map<Integer, CellData> headMap, AnalysisContext context) { public void invokeHead(Map<Integer, CellData> headMap, AnalysisContext context) {
invokeHeadMap(ConverterUtils.convertToStringMap(headMap, context.currentReadHolder()), context); invokeHeadMap(ConverterUtils.convertToStringMap(headMap, context), context);
} }
/** /**

35
src/main/java/com/alibaba/excel/exception/ExcelDataConvertException.java

@ -1,5 +1,6 @@
package com.alibaba.excel.exception; package com.alibaba.excel.exception;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.property.ExcelContentProperty; import com.alibaba.excel.metadata.property.ExcelContentProperty;
import com.alibaba.excel.write.builder.ExcelWriterBuilder; import com.alibaba.excel.write.builder.ExcelWriterBuilder;
@ -17,6 +18,10 @@ public class ExcelDataConvertException extends RuntimeException {
* NotNull. * NotNull.
*/ */
private Integer columnIndex; private Integer columnIndex;
/**
* NotNull.
*/
private CellData cellData;
/** /**
* Nullable.Only when the header is configured and when the class header is used is not null. * Nullable.Only when the header is configured and when the class header is used is not null.
* *
@ -24,34 +29,24 @@ public class ExcelDataConvertException extends RuntimeException {
*/ */
private ExcelContentProperty excelContentProperty; private ExcelContentProperty excelContentProperty;
public ExcelDataConvertException(String message) { public ExcelDataConvertException(Integer rowIndex, Integer columnIndex, CellData cellData,
super(message); ExcelContentProperty excelContentProperty, String message) {
}
public ExcelDataConvertException(Integer rowIndex, Integer columnIndex, ExcelContentProperty excelContentProperty,
String message) {
super(message); super(message);
this.rowIndex = rowIndex; this.rowIndex = rowIndex;
this.columnIndex = columnIndex; this.columnIndex = columnIndex;
this.cellData = cellData;
this.excelContentProperty = excelContentProperty; this.excelContentProperty = excelContentProperty;
} }
public ExcelDataConvertException(Integer rowIndex, Integer columnIndex, ExcelContentProperty excelContentProperty, public ExcelDataConvertException(Integer rowIndex, Integer columnIndex, CellData cellData,
String message, Throwable cause) { ExcelContentProperty excelContentProperty, String message, Throwable cause) {
super(message, cause); super(message, cause);
this.rowIndex = rowIndex; this.rowIndex = rowIndex;
this.columnIndex = columnIndex; this.columnIndex = columnIndex;
this.cellData = cellData;
this.excelContentProperty = excelContentProperty; this.excelContentProperty = excelContentProperty;
} }
public ExcelDataConvertException(String message, Throwable cause) {
super(message, cause);
}
public ExcelDataConvertException(Throwable cause) {
super(cause);
}
public Integer getRowIndex() { public Integer getRowIndex() {
return rowIndex; return rowIndex;
} }
@ -75,4 +70,12 @@ public class ExcelDataConvertException extends RuntimeException {
public void setExcelContentProperty(ExcelContentProperty excelContentProperty) { public void setExcelContentProperty(ExcelContentProperty excelContentProperty) {
this.excelContentProperty = excelContentProperty; this.excelContentProperty = excelContentProperty;
} }
public CellData getCellData() {
return cellData;
}
public void setCellData(CellData cellData) {
this.cellData = cellData;
}
} }

12
src/main/java/com/alibaba/excel/metadata/AbstractHolder.java

@ -1,6 +1,7 @@
package com.alibaba.excel.metadata; package com.alibaba.excel.metadata;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.Map; import java.util.Map;
import com.alibaba.excel.converters.Converter; import com.alibaba.excel.converters.Converter;
@ -27,7 +28,6 @@ public abstract class AbstractHolder implements ConfigurationHolder {
* Some global variables * Some global variables
*/ */
private GlobalConfiguration globalConfiguration; private GlobalConfiguration globalConfiguration;
/** /**
* <p> * <p>
* Read key: * Read key:
@ -58,6 +58,16 @@ public abstract class AbstractHolder implements ConfigurationHolder {
} else { } else {
globalConfiguration.setAutoTrim(basicParameter.getAutoTrim()); globalConfiguration.setAutoTrim(basicParameter.getAutoTrim());
} }
if (basicParameter.getLocale() == null) {
if (prentAbstractHolder == null) {
globalConfiguration.setLocale(Locale.getDefault());
} else {
globalConfiguration.setLocale(prentAbstractHolder.getGlobalConfiguration().getLocale());
}
} else {
globalConfiguration.setLocale(basicParameter.getLocale());
}
} }
public Boolean getNewInitialization() { public Boolean getNewInitialization() {

93
src/main/java/com/alibaba/excel/metadata/AbstractParameterBuilder.java

@ -0,0 +1,93 @@
package com.alibaba.excel.metadata;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import com.alibaba.excel.converters.Converter;
/**
* ExcelBuilder
*
* @author Jiaju Zhuang
*/
public abstract class AbstractParameterBuilder<T extends AbstractParameterBuilder, C extends BasicParameter> {
/**
* You can only choose one of the {@link #head(List)} and {@link #head(Class)}
*
* @param head
* @return
*/
public T head(List<List<String>> head) {
parameter().setHead(head);
return self();
}
/**
* You can only choose one of the {@link #head(List)} and {@link #head(Class)}
*
* @param clazz
* @return
*/
public T head(Class clazz) {
parameter().setClazz(clazz);
return self();
}
/**
* Custom type conversions override the default.
*
* @param converter
* @return
*/
public T registerConverter(Converter converter) {
if (parameter().getCustomConverterList() == null) {
parameter().setCustomConverterList(new ArrayList<Converter>());
}
parameter().getCustomConverterList().add(converter);
return self();
}
/**
* true if date uses 1904 windowing, or false if using 1900 date windowing.
*
* default is false
*
* @param use1904windowing
* @return
*/
public T use1904windowing(Boolean use1904windowing) {
parameter().setUse1904windowing(use1904windowing);
return self();
}
/**
* A <code>Locale</code> object represents a specific geographical, political, or cultural region. This parameter is
* used when formatting dates and numbers.
*
* @param locale
* @return
*/
public T locale(Locale locale) {
parameter().setLocale(locale);
return self();
}
/**
* Automatic trim includes sheet name and content
*
* @param autoTrim
* @return
*/
public T autoTrim(Boolean autoTrim) {
parameter().setAutoTrim(autoTrim);
return self();
}
@SuppressWarnings("unchecked")
protected T self() {
return (T)this;
}
protected abstract C parameter();
}

13
src/main/java/com/alibaba/excel/metadata/BasicParameter.java

@ -1,6 +1,7 @@
package com.alibaba.excel.metadata; package com.alibaba.excel.metadata;
import java.util.List; import java.util.List;
import java.util.Locale;
import com.alibaba.excel.converters.Converter; import com.alibaba.excel.converters.Converter;
@ -34,6 +35,11 @@ public class BasicParameter {
* @return * @return
*/ */
private Boolean use1904windowing; private Boolean use1904windowing;
/**
* A <code>Locale</code> object represents a specific geographical, political, or cultural region. This parameter is
* used when formatting dates and numbers.
*/
private Locale locale;
public List<List<String>> getHead() { public List<List<String>> getHead() {
return head; return head;
@ -75,4 +81,11 @@ public class BasicParameter {
this.use1904windowing = use1904windowing; this.use1904windowing = use1904windowing;
} }
public Locale getLocale() {
return locale;
}
public void setLocale(Locale locale) {
this.locale = locale;
}
} }

11
src/main/java/com/alibaba/excel/metadata/CellData.java

@ -202,6 +202,9 @@ public class CellData<T> {
* Ensure that the object does not appear null * Ensure that the object does not appear null
*/ */
public void checkEmpty() { public void checkEmpty() {
if (type == null) {
type = CellDataTypeEnum.EMPTY;
}
switch (type) { switch (type) {
case STRING: case STRING:
case ERROR: case ERROR:
@ -225,16 +228,22 @@ public class CellData<T> {
@Override @Override
public String toString() { public String toString() {
if (type == null) {
return StringUtils.EMPTY;
}
switch (type) { switch (type) {
case NUMBER: case NUMBER:
return numberValue.toString(); return numberValue.toString();
case BOOLEAN: case BOOLEAN:
return booleanValue.toString(); return booleanValue.toString();
case DIRECT_STRING:
case STRING: case STRING:
case ERROR: case ERROR:
return stringValue; return stringValue;
case IMAGE:
return "image[" + imageValue.length + "]";
default: default:
return "empty"; return StringUtils.EMPTY;
} }
} }

786
src/main/java/com/alibaba/excel/metadata/DataFormatter.java

@ -0,0 +1,786 @@
/*
* ==================================================================== Licensed to the Apache Software Foundation (ASF)
* under one or more contributor license agreements. See the NOTICE file distributed with this work for additional
* information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the
* License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* 2012 - Alfresco Software, Ltd. Alfresco Software has modified source of this file The details of changes as svn diff
* can be found in svn at location root/projects/3rd-party/src
* ====================================================================
*/
package com.alibaba.excel.metadata;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DateFormatSymbols;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.FieldPosition;
import java.text.Format;
import java.text.ParsePosition;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.ss.usermodel.ExcelGeneralNumberFormat;
import org.apache.poi.ss.usermodel.ExcelStyleDateFormatter;
import org.apache.poi.ss.usermodel.FractionFormat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.excel.util.DateUtils;
/**
* Written with reference to {@link org.apache.poi.ss.usermodel.DataFormatter}.Made some optimizations for date
* conversion.
* <p>
* This is a non-thread-safe class.
*
* @author Jiaju Zhuang
*/
public class DataFormatter {
/** For logging any problems we find */
private static final Logger LOGGER = LoggerFactory.getLogger(DataFormatter.class);
private static final String defaultFractionWholePartFormat = "#";
private static final String defaultFractionFractionPartFormat = "#/##";
/** Pattern to find a number format: "0" or "#" */
private static final Pattern numPattern = Pattern.compile("[0#]+");
/** Pattern to find days of week as text "ddd...." */
private static final Pattern daysAsText = Pattern.compile("([d]{3,})", Pattern.CASE_INSENSITIVE);
/** Pattern to find "AM/PM" marker */
private static final Pattern amPmPattern =
Pattern.compile("(([AP])[M/P]*)|(([上下])[午/下]*)", Pattern.CASE_INSENSITIVE);
/** Pattern to find formats with condition ranges e.g. [>=100] */
private static final Pattern rangeConditionalPattern =
Pattern.compile(".*\\[\\s*(>|>=|<|<=|=)\\s*[0-9]*\\.*[0-9].*");
/**
* A regex to find locale patterns like [$$-1009] and [$?-452]. Note that we don't currently process these into
* locales
*/
private static final Pattern localePatternGroup = Pattern.compile("(\\[\\$[^-\\]]*-[0-9A-Z]+])");
/**
* A regex to match the colour formattings rules. Allowed colours are: Black, Blue, Cyan, Green, Magenta, Red,
* White, Yellow, "Color n" (1<=n<=56)
*/
private static final Pattern colorPattern = Pattern.compile(
"(\\[BLACK])|(\\[BLUE])|(\\[CYAN])|(\\[GREEN])|" + "(\\[MAGENTA])|(\\[RED])|(\\[WHITE])|(\\[YELLOW])|"
+ "(\\[COLOR\\s*\\d])|(\\[COLOR\\s*[0-5]\\d])|(\\[DBNum(1|2|3)])|(\\[\\$-\\d{0,3}])",
Pattern.CASE_INSENSITIVE);
/**
* A regex to identify a fraction pattern. This requires that replaceAll("\\?", "#") has already been called
*/
private static final Pattern fractionPattern = Pattern.compile("(?:([#\\d]+)\\s+)?(#+)\\s*/\\s*([#\\d]+)");
/**
* A regex to strip junk out of fraction formats
*/
private static final Pattern fractionStripper = Pattern.compile("(\"[^\"]*\")|([^ ?#\\d/]+)");
/**
* A regex to detect if an alternate grouping character is used in a numeric format
*/
private static final Pattern alternateGrouping = Pattern.compile("([#0]([^.#0])[#0]{3})");
/**
* Cells formatted with a date or time format and which contain invalid date or time values show 255 pound signs
* ("#").
*/
private static final String invalidDateTimeString;
static {
StringBuilder buf = new StringBuilder();
for (int i = 0; i < 255; i++)
buf.append('#');
invalidDateTimeString = buf.toString();
}
/**
* The decimal symbols of the locale used for formatting values.
*/
private DecimalFormatSymbols decimalSymbols;
/**
* The date symbols of the locale used for formatting values.
*/
private DateFormatSymbols dateSymbols;
/** A default format to use when a number pattern cannot be parsed. */
private Format defaultNumFormat;
/**
* A map to cache formats. Map<String,Format> formats
*/
private final Map<String, Format> formats = new HashMap<String, Format>();
/** stores the locale valid it the last formatting call */
private Locale locale;
/**
* true if date uses 1904 windowing, or false if using 1900 date windowing.
*
* default is false
*
* @return
*/
private Boolean use1904windowing;
/**
* Creates a formatter using the {@link Locale#getDefault() default locale}.
*/
public DataFormatter() {
this(null, null);
}
/**
* Creates a formatter using the given locale.
*
*/
public DataFormatter(Locale locale, Boolean use1904windowing) {
this.use1904windowing = use1904windowing != null ? use1904windowing : Boolean.FALSE;
this.locale = locale != null ? locale : Locale.getDefault();
this.dateSymbols = DateFormatSymbols.getInstance(this.locale);
this.decimalSymbols = DecimalFormatSymbols.getInstance(this.locale);
}
private Format getFormat(Integer dataFormat, String dataFormatString) {
// See if we already have it cached
Format format = formats.get(dataFormatString);
if (format != null) {
return format;
}
// Is it one of the special built in types, General or @?
if ("General".equalsIgnoreCase(dataFormatString) || "@".equals(dataFormatString)) {
format = getDefaultFormat();
addFormat(dataFormatString, format);
return format;
}
// Build a formatter, and cache it
format = createFormat(dataFormat, dataFormatString);
addFormat(dataFormatString, format);
return format;
}
private Format createFormat(Integer dataFormat, String dataFormatString) {
String formatStr = dataFormatString;
Format format = checkSpecialConverter(formatStr);
if (format != null) {
return format;
}
// Remove colour formatting if present
Matcher colourM = colorPattern.matcher(formatStr);
while (colourM.find()) {
String colour = colourM.group();
// Paranoid replacement...
int at = formatStr.indexOf(colour);
if (at == -1)
break;
String nFormatStr = formatStr.substring(0, at) + formatStr.substring(at + colour.length());
if (nFormatStr.equals(formatStr))
break;
// Try again in case there's multiple
formatStr = nFormatStr;
colourM = colorPattern.matcher(formatStr);
}
// Strip off the locale information, we use an instance-wide locale for everything
Matcher m = localePatternGroup.matcher(formatStr);
while (m.find()) {
String match = m.group();
String symbol = match.substring(match.indexOf('$') + 1, match.indexOf('-'));
if (symbol.indexOf('$') > -1) {
symbol = symbol.substring(0, symbol.indexOf('$')) + '\\' + symbol.substring(symbol.indexOf('$'));
}
formatStr = m.replaceAll(symbol);
m = localePatternGroup.matcher(formatStr);
}
// Check for special cases
if (formatStr == null || formatStr.trim().length() == 0) {
return getDefaultFormat();
}
if ("General".equalsIgnoreCase(formatStr) || "@".equals(formatStr)) {
return getDefaultFormat();
}
if (DateUtils.isADateFormat(dataFormat, formatStr)) {
return createDateFormat(formatStr);
}
// Excel supports fractions in format strings, which Java doesn't
if (formatStr.contains("#/") || formatStr.contains("?/")) {
String[] chunks = formatStr.split(";");
for (String chunk1 : chunks) {
String chunk = chunk1.replaceAll("\\?", "#");
Matcher matcher = fractionStripper.matcher(chunk);
chunk = matcher.replaceAll(" ");
chunk = chunk.replaceAll(" +", " ");
Matcher fractionMatcher = fractionPattern.matcher(chunk);
// take the first match
if (fractionMatcher.find()) {
String wholePart = (fractionMatcher.group(1) == null) ? "" : defaultFractionWholePartFormat;
return new FractionFormat(wholePart, fractionMatcher.group(3));
}
}
// Strip custom text in quotes and escaped characters for now as it can cause performance problems in
// fractions.
// String strippedFormatStr = formatStr.replaceAll("\\\\ ", " ").replaceAll("\\\\.",
// "").replaceAll("\"[^\"]*\"", " ").replaceAll("\\?", "#");
return new FractionFormat(defaultFractionWholePartFormat, defaultFractionFractionPartFormat);
}
if (numPattern.matcher(formatStr).find()) {
return createNumberFormat(formatStr);
}
return getDefaultFormat();
}
private Format checkSpecialConverter(String dataFormatString) {
if ("00000\\-0000".equals(dataFormatString) || "00000-0000".equals(dataFormatString)) {
return new ZipPlusFourFormat();
}
if ("[<=9999999]###\\-####;\\(###\\)\\ ###\\-####".equals(dataFormatString)
|| "[<=9999999]###-####;(###) ###-####".equals(dataFormatString)
|| "###\\-####;\\(###\\)\\ ###\\-####".equals(dataFormatString)
|| "###-####;(###) ###-####".equals(dataFormatString)) {
return new PhoneFormat();
}
if ("000\\-00\\-0000".equals(dataFormatString) || "000-00-0000".equals(dataFormatString)) {
return new SSNFormat();
}
return null;
}
private Format createDateFormat(String pFormatStr) {
String formatStr = pFormatStr;
formatStr = formatStr.replaceAll("\\\\-", "-");
formatStr = formatStr.replaceAll("\\\\,", ",");
formatStr = formatStr.replaceAll("\\\\\\.", "."); // . is a special regexp char
formatStr = formatStr.replaceAll("\\\\ ", " ");
formatStr = formatStr.replaceAll("\\\\/", "/"); // weird: m\\/d\\/yyyy
formatStr = formatStr.replaceAll(";@", "");
formatStr = formatStr.replaceAll("\"/\"", "/"); // "/" is escaped for no reason in: mm"/"dd"/"yyyy
formatStr = formatStr.replace("\"\"", "'"); // replace Excel quoting with Java style quoting
formatStr = formatStr.replaceAll("\\\\T", "'T'"); // Quote the T is iso8601 style dates
formatStr = formatStr.replace("\"", "");
boolean hasAmPm = false;
Matcher amPmMatcher = amPmPattern.matcher(formatStr);
while (amPmMatcher.find()) {
formatStr = amPmMatcher.replaceAll("@");
hasAmPm = true;
amPmMatcher = amPmPattern.matcher(formatStr);
}
formatStr = formatStr.replaceAll("@", "a");
Matcher dateMatcher = daysAsText.matcher(formatStr);
if (dateMatcher.find()) {
String match = dateMatcher.group(0).toUpperCase(Locale.ROOT).replaceAll("D", "E");
formatStr = dateMatcher.replaceAll(match);
}
// Convert excel date format to SimpleDateFormat.
// Excel uses lower and upper case 'm' for both minutes and months.
// From Excel help:
/*
The "m" or "mm" code must appear immediately after the "h" or"hh"
code or immediately before the "ss" code; otherwise, Microsoft
Excel displays the month instead of minutes."
*/
StringBuilder sb = new StringBuilder();
char[] chars = formatStr.toCharArray();
boolean mIsMonth = true;
List<Integer> ms = new ArrayList<Integer>();
boolean isElapsed = false;
for (int j = 0; j < chars.length; j++) {
char c = chars[j];
if (c == '\'') {
sb.append(c);
j++;
// skip until the next quote
while (j < chars.length) {
c = chars[j];
sb.append(c);
if (c == '\'') {
break;
}
j++;
}
} else if (c == '[' && !isElapsed) {
isElapsed = true;
mIsMonth = false;
sb.append(c);
} else if (c == ']' && isElapsed) {
isElapsed = false;
sb.append(c);
} else if (isElapsed) {
if (c == 'h' || c == 'H') {
sb.append('H');
} else if (c == 'm' || c == 'M') {
sb.append('m');
} else if (c == 's' || c == 'S') {
sb.append('s');
} else {
sb.append(c);
}
} else if (c == 'h' || c == 'H') {
mIsMonth = false;
if (hasAmPm) {
sb.append('h');
} else {
sb.append('H');
}
} else if (c == 'm' || c == 'M') {
if (mIsMonth) {
sb.append('M');
ms.add(Integer.valueOf(sb.length() - 1));
} else {
sb.append('m');
}
} else if (c == 's' || c == 'S') {
sb.append('s');
// if 'M' precedes 's' it should be minutes ('m')
for (int index : ms) {
if (sb.charAt(index) == 'M') {
sb.replace(index, index + 1, "m");
}
}
mIsMonth = true;
ms.clear();
} else if (Character.isLetter(c)) {
mIsMonth = true;
ms.clear();
if (c == 'y' || c == 'Y') {
sb.append('y');
} else if (c == 'd' || c == 'D') {
sb.append('d');
} else {
sb.append(c);
}
} else {
if (Character.isWhitespace(c)) {
ms.clear();
}
sb.append(c);
}
}
formatStr = sb.toString();
try {
return new ExcelStyleDateFormatter(formatStr, dateSymbols);
} catch (IllegalArgumentException iae) {
LOGGER.debug("Formatting failed for format {}, falling back", formatStr, iae);
// the pattern could not be parsed correctly,
// so fall back to the default number format
return getDefaultFormat();
}
}
private String cleanFormatForNumber(String formatStr) {
StringBuilder sb = new StringBuilder(formatStr);
// If they requested spacers, with "_",
// remove those as we don't do spacing
// If they requested full-column-width
// padding, with "*", remove those too
for (int i = 0; i < sb.length(); i++) {
char c = sb.charAt(i);
if (c == '_' || c == '*') {
if (i > 0 && sb.charAt((i - 1)) == '\\') {
// It's escaped, don't worry
continue;
}
if (i < sb.length() - 1) {
// Remove the character we're supposed
// to match the space of / pad to the
// column width with
sb.deleteCharAt(i + 1);
}
// Remove the _ too
sb.deleteCharAt(i);
i--;
}
}
// Now, handle the other aspects like
// quoting and scientific notation
for (int i = 0; i < sb.length(); i++) {
char c = sb.charAt(i);
// remove quotes and back slashes
if (c == '\\' || c == '"') {
sb.deleteCharAt(i);
i--;
// for scientific/engineering notation
} else if (c == '+' && i > 0 && sb.charAt(i - 1) == 'E') {
sb.deleteCharAt(i);
i--;
}
}
return sb.toString();
}
private static class InternalDecimalFormatWithScale extends Format {
private static final Pattern endsWithCommas = Pattern.compile("(,+)$");
private BigDecimal divider;
private static final BigDecimal ONE_THOUSAND = new BigDecimal(1000);
private final DecimalFormat df;
private static String trimTrailingCommas(String s) {
return s.replaceAll(",+$", "");
}
public InternalDecimalFormatWithScale(String pattern, DecimalFormatSymbols symbols) {
df = new DecimalFormat(trimTrailingCommas(pattern), symbols);
setExcelStyleRoundingMode(df);
Matcher endsWithCommasMatcher = endsWithCommas.matcher(pattern);
if (endsWithCommasMatcher.find()) {
String commas = (endsWithCommasMatcher.group(1));
BigDecimal temp = BigDecimal.ONE;
for (int i = 0; i < commas.length(); ++i) {
temp = temp.multiply(ONE_THOUSAND);
}
divider = temp;
} else {
divider = null;
}
}
private Object scaleInput(Object obj) {
if (divider != null) {
if (obj instanceof BigDecimal) {
obj = ((BigDecimal)obj).divide(divider, RoundingMode.HALF_UP);
} else if (obj instanceof Double) {
obj = (Double)obj / divider.doubleValue();
} else {
throw new UnsupportedOperationException();
}
}
return obj;
}
@Override
public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
obj = scaleInput(obj);
return df.format(obj, toAppendTo, pos);
}
@Override
public Object parseObject(String source, ParsePosition pos) {
throw new UnsupportedOperationException();
}
}
private Format createNumberFormat(String formatStr) {
String format = cleanFormatForNumber(formatStr);
DecimalFormatSymbols symbols = decimalSymbols;
// Do we need to change the grouping character?
// eg for a format like #'##0 which wants 12'345 not 12,345
Matcher agm = alternateGrouping.matcher(format);
if (agm.find()) {
char grouping = agm.group(2).charAt(0);
// Only replace the grouping character if it is not the default
// grouping character for the US locale (',') in order to enable
// correct grouping for non-US locales.
if (grouping != ',') {
symbols = DecimalFormatSymbols.getInstance(locale);
symbols.setGroupingSeparator(grouping);
String oldPart = agm.group(1);
String newPart = oldPart.replace(grouping, ',');
format = format.replace(oldPart, newPart);
}
}
try {
return new InternalDecimalFormatWithScale(format, symbols);
} catch (IllegalArgumentException iae) {
LOGGER.debug("Formatting failed for format {}, falling back", formatStr, iae);
// the pattern could not be parsed correctly,
// so fall back to the default number format
return getDefaultFormat();
}
}
private Format getDefaultFormat() {
// for numeric cells try user supplied default
if (defaultNumFormat != null) {
return defaultNumFormat;
// otherwise use general format
}
defaultNumFormat = new ExcelGeneralNumberFormat(locale);
return defaultNumFormat;
}
/**
* Performs Excel-style date formatting, using the supplied Date and format
*/
private String performDateFormatting(Date d, Format dateFormat) {
Format df = dateFormat != null ? dateFormat : getDefaultFormat();
return df.format(d);
}
/**
* Returns the formatted value of an Excel date as a <tt>String</tt> based on the cell's <code>DataFormat</code>.
* i.e. "Thursday, January 02, 2003" , "01/02/2003" , "02-Jan" , etc.
* <p>
* If any conditional format rules apply, the highest priority with a number format is used. If no rules contain a
* number format, or no rules apply, the cell's style format is used. If the style does not have a format, the
* default date format is applied.
*
* @param data
* to format
* @param dataFormat
* @param dataFormatString
* @return Formatted value
*/
private String getFormattedDateString(Double data, Integer dataFormat, String dataFormatString) {
Format dateFormat = getFormat(dataFormat, dataFormatString);
if (dateFormat instanceof ExcelStyleDateFormatter) {
// Hint about the raw excel value
((ExcelStyleDateFormatter)dateFormat).setDateToBeFormatted(data);
}
return performDateFormatting(DateUtil.getJavaDate(data, use1904windowing), dateFormat);
}
/**
* Returns the formatted value of an Excel number as a <tt>String</tt> based on the cell's <code>DataFormat</code>.
* Supported formats include currency, percents, decimals, phone number, SSN, etc.: "61.54%", "$100.00", "(800)
* 555-1234".
* <p>
* Format comes from either the highest priority conditional format rule with a specified format, or from the cell
* style.
*
* @param data
* to format
* @param dataFormat
* @param dataFormatString
* @return a formatted number string
*/
private String getFormattedNumberString(Double data, Integer dataFormat, String dataFormatString) {
Format numberFormat = getFormat(dataFormat, dataFormatString);
String formatted = numberFormat.format(data);
return formatted.replaceFirst("E(\\d)", "E+$1"); // to match Excel's E-notation
}
/**
* Format data.
*
* @param data
* @param dataFormat
* @param dataFormatString
* @return
*/
public String format(Double data, Integer dataFormat, String dataFormatString) {
if (DateUtils.isADateFormat(dataFormat, dataFormatString)) {
return getFormattedDateString(data, dataFormat, dataFormatString);
}
return getFormattedNumberString(data, dataFormat, dataFormatString);
}
/**
* <p>
* Sets a default number format to be used when the Excel format cannot be parsed successfully. <b>Note:</b> This is
* a fall back for when an error occurs while parsing an Excel number format pattern. This will not affect cells
* with the <em>General</em> format.
* </p>
* <p>
* The value that will be passed to the Format's format method (specified by <code>java.text.Format#format</code>)
* will be a double value from a numeric cell. Therefore the code in the format method should expect a
* <code>Number</code> value.
* </p>
*
* @param format
* A Format instance to be used as a default
* @see Format#format
*/
public void setDefaultNumberFormat(Format format) {
for (Map.Entry<String, Format> entry : formats.entrySet()) {
if (entry.getValue() == defaultNumFormat) {
entry.setValue(format);
}
}
defaultNumFormat = format;
}
/**
* Adds a new format to the available formats.
* <p>
* The value that will be passed to the Format's format method (specified by <code>java.text.Format#format</code>)
* will be a double value from a numeric cell. Therefore the code in the format method should expect a
* <code>Number</code> value.
* </p>
*
* @param excelFormatStr
* The data format string
* @param format
* A Format instance
*/
public void addFormat(String excelFormatStr, Format format) {
formats.put(excelFormatStr, format);
}
// Some custom formats
/**
* @return a <tt>DecimalFormat</tt> with parseIntegerOnly set <code>true</code>
*/
private static DecimalFormat createIntegerOnlyFormat(String fmt) {
DecimalFormatSymbols dsf = DecimalFormatSymbols.getInstance(Locale.ROOT);
DecimalFormat result = new DecimalFormat(fmt, dsf);
result.setParseIntegerOnly(true);
return result;
}
/**
* Enables excel style rounding mode (round half up) on the Decimal Format given.
*/
public static void setExcelStyleRoundingMode(DecimalFormat format) {
setExcelStyleRoundingMode(format, RoundingMode.HALF_UP);
}
/**
* Enables custom rounding mode on the given Decimal Format.
*
* @param format
* DecimalFormat
* @param roundingMode
* RoundingMode
*/
public static void setExcelStyleRoundingMode(DecimalFormat format, RoundingMode roundingMode) {
format.setRoundingMode(roundingMode);
}
/**
* Format class for Excel's SSN format. This class mimics Excel's built-in SSN formatting.
*
* @author James May
*/
@SuppressWarnings("serial")
private static final class SSNFormat extends Format {
private static final DecimalFormat df = createIntegerOnlyFormat("000000000");
private SSNFormat() {
// enforce singleton
}
/** Format a number as an SSN */
public static String format(Number num) {
String result = df.format(num);
return result.substring(0, 3) + '-' + result.substring(3, 5) + '-' + result.substring(5, 9);
}
@Override
public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
return toAppendTo.append(format((Number)obj));
}
@Override
public Object parseObject(String source, ParsePosition pos) {
return df.parseObject(source, pos);
}
}
/**
* Format class for Excel Zip + 4 format. This class mimics Excel's built-in formatting for Zip + 4.
*
* @author James May
*/
@SuppressWarnings("serial")
private static final class ZipPlusFourFormat extends Format {
private static final DecimalFormat df = createIntegerOnlyFormat("000000000");
private ZipPlusFourFormat() {
// enforce singleton
}
/** Format a number as Zip + 4 */
public static String format(Number num) {
String result = df.format(num);
return result.substring(0, 5) + '-' + result.substring(5, 9);
}
@Override
public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
return toAppendTo.append(format((Number)obj));
}
@Override
public Object parseObject(String source, ParsePosition pos) {
return df.parseObject(source, pos);
}
}
/**
* Format class for Excel phone number format. This class mimics Excel's built-in phone number formatting.
*
* @author James May
*/
@SuppressWarnings("serial")
private static final class PhoneFormat extends Format {
private static final DecimalFormat df = createIntegerOnlyFormat("##########");
private PhoneFormat() {
// enforce singleton
}
/** Format a number as a phone number */
public static String format(Number num) {
String result = df.format(num);
StringBuilder sb = new StringBuilder();
String seg1, seg2, seg3;
int len = result.length();
if (len <= 4) {
return result;
}
seg3 = result.substring(len - 4, len);
seg2 = result.substring(Math.max(0, len - 7), len - 4);
seg1 = result.substring(Math.max(0, len - 10), Math.max(0, len - 7));
if (seg1.trim().length() > 0) {
sb.append('(').append(seg1).append(") ");
}
if (seg2.trim().length() > 0) {
sb.append(seg2).append('-');
}
sb.append(seg3);
return sb.toString();
}
@Override
public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
return toAppendTo.append(format((Number)obj));
}
@Override
public Object parseObject(String source, ParsePosition pos) {
return df.parseObject(source, pos);
}
}
}

15
src/main/java/com/alibaba/excel/metadata/GlobalConfiguration.java

@ -1,5 +1,7 @@
package com.alibaba.excel.metadata; package com.alibaba.excel.metadata;
import java.util.Locale;
/** /**
* Global configuration * Global configuration
* *
@ -18,6 +20,11 @@ public class GlobalConfiguration {
* @return * @return
*/ */
private Boolean use1904windowing; private Boolean use1904windowing;
/**
* A <code>Locale</code> object represents a specific geographical, political, or cultural region. This parameter is
* used when formatting dates and numbers.
*/
private Locale locale;
public Boolean getUse1904windowing() { public Boolean getUse1904windowing() {
return use1904windowing; return use1904windowing;
@ -34,4 +41,12 @@ public class GlobalConfiguration {
public void setAutoTrim(Boolean autoTrim) { public void setAutoTrim(Boolean autoTrim) {
this.autoTrim = autoTrim; this.autoTrim = autoTrim;
} }
public Locale getLocale() {
return locale;
}
public void setLocale(Locale locale) {
this.locale = locale;
}
} }

95
src/main/java/com/alibaba/excel/metadata/property/ExcelHeadProperty.java

@ -17,14 +17,16 @@ import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.format.DateTimeFormat; import com.alibaba.excel.annotation.format.DateTimeFormat;
import com.alibaba.excel.annotation.format.NumberFormat; import com.alibaba.excel.annotation.format.NumberFormat;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.alibaba.excel.converters.AutoConverter; import com.alibaba.excel.converters.AutoConverter;
import com.alibaba.excel.converters.Converter; import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.HeadKindEnum; import com.alibaba.excel.enums.HeadKindEnum;
import com.alibaba.excel.exception.ExcelCommonException; import com.alibaba.excel.exception.ExcelCommonException;
import com.alibaba.excel.exception.ExcelGenerateException; import com.alibaba.excel.exception.ExcelGenerateException;
import com.alibaba.excel.metadata.Head; import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.Holder;
import com.alibaba.excel.util.ClassUtils;
import com.alibaba.excel.util.StringUtils; import com.alibaba.excel.util.StringUtils;
import com.alibaba.excel.write.metadata.holder.AbstractWriteHolder;
/** /**
* Define the header attribute of excel * Define the header attribute of excel
@ -46,7 +48,6 @@ public class ExcelHeadProperty {
* The number of rows in the line with the most rows * The number of rows in the line with the most rows
*/ */
private int headRowNumber; private int headRowNumber;
/** /**
* Configuration header information * Configuration header information
*/ */
@ -64,7 +65,7 @@ public class ExcelHeadProperty {
*/ */
private Map<String, Field> ignoreMap; private Map<String, Field> ignoreMap;
public ExcelHeadProperty(Class headClazz, List<List<String>> head, Boolean convertAllFiled) { public ExcelHeadProperty(Holder holder, Class headClazz, List<List<String>> head, Boolean convertAllFiled) {
this.headClazz = headClazz; this.headClazz = headClazz;
headMap = new TreeMap<Integer, Head>(); headMap = new TreeMap<Integer, Head>();
contentPropertyMap = new TreeMap<Integer, ExcelContentProperty>(); contentPropertyMap = new TreeMap<Integer, ExcelContentProperty>();
@ -73,14 +74,21 @@ public class ExcelHeadProperty {
headKind = HeadKindEnum.NONE; headKind = HeadKindEnum.NONE;
headRowNumber = 0; headRowNumber = 0;
if (head != null && !head.isEmpty()) { if (head != null && !head.isEmpty()) {
int headIndex = 0;
for (int i = 0; i < head.size(); i++) { for (int i = 0; i < head.size(); i++) {
headMap.put(i, new Head(i, null, head.get(i), Boolean.FALSE, Boolean.TRUE)); if (holder instanceof AbstractWriteHolder) {
contentPropertyMap.put(i, null); if (((AbstractWriteHolder)holder).ignore(null, i)) {
continue;
}
}
headMap.put(headIndex, new Head(headIndex, null, head.get(i), Boolean.FALSE, Boolean.TRUE));
contentPropertyMap.put(headIndex, null);
headIndex++;
} }
headKind = HeadKindEnum.STRING; headKind = HeadKindEnum.STRING;
} else { } else {
// convert headClazz to head // convert headClazz to head
initColumnProperties(convertAllFiled); initColumnProperties(holder, convertAllFiled);
} }
initHeadRowNumber(); initHeadRowNumber();
if (LOGGER.isDebugEnabled()) { if (LOGGER.isDebugEnabled()) {
@ -108,73 +116,49 @@ public class ExcelHeadProperty {
} }
} }
private void initColumnProperties(Boolean convertAllFiled) { private void initColumnProperties(Holder holder, Boolean convertAllFiled) {
if (headClazz == null) { if (headClazz == null) {
return; return;
} }
List<Field> fieldList = new ArrayList<Field>(); // Declared fields
Class tempClass = headClazz;
// When the parent class is null, it indicates that the parent class (Object class) has reached the top
// level.
while (tempClass != null) {
Collections.addAll(fieldList, tempClass.getDeclaredFields());
// Get the parent class and give it to yourself
tempClass = tempClass.getSuperclass();
}
ExcelIgnoreUnannotated excelIgnoreUnannotated =
(ExcelIgnoreUnannotated)headClazz.getAnnotation(ExcelIgnoreUnannotated.class);
// Screening of field
List<Field> defaultFieldList = new ArrayList<Field>(); List<Field> defaultFieldList = new ArrayList<Field>();
Map<Integer, Field> customFiledMap = new TreeMap<Integer, Field>(); Map<Integer, Field> customFiledMap = new TreeMap<Integer, Field>();
for (Field field : fieldList) { ClassUtils.declaredFields(headClazz, defaultFieldList, customFiledMap, ignoreMap, convertAllFiled);
ExcelIgnore excelIgnore = field.getAnnotation(ExcelIgnore.class);
if (excelIgnore != null) {
ignoreMap.put(field.getName(), field);
continue;
}
ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
boolean noExcelProperty = excelProperty == null
&& ((convertAllFiled != null && !convertAllFiled) || excelIgnoreUnannotated != null);
if (noExcelProperty) {
ignoreMap.put(field.getName(), field);
continue;
}
boolean isStaticFinalOrTransient =
(Modifier.isStatic(field.getModifiers()) && Modifier.isFinal(field.getModifiers()))
|| Modifier.isTransient(field.getModifiers());
if (excelProperty == null && isStaticFinalOrTransient) {
ignoreMap.put(field.getName(), field);
continue;
}
if (excelProperty == null || excelProperty.index() < 0) {
defaultFieldList.add(field);
continue;
}
if (customFiledMap.containsKey(excelProperty.index())) {
throw new ExcelGenerateException("The index of '" + customFiledMap.get(excelProperty.index()).getName()
+ "' and '" + field.getName() + "' must be inconsistent");
}
customFiledMap.put(excelProperty.index(), field);
}
int index = 0; int index = 0;
for (Field field : defaultFieldList) { for (Field field : defaultFieldList) {
while (customFiledMap.containsKey(index)) { while (customFiledMap.containsKey(index)) {
initOneColumnProperty(index, customFiledMap.get(index), Boolean.TRUE); Field customFiled = customFiledMap.get(index);
customFiledMap.remove(index); customFiledMap.remove(index);
if (!initOneColumnProperty(holder, index, customFiled, Boolean.TRUE)) {
index++;
}
}
if (!initOneColumnProperty(holder, index, field, Boolean.FALSE)) {
index++; index++;
} }
initOneColumnProperty(index, field, Boolean.FALSE);
index++;
} }
for (Map.Entry<Integer, Field> entry : customFiledMap.entrySet()) { for (Map.Entry<Integer, Field> entry : customFiledMap.entrySet()) {
initOneColumnProperty(entry.getKey(), entry.getValue(), Boolean.TRUE); initOneColumnProperty(holder, entry.getKey(), entry.getValue(), Boolean.TRUE);
} }
headKind = HeadKindEnum.CLASS; headKind = HeadKindEnum.CLASS;
} }
private void initOneColumnProperty(int index, Field field, Boolean forceIndex) { /**
* Initialization column property
*
* @param holder
* @param index
* @param field
* @param forceIndex
* @return Ignore current field
*/
private boolean initOneColumnProperty(Holder holder, int index, Field field, Boolean forceIndex) {
if (holder instanceof AbstractWriteHolder) {
if (((AbstractWriteHolder)holder).ignore(field.getName(), index)) {
return true;
}
}
ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class); ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
List<String> tmpHeadList = new ArrayList<String>(); List<String> tmpHeadList = new ArrayList<String>();
boolean notForceName = excelProperty == null || excelProperty.value().length <= 0 boolean notForceName = excelProperty == null || excelProperty.value().length <= 0
@ -206,6 +190,7 @@ public class ExcelHeadProperty {
headMap.put(index, head); headMap.put(index, head);
contentPropertyMap.put(index, excelContentProperty); contentPropertyMap.put(index, excelContentProperty);
fieldNameContentPropertyMap.put(field.getName(), excelContentProperty); fieldNameContentPropertyMap.put(field.getName(), excelContentProperty);
return false;
} }
public Class getHeadClazz() { public Class getHeadClazz() {

47
src/main/java/com/alibaba/excel/read/builder/AbstractExcelReaderParameterBuilder.java

@ -0,0 +1,47 @@
package com.alibaba.excel.read.builder;
import java.util.ArrayList;
import com.alibaba.excel.metadata.AbstractParameterBuilder;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.read.metadata.ReadBasicParameter;
/**
* Build ExcelBuilder
*
* @author Jiaju Zhuang
*/
public abstract class AbstractExcelReaderParameterBuilder<T extends AbstractExcelReaderParameterBuilder,
C extends ReadBasicParameter> extends AbstractParameterBuilder<T, C> {
/**
* Count the number of added heads when read sheet.
*
* <p>
* 0 - This Sheet has no head ,since the first row are the data
* <p>
* 1 - This Sheet has one row head , this is the default
* <p>
* 2 - This Sheet has two row head ,since the third row is the data
*
* @param headRowNumber
* @return
*/
public T headRowNumber(Integer headRowNumber) {
parameter().setHeadRowNumber(headRowNumber);
return self();
}
/**
* Custom type listener run after default
*
* @param readListener
* @return
*/
public T registerReadListener(ReadListener readListener) {
if (parameter().getCustomReadListenerList() == null) {
parameter().setCustomReadListenerList(new ArrayList<ReadListener>());
}
parameter().getCustomReadListenerList().add(readListener);
return self();
}
}

115
src/main/java/com/alibaba/excel/read/builder/ExcelReaderBuilder.java

@ -2,15 +2,15 @@ package com.alibaba.excel.read.builder;
import java.io.File; import java.io.File;
import java.io.InputStream; import java.io.InputStream;
import java.util.ArrayList;
import java.util.List; import javax.xml.parsers.SAXParserFactory;
import com.alibaba.excel.ExcelReader; import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.cache.ReadCache; import com.alibaba.excel.cache.ReadCache;
import com.alibaba.excel.cache.selector.ReadCacheSelector; import com.alibaba.excel.cache.selector.ReadCacheSelector;
import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.event.AnalysisEventListener; import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.read.listener.ModelBuildEventListener;
import com.alibaba.excel.read.listener.ReadListener; import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.read.metadata.ReadWorkbook; import com.alibaba.excel.read.metadata.ReadWorkbook;
import com.alibaba.excel.support.ExcelTypeEnum; import com.alibaba.excel.support.ExcelTypeEnum;
@ -20,7 +20,7 @@ import com.alibaba.excel.support.ExcelTypeEnum;
* *
* @author Jiaju Zhuang * @author Jiaju Zhuang
*/ */
public class ExcelReaderBuilder { public class ExcelReaderBuilder extends AbstractExcelReaderParameterBuilder<ExcelReaderBuilder, ReadWorkbook> {
/** /**
* Workbook * Workbook
*/ */
@ -131,105 +131,43 @@ public class ExcelReaderBuilder {
} }
/** /**
* Count the number of added heads when read sheet. * Whether the encryption
*
* <p>
* 0 - This Sheet has no head ,since the first row are the data
* <p>
* 1 - This Sheet has one row head , this is the default
* <p>
* 2 - This Sheet has two row head ,since the third row is the data
*
* @param headRowNumber
* @return
*/
public ExcelReaderBuilder headRowNumber(Integer headRowNumber) {
readWorkbook.setHeadRowNumber(headRowNumber);
return this;
}
/**
* You can only choose one of the {@link ExcelReaderBuilder#head(List)} and {@link ExcelReaderBuilder#head(Class)}
*
* @param head
* @return
*/
public ExcelReaderBuilder head(List<List<String>> head) {
readWorkbook.setHead(head);
return this;
}
/**
* You can only choose one of the {@link ExcelReaderBuilder#head(List)} and {@link ExcelReaderBuilder#head(Class)}
*
* @param clazz
* @return
*/
public ExcelReaderBuilder head(Class clazz) {
readWorkbook.setClazz(clazz);
return this;
}
/**
* Custom type conversions override the default.
*
* @param converter
* @return
*/
public ExcelReaderBuilder registerConverter(Converter converter) {
if (readWorkbook.getCustomConverterList() == null) {
readWorkbook.setCustomConverterList(new ArrayList<Converter>());
}
readWorkbook.getCustomConverterList().add(converter);
return this;
}
/**
* Custom type listener run after default
*
* @param readListener
* @return
*/
public ExcelReaderBuilder registerReadListener(ReadListener readListener) {
if (readWorkbook.getCustomReadListenerList() == null) {
readWorkbook.setCustomReadListenerList(new ArrayList<ReadListener>());
}
readWorkbook.getCustomReadListenerList().add(readListener);
return this;
}
/**
* true if date uses 1904 windowing, or false if using 1900 date windowing.
*
* default is false
* *
* @param use1904windowing * @param password
* @return * @return
*/ */
public ExcelReaderBuilder use1904windowing(Boolean use1904windowing) { public ExcelReaderBuilder password(String password) {
readWorkbook.setUse1904windowing(use1904windowing); readWorkbook.setPassword(password);
return this; return this;
} }
/** /**
* Automatic trim includes sheet name and content * SAXParserFactory used when reading xlsx.
* <p>
* The default will automatically find.
* <p>
* Please pass in the name of a class ,like : "com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl"
* *
* @param autoTrim * @see SAXParserFactory#newInstance()
* @see SAXParserFactory#newInstance(String, ClassLoader)
* @param xlsxSAXParserFactoryName
* @return * @return
*/ */
public ExcelReaderBuilder autoTrim(Boolean autoTrim) { public ExcelReaderBuilder xlsxSAXParserFactoryName(String xlsxSAXParserFactoryName) {
readWorkbook.setAutoTrim(autoTrim); readWorkbook.setXlsxSAXParserFactoryName(xlsxSAXParserFactoryName);
return this; return this;
} }
/** /**
* Whether the encryption * Whether to use the default listener, which is used by default.
* <p>
* The {@link ModelBuildEventListener} is loaded by default to convert the object.
* *
* @param password * @param useDefaultListener
* @return * @return
*/ */
public ExcelReaderBuilder password(String password) { public ExcelReaderBuilder useDefaultListener(Boolean useDefaultListener) {
readWorkbook.setPassword(password); readWorkbook.setUseDefaultListener(useDefaultListener);
return this; return this;
} }
@ -266,4 +204,9 @@ public class ExcelReaderBuilder {
} }
return excelReaderSheetBuilder; return excelReaderSheetBuilder;
} }
@Override
protected ReadWorkbook parameter() {
return readWorkbook;
}
} }

105
src/main/java/com/alibaba/excel/read/builder/ExcelReaderSheetBuilder.java

@ -1,14 +1,11 @@
package com.alibaba.excel.read.builder; package com.alibaba.excel.read.builder;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import com.alibaba.excel.ExcelReader; import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.event.SyncReadListener; import com.alibaba.excel.event.SyncReadListener;
import com.alibaba.excel.exception.ExcelAnalysisException; import com.alibaba.excel.exception.ExcelAnalysisException;
import com.alibaba.excel.exception.ExcelGenerateException; import com.alibaba.excel.exception.ExcelGenerateException;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.read.metadata.ReadSheet; import com.alibaba.excel.read.metadata.ReadSheet;
/** /**
@ -16,7 +13,7 @@ import com.alibaba.excel.read.metadata.ReadSheet;
* *
* @author Jiaju Zhuang * @author Jiaju Zhuang
*/ */
public class ExcelReaderSheetBuilder { public class ExcelReaderSheetBuilder extends AbstractExcelReaderParameterBuilder<ExcelReaderSheetBuilder, ReadSheet> {
private ExcelReader excelReader; private ExcelReader excelReader;
/** /**
* Sheet * Sheet
@ -54,98 +51,6 @@ public class ExcelReaderSheetBuilder {
return this; return this;
} }
/**
* Count the number of added heads when read sheet.
*
* <p>
* 0 - This Sheet has no head ,since the first row are the data
* <p>
* 1 - This Sheet has one row head , this is the default
* <p>
* 2 - This Sheet has two row head ,since the third row is the data
*
* @param headRowNumber
* @return
*/
public ExcelReaderSheetBuilder headRowNumber(Integer headRowNumber) {
readSheet.setHeadRowNumber(headRowNumber);
return this;
}
/**
* You can only choose one of the {@link ExcelReaderBuilder#head(List)} and {@link ExcelReaderBuilder#head(Class)}
*
* @param head
* @return
*/
public ExcelReaderSheetBuilder head(List<List<String>> head) {
readSheet.setHead(head);
return this;
}
/**
* You can only choose one of the {@link ExcelReaderBuilder#head(List)} and {@link ExcelReaderBuilder#head(Class)}
*
* @param clazz
* @return
*/
public ExcelReaderSheetBuilder head(Class clazz) {
readSheet.setClazz(clazz);
return this;
}
/**
* Custom type conversions override the default.
*
* @param converter
* @return
*/
public ExcelReaderSheetBuilder registerConverter(Converter converter) {
if (readSheet.getCustomConverterList() == null) {
readSheet.setCustomConverterList(new ArrayList<Converter>());
}
readSheet.getCustomConverterList().add(converter);
return this;
}
/**
* Custom type listener run after default
*
* @param readListener
* @return
*/
public ExcelReaderSheetBuilder registerReadListener(ReadListener readListener) {
if (readSheet.getCustomReadListenerList() == null) {
readSheet.setCustomReadListenerList(new ArrayList<ReadListener>());
}
readSheet.getCustomReadListenerList().add(readListener);
return this;
}
/**
* true if date uses 1904 windowing, or false if using 1900 date windowing.
*
* default is false
*
* @param use1904windowing
* @return
*/
public ExcelReaderSheetBuilder use1904windowing(Boolean use1904windowing) {
readSheet.setUse1904windowing(use1904windowing);
return this;
}
/**
* Automatic trim includes sheet name and content
*
* @param autoTrim
* @return
*/
public ExcelReaderSheetBuilder autoTrim(Boolean autoTrim) {
readSheet.setAutoTrim(autoTrim);
return this;
}
public ReadSheet build() { public ReadSheet build() {
return readSheet; return readSheet;
} }
@ -166,7 +71,7 @@ public class ExcelReaderSheetBuilder {
* *
* @return * @return
*/ */
public List<Object> doReadSync() { public <T> List<T> doReadSync() {
if (excelReader == null) { if (excelReader == null) {
throw new ExcelAnalysisException("Must use 'EasyExcelFactory.read().sheet()' to call this method"); throw new ExcelAnalysisException("Must use 'EasyExcelFactory.read().sheet()' to call this method");
} }
@ -174,7 +79,11 @@ public class ExcelReaderSheetBuilder {
registerReadListener(syncReadListener); registerReadListener(syncReadListener);
excelReader.read(build()); excelReader.read(build());
excelReader.finish(); excelReader.finish();
return syncReadListener.getList(); return (List<T>)syncReadListener.getList();
} }
@Override
protected ReadSheet parameter() {
return readSheet;
}
} }

6
src/main/java/com/alibaba/excel/read/listener/ModelBuildEventListener.java

@ -2,6 +2,7 @@ package com.alibaba.excel.read.listener;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -44,7 +45,7 @@ public class ModelBuildEventListener extends AbstractIgnoreExceptionReadListener
AnalysisContext context) { AnalysisContext context) {
int index = 0; int index = 0;
if (context.readWorkbookHolder().getDefaultReturnMap()) { if (context.readWorkbookHolder().getDefaultReturnMap()) {
Map<Integer, String> map = new HashMap<Integer, String>(cellDataMap.size() * 4 / 3 + 1); Map<Integer, String> map = new LinkedHashMap<Integer, String>(cellDataMap.size() * 4 / 3 + 1);
for (Map.Entry<Integer, CellData> entry : cellDataMap.entrySet()) { for (Map.Entry<Integer, CellData> entry : cellDataMap.entrySet()) {
Integer key = entry.getKey(); Integer key = entry.getKey();
CellData cellData = entry.getValue(); CellData cellData = entry.getValue();
@ -92,7 +93,8 @@ public class ModelBuildEventListener extends AbstractIgnoreExceptionReadListener
try { try {
resultModel = excelReadHeadProperty.getHeadClazz().newInstance(); resultModel = excelReadHeadProperty.getHeadClazz().newInstance();
} catch (Exception e) { } catch (Exception e) {
throw new ExcelDataConvertException( throw new ExcelDataConvertException(context.readRowHolder().getRowIndex(), 0,
new CellData(CellDataTypeEnum.EMPTY), null,
"Can not instance class: " + excelReadHeadProperty.getHeadClazz().getName(), e); "Can not instance class: " + excelReadHeadProperty.getHeadClazz().getName(), e);
} }
Map<Integer, Head> headMap = excelReadHeadProperty.getHeadMap(); Map<Integer, Head> headMap = excelReadHeadProperty.getHeadMap();

36
src/main/java/com/alibaba/excel/read/metadata/ReadWorkbook.java

@ -3,10 +3,13 @@ package com.alibaba.excel.read.metadata;
import java.io.File; import java.io.File;
import java.io.InputStream; import java.io.InputStream;
import javax.xml.parsers.SAXParserFactory;
import com.alibaba.excel.cache.ReadCache; import com.alibaba.excel.cache.ReadCache;
import com.alibaba.excel.cache.selector.ReadCacheSelector; import com.alibaba.excel.cache.selector.ReadCacheSelector;
import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener; import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.read.listener.ModelBuildEventListener;
import com.alibaba.excel.support.ExcelTypeEnum; import com.alibaba.excel.support.ExcelTypeEnum;
/** /**
@ -63,6 +66,23 @@ public class ReadWorkbook extends ReadBasicParameter {
* Whether the encryption * Whether the encryption
*/ */
private String password; private String password;
/**
* SAXParserFactory used when reading xlsx.
* <p>
* The default will automatically find.
* <p>
* Please pass in the name of a class ,like : "com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl"
*
* @see SAXParserFactory#newInstance()
* @see SAXParserFactory#newInstance(String, ClassLoader)
*/
private String xlsxSAXParserFactoryName;
/**
* Whether to use the default listener, which is used by default.
* <p>
* The {@link ModelBuildEventListener} is loaded by default to convert the object.
*/
private Boolean useDefaultListener;
/** /**
* The default is all excel objects.Default is true. * The default is all excel objects.Default is true.
* <p> * <p>
@ -176,4 +196,20 @@ public class ReadWorkbook extends ReadBasicParameter {
public void setPassword(String password) { public void setPassword(String password) {
this.password = password; this.password = password;
} }
public String getXlsxSAXParserFactoryName() {
return xlsxSAXParserFactoryName;
}
public void setXlsxSAXParserFactoryName(String xlsxSAXParserFactoryName) {
this.xlsxSAXParserFactoryName = xlsxSAXParserFactoryName;
}
public Boolean getUseDefaultListener() {
return useDefaultListener;
}
public void setUseDefaultListener(Boolean useDefaultListener) {
this.useDefaultListener = useDefaultListener;
}
} }

23
src/main/java/com/alibaba/excel/read/metadata/holder/AbstractReadHolder.java

@ -26,6 +26,7 @@ import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.read.listener.ReadListenerRegistryCenter; import com.alibaba.excel.read.listener.ReadListenerRegistryCenter;
import com.alibaba.excel.read.listener.event.AnalysisFinishEvent; import com.alibaba.excel.read.listener.event.AnalysisFinishEvent;
import com.alibaba.excel.read.metadata.ReadBasicParameter; import com.alibaba.excel.read.metadata.ReadBasicParameter;
import com.alibaba.excel.read.metadata.ReadWorkbook;
import com.alibaba.excel.read.metadata.property.ExcelReadHeadProperty; import com.alibaba.excel.read.metadata.property.ExcelReadHeadProperty;
import com.alibaba.excel.util.CollectionUtils; import com.alibaba.excel.util.CollectionUtils;
import com.alibaba.excel.util.ConverterUtils; import com.alibaba.excel.util.ConverterUtils;
@ -70,7 +71,7 @@ public abstract class AbstractReadHolder extends AbstractHolder implements ReadH
} }
// Initialization property // Initialization property
this.excelReadHeadProperty = new ExcelReadHeadProperty(getClazz(), getHead(), convertAllFiled); this.excelReadHeadProperty = new ExcelReadHeadProperty(this, getClazz(), getHead(), convertAllFiled);
if (readBasicParameter.getHeadRowNumber() == null) { if (readBasicParameter.getHeadRowNumber() == null) {
if (parentAbstractReadHolder == null) { if (parentAbstractReadHolder == null) {
if (excelReadHeadProperty.hasHead()) { if (excelReadHeadProperty.hasHead()) {
@ -91,7 +92,10 @@ public abstract class AbstractReadHolder extends AbstractHolder implements ReadH
this.readListenerList = new ArrayList<ReadListener>(parentAbstractReadHolder.getReadListenerList()); this.readListenerList = new ArrayList<ReadListener>(parentAbstractReadHolder.getReadListenerList());
} }
if (HolderEnum.WORKBOOK.equals(holderType())) { if (HolderEnum.WORKBOOK.equals(holderType())) {
readListenerList.add(new ModelBuildEventListener()); Boolean useDefaultListener = ((ReadWorkbook)readBasicParameter).getUseDefaultListener();
if (useDefaultListener == null || useDefaultListener) {
readListenerList.add(new ModelBuildEventListener());
}
} }
if (readBasicParameter.getCustomReadListenerList() != null if (readBasicParameter.getCustomReadListenerList() != null
&& !readBasicParameter.getCustomReadListenerList().isEmpty()) { && !readBasicParameter.getCustomReadListenerList().isEmpty()) {
@ -144,7 +148,7 @@ public abstract class AbstractReadHolder extends AbstractHolder implements ReadH
try { try {
readListenerException.onException(e, analysisContext); readListenerException.onException(e, analysisContext);
} catch (Exception exception) { } catch (Exception exception) {
throw new ExcelAnalysisException("Listen error!", exception); throw new ExcelAnalysisException(exception.getMessage(), exception);
} }
} }
break; break;
@ -158,7 +162,6 @@ public abstract class AbstractReadHolder extends AbstractHolder implements ReadH
if (currentheadRowNumber == rowIndex + 1) { if (currentheadRowNumber == rowIndex + 1) {
buildHead(analysisContext, cellDataMap); buildHead(analysisContext, cellDataMap);
} }
// Now is header // Now is header
for (ReadListener readListener : analysisContext.currentReadHolder().readListenerList()) { for (ReadListener readListener : analysisContext.currentReadHolder().readListenerList()) {
try { try {
@ -168,7 +171,7 @@ public abstract class AbstractReadHolder extends AbstractHolder implements ReadH
try { try {
readListenerException.onException(e, analysisContext); readListenerException.onException(e, analysisContext);
} catch (Exception exception) { } catch (Exception exception) {
throw new ExcelAnalysisException("Listen error!", exception); throw new ExcelAnalysisException(exception.getMessage(), exception);
} }
} }
break; break;
@ -191,8 +194,7 @@ public abstract class AbstractReadHolder extends AbstractHolder implements ReadH
if (!HeadKindEnum.CLASS.equals(analysisContext.currentReadHolder().excelReadHeadProperty().getHeadKind())) { if (!HeadKindEnum.CLASS.equals(analysisContext.currentReadHolder().excelReadHeadProperty().getHeadKind())) {
return; return;
} }
Map<Integer, String> dataMap = Map<Integer, String> dataMap = ConverterUtils.convertToStringMap(cellDataMap, analysisContext);
ConverterUtils.convertToStringMap(cellDataMap, analysisContext.currentReadHolder());
ExcelReadHeadProperty excelHeadPropertyData = analysisContext.readSheetHolder().excelReadHeadProperty(); ExcelReadHeadProperty excelHeadPropertyData = analysisContext.readSheetHolder().excelReadHeadProperty();
Map<Integer, Head> headMapData = excelHeadPropertyData.getHeadMap(); Map<Integer, Head> headMapData = excelHeadPropertyData.getHeadMap();
Map<Integer, ExcelContentProperty> contentPropertyMapData = excelHeadPropertyData.getContentPropertyMap(); Map<Integer, ExcelContentProperty> contentPropertyMapData = excelHeadPropertyData.getContentPropertyMap();
@ -206,9 +208,12 @@ public abstract class AbstractReadHolder extends AbstractHolder implements ReadH
tmpContentPropertyMap.put(entry.getKey(), contentPropertyMapData.get(entry.getKey())); tmpContentPropertyMap.put(entry.getKey(), contentPropertyMapData.get(entry.getKey()));
continue; continue;
} }
String headName = headData.getHeadNameList().get(0); List<String> headNameList = headData.getHeadNameList();
String headName = headNameList.get(headNameList.size() - 1);
for (Map.Entry<Integer, String> stringEntry : dataMap.entrySet()) { for (Map.Entry<Integer, String> stringEntry : dataMap.entrySet()) {
if (stringEntry == null) {
continue;
}
String headString = stringEntry.getValue(); String headString = stringEntry.getValue();
Integer stringKey = stringEntry.getKey(); Integer stringKey = stringEntry.getKey();
if (StringUtils.isEmpty(headString)) { if (StringUtils.isEmpty(headString)) {

28
src/main/java/com/alibaba/excel/read/metadata/holder/ReadSheetHolder.java

@ -26,10 +26,9 @@ public class ReadSheetHolder extends AbstractReadHolder {
*/ */
private String sheetName; private String sheetName;
/** /**
* get total row , Data may be inaccurate * Gets the total number of rows , data may be inaccurate
*/ */
@Deprecated private Integer approximateTotalRowNumber;
private Integer total;
public ReadSheetHolder(ReadSheet readSheet, ReadWorkbookHolder readWorkbookHolder) { public ReadSheetHolder(ReadSheet readSheet, ReadWorkbookHolder readWorkbookHolder) {
super(readSheet, readWorkbookHolder, readWorkbookHolder.getReadWorkbook().getConvertAllFiled()); super(readSheet, readWorkbookHolder, readWorkbookHolder.getReadWorkbook().getConvertAllFiled());
@ -71,12 +70,29 @@ public class ReadSheetHolder extends AbstractReadHolder {
this.sheetName = sheetName; this.sheetName = sheetName;
} }
/**
*
* Approximate total number of rows
*
* @return
* @see #getApproximateTotalRowNumber()
*/
@Deprecated
public Integer getTotal() { public Integer getTotal() {
return total; return approximateTotalRowNumber;
}
/**
* Approximate total number of rows
*
* @return
*/
public Integer getApproximateTotalRowNumber() {
return approximateTotalRowNumber;
} }
public void setTotal(Integer total) { public void setApproximateTotalRowNumber(Integer approximateTotalRowNumber) {
this.total = total; this.approximateTotalRowNumber = approximateTotalRowNumber;
} }
@Override @Override

22
src/main/java/com/alibaba/excel/read/metadata/holder/ReadWorkbookHolder.java

@ -6,6 +6,8 @@ import java.io.InputStream;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import javax.xml.parsers.SAXParserFactory;
import org.apache.poi.openxml4j.opc.OPCPackage; import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.apache.poi.poifs.filesystem.POIFSFileSystem;
@ -83,6 +85,17 @@ public class ReadWorkbookHolder extends AbstractReadHolder {
* Whether the encryption * Whether the encryption
*/ */
private String password; private String password;
/**
* SAXParserFactory used when reading xlsx.
* <p>
* The default will automatically find.
* <p>
* Please pass in the name of a class ,like : "com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl"
*
* @see SAXParserFactory#newInstance()
* @see SAXParserFactory#newInstance(String, ClassLoader)
*/
private String xlsxSAXParserFactoryName;
/** /**
* The default is all excel objects.if true , you can use {@link com.alibaba.excel.annotation.ExcelIgnore} ignore a * The default is all excel objects.if true , you can use {@link com.alibaba.excel.annotation.ExcelIgnore} ignore a
* field. if false , you must use {@link com.alibaba.excel.annotation.ExcelProperty} to use a filed. * field. if false , you must use {@link com.alibaba.excel.annotation.ExcelProperty} to use a filed.
@ -172,6 +185,7 @@ public class ReadWorkbookHolder extends AbstractReadHolder {
} else { } else {
this.defaultReturnMap = readWorkbook.getDefaultReturnMap(); this.defaultReturnMap = readWorkbook.getDefaultReturnMap();
} }
this.xlsxSAXParserFactoryName = readWorkbook.getXlsxSAXParserFactoryName();
this.hasReadSheet = new HashSet<Integer>(); this.hasReadSheet = new HashSet<Integer>();
this.ignoreRecord03 = Boolean.FALSE; this.ignoreRecord03 = Boolean.FALSE;
this.password = readWorkbook.getPassword(); this.password = readWorkbook.getPassword();
@ -321,6 +335,14 @@ public class ReadWorkbookHolder extends AbstractReadHolder {
this.password = password; this.password = password;
} }
public String getXlsxSAXParserFactoryName() {
return xlsxSAXParserFactoryName;
}
public void setXlsxSAXParserFactoryName(String xlsxSAXParserFactoryName) {
this.xlsxSAXParserFactoryName = xlsxSAXParserFactoryName;
}
@Override @Override
public HolderEnum holderType() { public HolderEnum holderType() {
return HolderEnum.WORKBOOK; return HolderEnum.WORKBOOK;

5
src/main/java/com/alibaba/excel/read/metadata/property/ExcelReadHeadProperty.java

@ -2,6 +2,7 @@ package com.alibaba.excel.read.metadata.property;
import java.util.List; import java.util.List;
import com.alibaba.excel.metadata.Holder;
import com.alibaba.excel.metadata.property.ExcelHeadProperty; import com.alibaba.excel.metadata.property.ExcelHeadProperty;
/** /**
@ -11,7 +12,7 @@ import com.alibaba.excel.metadata.property.ExcelHeadProperty;
*/ */
public class ExcelReadHeadProperty extends ExcelHeadProperty { public class ExcelReadHeadProperty extends ExcelHeadProperty {
public ExcelReadHeadProperty(Class headClazz, List<List<String>> head, Boolean convertAllFiled) { public ExcelReadHeadProperty(Holder holder, Class headClazz, List<List<String>> head, Boolean convertAllFiled) {
super(headClazz, head, convertAllFiled); super(holder, headClazz, head, convertAllFiled);
} }
} }

150
src/main/java/com/alibaba/excel/util/ClassUtils.java

@ -0,0 +1,150 @@
package com.alibaba.excel.util;
import java.lang.ref.SoftReference;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.exception.ExcelCommonException;
import com.alibaba.excel.metadata.BaseRowModel;
/**
* Class utils
*
* @author Jiaju Zhuang
**/
public class ClassUtils {
private static final Map<Class, SoftReference<FieldCache>> FIELD_CACHE =
new ConcurrentHashMap<Class, SoftReference<FieldCache>>();
public static void declaredFields(Class clazz, List<Field> defaultFieldList, Map<Integer, Field> customFiledMap,
Map<String, Field> ignoreMap, Boolean convertAllFiled) {
FieldCache fieldCache = getFieldCache(clazz, convertAllFiled);
if (fieldCache != null) {
defaultFieldList.addAll(fieldCache.getDefaultFieldList());
customFiledMap.putAll(fieldCache.getCustomFiledMap());
ignoreMap.putAll(fieldCache.getIgnoreMap());
}
}
public static void declaredFields(Class clazz, List<Field> fieldList, Boolean convertAllFiled) {
FieldCache fieldCache = getFieldCache(clazz, convertAllFiled);
if (fieldCache != null) {
fieldList.addAll(fieldCache.getAllFieldList());
}
}
private static FieldCache getFieldCache(Class clazz, Boolean convertAllFiled) {
if (clazz == null) {
return null;
}
SoftReference<FieldCache> fieldCacheSoftReference = FIELD_CACHE.get(clazz);
if (fieldCacheSoftReference != null && fieldCacheSoftReference.get() != null) {
return fieldCacheSoftReference.get();
}
synchronized (clazz) {
fieldCacheSoftReference = FIELD_CACHE.get(clazz);
if (fieldCacheSoftReference != null && fieldCacheSoftReference.get() != null) {
return fieldCacheSoftReference.get();
}
declaredFields(clazz, convertAllFiled);
}
return FIELD_CACHE.get(clazz).get();
}
private static void declaredFields(Class clazz, Boolean convertAllFiled) {
List<Field> tempFieldList = new ArrayList<Field>();
Class tempClass = clazz;
// When the parent class is null, it indicates that the parent class (Object class) has reached the top
// level.
while (tempClass != null && tempClass != BaseRowModel.class) {
Collections.addAll(tempFieldList, tempClass.getDeclaredFields());
// Get the parent class and give it to yourself
tempClass = tempClass.getSuperclass();
}
// Screening of field
List<Field> defaultFieldList = new ArrayList<Field>();
Map<Integer, Field> customFiledMap = new TreeMap<Integer, Field>();
List<Field> allFieldList = new ArrayList<Field>();
Map<String, Field> ignoreMap = new HashMap<String, Field>(16);
ExcelIgnoreUnannotated excelIgnoreUnannotated =
(ExcelIgnoreUnannotated)clazz.getAnnotation(ExcelIgnoreUnannotated.class);
for (Field field : tempFieldList) {
ExcelIgnore excelIgnore = field.getAnnotation(ExcelIgnore.class);
if (excelIgnore != null) {
ignoreMap.put(field.getName(), field);
continue;
}
ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
boolean noExcelProperty = excelProperty == null
&& ((convertAllFiled != null && !convertAllFiled) || excelIgnoreUnannotated != null);
if (noExcelProperty) {
ignoreMap.put(field.getName(), field);
continue;
}
boolean isStaticFinalOrTransient =
(Modifier.isStatic(field.getModifiers()) && Modifier.isFinal(field.getModifiers()))
|| Modifier.isTransient(field.getModifiers());
if (excelProperty == null && isStaticFinalOrTransient) {
ignoreMap.put(field.getName(), field);
continue;
}
if (excelProperty == null || excelProperty.index() < 0) {
defaultFieldList.add(field);
allFieldList.add(field);
continue;
}
if (customFiledMap.containsKey(excelProperty.index())) {
throw new ExcelCommonException("The index of '" + customFiledMap.get(excelProperty.index()).getName()
+ "' and '" + field.getName() + "' must be inconsistent");
}
customFiledMap.put(excelProperty.index(), field);
allFieldList.add(field);
}
FIELD_CACHE.put(clazz,
new SoftReference<FieldCache>(new FieldCache(defaultFieldList, customFiledMap, allFieldList, ignoreMap)));
}
private static class FieldCache {
private List<Field> defaultFieldList;
private Map<Integer, Field> customFiledMap;
private List<Field> allFieldList;
private Map<String, Field> ignoreMap;
public FieldCache(List<Field> defaultFieldList, Map<Integer, Field> customFiledMap, List<Field> allFieldList,
Map<String, Field> ignoreMap) {
this.defaultFieldList = defaultFieldList;
this.customFiledMap = customFiledMap;
this.allFieldList = allFieldList;
this.ignoreMap = ignoreMap;
}
public List<Field> getDefaultFieldList() {
return defaultFieldList;
}
public Map<Integer, Field> getCustomFiledMap() {
return customFiledMap;
}
public List<Field> getAllFieldList() {
return allFieldList;
}
public Map<String, Field> getIgnoreMap() {
return ignoreMap;
}
}
}

30
src/main/java/com/alibaba/excel/util/ConverterUtils.java

@ -6,6 +6,7 @@ import java.lang.reflect.Type;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.converters.Converter; import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.converters.ConverterKeyBuild; import com.alibaba.excel.converters.ConverterKeyBuild;
import com.alibaba.excel.enums.CellDataTypeEnum; import com.alibaba.excel.enums.CellDataTypeEnum;
@ -28,28 +29,37 @@ public class ConverterUtils {
* Convert it into a String map * Convert it into a String map
* *
* @param cellDataMap * @param cellDataMap
* @param readHolder * @param context
* @return * @return
*/ */
public static Map<Integer, String> convertToStringMap(Map<Integer, CellData> cellDataMap, ReadHolder readHolder) { public static Map<Integer, String> convertToStringMap(Map<Integer, CellData> cellDataMap, AnalysisContext context) {
Map<Integer, String> stringMap = new HashMap<Integer, String>(cellDataMap.size() * 4 / 3 + 1); Map<Integer, String> stringMap = new HashMap<Integer, String>(cellDataMap.size() * 4 / 3 + 1);
ReadHolder currentReadHolder = context.currentReadHolder();
int index = 0;
for (Map.Entry<Integer, CellData> entry : cellDataMap.entrySet()) { for (Map.Entry<Integer, CellData> entry : cellDataMap.entrySet()) {
Integer key = entry.getKey();
CellData cellData = entry.getValue(); CellData cellData = entry.getValue();
while (index < key) {
stringMap.put(index, null);
index++;
}
index++;
if (cellData.getType() == CellDataTypeEnum.EMPTY) { if (cellData.getType() == CellDataTypeEnum.EMPTY) {
stringMap.put(entry.getKey(), null); stringMap.put(key, null);
continue; continue;
} }
Converter converter = Converter converter =
readHolder.converterMap().get(ConverterKeyBuild.buildKey(String.class, cellData.getType())); currentReadHolder.converterMap().get(ConverterKeyBuild.buildKey(String.class, cellData.getType()));
if (converter == null) { if (converter == null) {
throw new ExcelDataConvertException( throw new ExcelDataConvertException(context.readRowHolder().getRowIndex(), key, cellData, null,
"Converter not found, convert " + cellData.getType() + " to String"); "Converter not found, convert " + cellData.getType() + " to String");
} }
try { try {
stringMap.put(entry.getKey(), stringMap.put(key,
(String)(converter.convertToJavaData(cellData, null, readHolder.globalConfiguration()))); (String)(converter.convertToJavaData(cellData, null, currentReadHolder.globalConfiguration())));
} catch (Exception e) { } catch (Exception e) {
throw new ExcelDataConvertException("Convert data " + cellData + " to String error ", e); throw new ExcelDataConvertException(context.readRowHolder().getRowIndex(), key, cellData, null,
"Convert data " + cellData + " to String error ", e);
} }
} }
return stringMap; return stringMap;
@ -116,13 +126,13 @@ public class ConverterUtils {
converter = converterMap.get(ConverterKeyBuild.buildKey(clazz, cellData.getType())); converter = converterMap.get(ConverterKeyBuild.buildKey(clazz, cellData.getType()));
} }
if (converter == null) { if (converter == null) {
throw new ExcelDataConvertException(rowIndex, columnIndex, contentProperty, throw new ExcelDataConvertException(rowIndex, columnIndex, cellData, contentProperty,
"Converter not found, convert " + cellData.getType() + " to " + clazz.getName()); "Converter not found, convert " + cellData.getType() + " to " + clazz.getName());
} }
try { try {
return converter.convertToJavaData(cellData, contentProperty, globalConfiguration); return converter.convertToJavaData(cellData, contentProperty, globalConfiguration);
} catch (Exception e) { } catch (Exception e) {
throw new ExcelDataConvertException(rowIndex, columnIndex, contentProperty, throw new ExcelDataConvertException(rowIndex, columnIndex, cellData, contentProperty,
"Convert data " + cellData + " to " + clazz + " error ", e); "Convert data " + cellData + " to " + clazz + " error ", e);
} }
} }

218
src/main/java/com/alibaba/excel/util/DateUtils.java

@ -1,10 +1,12 @@
package com.alibaba.excel.util; package com.alibaba.excel.util;
import java.text.DateFormat;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
import java.util.HashMap;
import com.alibaba.excel.exception.ExcelDataConvertException; import java.util.Map;
import java.util.regex.Pattern;
/** /**
* Date utils * Date utils
@ -12,6 +14,34 @@ import com.alibaba.excel.exception.ExcelDataConvertException;
* @author Jiaju Zhuang * @author Jiaju Zhuang
**/ **/
public class DateUtils { public class DateUtils {
/**
* Is a cache of dates
*/
private static final ThreadLocal<Map<Integer, Boolean>> DATE_THREAD_LOCAL =
new ThreadLocal<Map<Integer, Boolean>>();
/**
* Is a cache of dates
*/
private static final ThreadLocal<Map<String, SimpleDateFormat>> DATE_FORMAT_THREAD_LOCAL =
new ThreadLocal<Map<String, SimpleDateFormat>>();
/**
* The following patterns are used in {@link #isADateFormat(Integer, String)}
*/
private static final Pattern date_ptrn1 = Pattern.compile("^\\[\\$\\-.*?\\]");
private static final Pattern date_ptrn2 = Pattern.compile("^\\[[a-zA-Z]+\\]");
private static final Pattern date_ptrn3a = Pattern.compile("[yYmMdDhHsS]");
// add "\u5e74 \u6708 \u65e5" for Chinese/Japanese date format:2017 \u5e74 2 \u6708 7 \u65e5
private static final Pattern date_ptrn3b =
Pattern.compile("^[\\[\\]yYmMdDhHsS\\-T/\u5e74\u6708\u65e5,. :\"\\\\]+0*[ampAMP/]*$");
// elapsed time patterns: [h],[m] and [s]
private static final Pattern date_ptrn4 = Pattern.compile("^\\[([hH]+|[mM]+|[sS]+)\\]");
// for format which start with "[DBNum1]" or "[DBNum2]" or "[DBNum3]" could be a Chinese date
private static final Pattern date_ptrn5 = Pattern.compile("^\\[DBNum(1|2|3)\\]");
// for format which start with "年" or "月" or "日" or "时" or "分" or "秒" could be a Chinese date
private static final Pattern date_ptrn6 = Pattern.compile("(年|月|日|时|分|秒)+");
public static final String DATE_FORMAT_10 = "yyyy-MM-dd";
public static final String DATE_FORMAT_14 = "yyyyMMddHHmmss"; public static final String DATE_FORMAT_14 = "yyyyMMddHHmmss";
public static final String DATE_FORMAT_17 = "yyyyMMdd HH:mm:ss"; public static final String DATE_FORMAT_17 = "yyyyMMdd HH:mm:ss";
public static final String DATE_FORMAT_19 = "yyyy-MM-dd HH:mm:ss"; public static final String DATE_FORMAT_19 = "yyyy-MM-dd HH:mm:ss";
@ -32,7 +62,7 @@ public class DateUtils {
if (StringUtils.isEmpty(dateFormat)) { if (StringUtils.isEmpty(dateFormat)) {
dateFormat = switchDateFormat(dateString); dateFormat = switchDateFormat(dateString);
} }
return new SimpleDateFormat(dateFormat).parse(dateString); return getCacheDateFormat(dateFormat).parse(dateString);
} }
/** /**
@ -65,8 +95,10 @@ public class DateUtils {
return DATE_FORMAT_17; return DATE_FORMAT_17;
case 14: case 14:
return DATE_FORMAT_14; return DATE_FORMAT_14;
case 10:
return DATE_FORMAT_10;
default: default:
throw new ExcelDataConvertException("can not find date format for:" + dateString); throw new IllegalArgumentException("can not find date format for:" + dateString);
} }
} }
@ -96,6 +128,182 @@ public class DateUtils {
if (StringUtils.isEmpty(dateFormat)) { if (StringUtils.isEmpty(dateFormat)) {
dateFormat = DATE_FORMAT_19; dateFormat = DATE_FORMAT_19;
} }
return new SimpleDateFormat(dateFormat).format(date); return getCacheDateFormat(dateFormat).format(date);
}
private static DateFormat getCacheDateFormat(String dateFormat) {
Map<String, SimpleDateFormat> dateFormatMap = DATE_FORMAT_THREAD_LOCAL.get();
if (dateFormatMap == null) {
dateFormatMap = new HashMap<String, SimpleDateFormat>();
DATE_FORMAT_THREAD_LOCAL.set(dateFormatMap);
} else {
SimpleDateFormat dateFormatCached = dateFormatMap.get(dateFormat);
if (dateFormatCached != null) {
return dateFormatCached;
}
}
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormat);
dateFormatMap.put(dateFormat, simpleDateFormat);
return simpleDateFormat;
}
/**
* Determine if it is a date format.
*
* @param formatIndex
* @param formatString
* @return
*/
public static boolean isADateFormat(Integer formatIndex, String formatString) {
if (formatIndex == null) {
return false;
}
Map<Integer, Boolean> isDateCache = DATE_THREAD_LOCAL.get();
if (isDateCache == null) {
isDateCache = new HashMap<Integer, Boolean>();
DATE_THREAD_LOCAL.set(isDateCache);
} else {
Boolean isDateCachedData = isDateCache.get(formatIndex);
if (isDateCachedData != null) {
return isDateCachedData;
}
}
boolean isDate = isADateFormatUncached(formatIndex, formatString);
isDateCache.put(formatIndex, isDate);
return isDate;
}
/**
* Determine if it is a date format.
*
* @param formatIndex
* @param formatString
* @return
*/
public static boolean isADateFormatUncached(Integer formatIndex, String formatString) {
// First up, is this an internal date format?
if (isInternalDateFormat(formatIndex)) {
return true;
}
if (StringUtils.isEmpty(formatString)) {
return false;
}
String fs = formatString;
final int length = fs.length();
StringBuilder sb = new StringBuilder(length);
for (int i = 0; i < length; i++) {
char c = fs.charAt(i);
if (i < length - 1) {
char nc = fs.charAt(i + 1);
if (c == '\\') {
switch (nc) {
case '-':
case ',':
case '.':
case ' ':
case '\\':
// skip current '\' and continue to the next char
continue;
}
} else if (c == ';' && nc == '@') {
i++;
// skip ";@" duplets
continue;
}
}
sb.append(c);
}
fs = sb.toString();
// short-circuit if it indicates elapsed time: [h], [m] or [s]
if (date_ptrn4.matcher(fs).matches()) {
return true;
}
// If it starts with [DBNum1] or [DBNum2] or [DBNum3]
// then it could be a Chinese date
fs = date_ptrn5.matcher(fs).replaceAll("");
// If it starts with [$-...], then could be a date, but
// who knows what that starting bit is all about
fs = date_ptrn1.matcher(fs).replaceAll("");
// If it starts with something like [Black] or [Yellow],
// then it could be a date
fs = date_ptrn2.matcher(fs).replaceAll("");
// You're allowed something like dd/mm/yy;[red]dd/mm/yy
// which would place dates before 1900/1904 in red
// For now, only consider the first one
final int separatorIndex = fs.indexOf(';');
if (0 < separatorIndex && separatorIndex < fs.length() - 1) {
fs = fs.substring(0, separatorIndex);
}
// Ensure it has some date letters in it
// (Avoids false positives on the rest of pattern 3)
if (!date_ptrn3a.matcher(fs).find()) {
return false;
}
// If we get here, check it's only made up, in any case, of:
// y m d h s - \ / , . : [ ] T
// optionally followed by AM/PM
boolean result = date_ptrn3b.matcher(fs).matches();
if (result) {
return true;
}
result = date_ptrn6.matcher(fs).find();
return result;
}
/**
* Given a format ID this will check whether the format represents an internal excel date format or not.
*
* @see #isADateFormat(Integer, String)
*/
public static boolean isInternalDateFormat(int format) {
switch (format) {
// Internal Date Formats as described on page 427 in
// Microsoft Excel Dev's Kit...
// 14-22
case 0x0e:
case 0x0f:
case 0x10:
case 0x11:
case 0x12:
case 0x13:
case 0x14:
case 0x15:
case 0x16:
// 27-36
case 0x1b:
case 0x1c:
case 0x1d:
case 0x1e:
case 0x1f:
case 0x20:
case 0x21:
case 0x22:
case 0x23:
case 0x24:
// 45-47
case 0x2d:
case 0x2e:
case 0x2f:
// 50-58
case 0x32:
case 0x33:
case 0x34:
case 0x35:
case 0x36:
case 0x37:
case 0x38:
case 0x39:
case 0x3a:
return true;
}
return false;
}
public static void removeThreadLocalCache() {
DATE_THREAD_LOCAL.remove();
DATE_FORMAT_THREAD_LOCAL.remove();
} }
} }

110
src/main/java/com/alibaba/excel/util/FileUtils.java

@ -9,24 +9,50 @@ import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.UUID; import java.util.UUID;
import org.apache.poi.util.DefaultTempFileCreationStrategy;
import org.apache.poi.util.TempFile;
import com.alibaba.excel.exception.ExcelAnalysisException; import com.alibaba.excel.exception.ExcelAnalysisException;
import com.alibaba.excel.exception.ExcelGenerateException; import com.alibaba.excel.exception.ExcelCommonException;
/** /**
* *
* @author jipengfei * @author jipengfei
*/ */
public class FileUtils { public class FileUtils {
public static final String POI_FILES = "poifiles";
public static final String EX_CACHE = "excache";
/**
* If a server has multiple projects in use at the same time, a directory with the same name will be created under
* the temporary directory, but each project is run by a different user, so there is a permission problem, so each
* project creates a unique UUID as a separate Temporary Files.
*/
private static String tempFilePrefix =
System.getProperty(TempFile.JAVA_IO_TMPDIR) + File.separator + UUID.randomUUID().toString() + File.separator;
/**
* Used to store poi temporary files.
*/
private static String poiFilesPath = tempFilePrefix + POI_FILES + File.separator;
/**
* Used to store easy excel temporary files.
*/
private static String cachePath = tempFilePrefix + EX_CACHE + File.separator;
private static final String JAVA_IO_TMPDIR = "java.io.tmpdir";
private static final String POIFILES = "poifiles";
private static final String CACHE = "excache";
private static final int WRITE_BUFF_SIZE = 8192; private static final int WRITE_BUFF_SIZE = 8192;
private FileUtils() {} private FileUtils() {}
static {
// Create a temporary directory in advance
File tempFile = new File(tempFilePrefix);
createDirectory(tempFile);
tempFile.deleteOnExit();
// Initialize the cache directory
File cacheFile = new File(cachePath);
createDirectory(cacheFile);
cacheFile.deleteOnExit();
}
/** /**
* Reads the contents of a file into a byte array. * The file is always closed. * Reads the contents of a file into a byte array. * The file is always closed.
* *
@ -106,19 +132,31 @@ public class FileUtils {
} }
} }
/**
*/
public static void createPoiFilesDirectory() { public static void createPoiFilesDirectory() {
createTmpDirectory(POIFILES); File poiFilesPathFile = new File(poiFilesPath);
createDirectory(poiFilesPathFile);
TempFile.setTempFileCreationStrategy(new DefaultTempFileCreationStrategy(poiFilesPathFile));
poiFilesPathFile.deleteOnExit();
} }
public static File createCacheTmpFile() { public static File createCacheTmpFile() {
File directory = createTmpDirectory(CACHE); return createDirectory(new File(cachePath + UUID.randomUUID().toString()));
File cache = new File(directory.getPath(), UUID.randomUUID().toString()); }
if (!cache.mkdir()) {
throw new ExcelGenerateException("Can not create temp file!"); public static File createTmpFile(String fileName) {
File directory = createDirectory(new File(tempFilePrefix));
return new File(directory, fileName);
}
/**
*
* @param directory
*/
private static File createDirectory(File directory) {
if (!directory.exists() && !directory.mkdirs()) {
throw new ExcelCommonException("Cannot create directory:" + directory.getAbsolutePath());
} }
return cache; return directory;
} }
/** /**
@ -144,35 +182,27 @@ public class FileUtils {
} }
} }
public static File createTmpDirectory(String path) { public static String getTempFilePrefix() {
String tmpDir = System.getProperty(JAVA_IO_TMPDIR); return tempFilePrefix;
if (tmpDir == null) {
throw new RuntimeException(
"Systems temporary directory not defined - set the -D" + JAVA_IO_TMPDIR + " jvm property!");
}
File directory = new File(tmpDir, path);
if (!directory.exists()) {
syncCreatePoiFilesDirectory(directory);
}
return directory;
} }
public static File createTmpFile(String fileName) { public static void setTempFilePrefix(String tempFilePrefix) {
String tmpDir = System.getProperty(JAVA_IO_TMPDIR); FileUtils.tempFilePrefix = tempFilePrefix;
if (tmpDir == null) {
throw new RuntimeException(
"Systems temporary directory not defined - set the -D" + JAVA_IO_TMPDIR + " jvm property!");
}
return new File(tmpDir, fileName);
} }
/** public static String getPoiFilesPath() {
* return poiFilesPath;
* @param directory }
*/
private static synchronized void syncCreatePoiFilesDirectory(File directory) { public static void setPoiFilesPath(String poiFilesPath) {
if (!directory.exists()) { FileUtils.poiFilesPath = poiFilesPath;
directory.mkdirs(); }
}
public static String getCachePath() {
return cachePath;
}
public static void setCachePath(String cachePath) {
FileUtils.cachePath = cachePath;
} }
} }

46
src/main/java/com/alibaba/excel/util/NumberDataFormatterUtils.java

@ -0,0 +1,46 @@
package com.alibaba.excel.util;
import com.alibaba.excel.metadata.DataFormatter;
import com.alibaba.excel.metadata.GlobalConfiguration;
/**
* Convert number data, including date.
*
* @author Jiaju Zhuang
**/
public class NumberDataFormatterUtils {
/**
* Cache DataFormatter.
*/
private static final ThreadLocal<DataFormatter> DATA_FORMATTER_THREAD_LOCAL = new ThreadLocal<DataFormatter>();
/**
* Format number data.
*
* @param data
* @param dataFormat
* Not null.
* @param dataFormatString
* @param globalConfiguration
* @return
*/
public static String format(Double data, Integer dataFormat, String dataFormatString,
GlobalConfiguration globalConfiguration) {
DataFormatter dataFormatter = DATA_FORMATTER_THREAD_LOCAL.get();
if (dataFormatter == null) {
if (globalConfiguration != null) {
dataFormatter =
new DataFormatter(globalConfiguration.getLocale(), globalConfiguration.getUse1904windowing());
} else {
dataFormatter = new DataFormatter();
}
DATA_FORMATTER_THREAD_LOCAL.set(dataFormatter);
}
return dataFormatter.format(data, dataFormat, dataFormatString);
}
public static void removeThreadLocalCache() {
DATA_FORMATTER_THREAD_LOCAL.remove();
}
}

17
src/main/java/com/alibaba/excel/util/WorkBookUtil.java

@ -31,12 +31,21 @@ public class WorkBookUtil {
if (writeWorkbookHolder.getTempTemplateInputStream() != null) { if (writeWorkbookHolder.getTempTemplateInputStream() != null) {
XSSFWorkbook xssfWorkbook = new XSSFWorkbook(writeWorkbookHolder.getTempTemplateInputStream()); XSSFWorkbook xssfWorkbook = new XSSFWorkbook(writeWorkbookHolder.getTempTemplateInputStream());
writeWorkbookHolder.setCachedWorkbook(xssfWorkbook); writeWorkbookHolder.setCachedWorkbook(xssfWorkbook);
writeWorkbookHolder.setWorkbook(new SXSSFWorkbook(xssfWorkbook, ROW_ACCESS_WINDOW_SIZE)); if (writeWorkbookHolder.getInMemory()) {
writeWorkbookHolder.setWorkbook(xssfWorkbook);
} else {
writeWorkbookHolder.setWorkbook(new SXSSFWorkbook(xssfWorkbook, ROW_ACCESS_WINDOW_SIZE));
}
return; return;
} }
SXSSFWorkbook sxssWorkbook = new SXSSFWorkbook(ROW_ACCESS_WINDOW_SIZE); Workbook workbook = null;
writeWorkbookHolder.setCachedWorkbook(sxssWorkbook); if (writeWorkbookHolder.getInMemory()) {
writeWorkbookHolder.setWorkbook(sxssWorkbook); workbook = new XSSFWorkbook();
} else {
workbook = new SXSSFWorkbook(ROW_ACCESS_WINDOW_SIZE);
}
writeWorkbookHolder.setCachedWorkbook(workbook);
writeWorkbookHolder.setWorkbook(workbook);
return; return;
} }
HSSFWorkbook hssfWorkbook; HSSFWorkbook hssfWorkbook;

5
src/main/java/com/alibaba/excel/write/ExcelBuilder.java

@ -71,11 +71,14 @@ public interface ExcelBuilder {
/** /**
* Close io * Close io
*
* @param onException
*/ */
void finish(); void finish(boolean onException);
/** /**
* add password * add password
*
* @param data * @param data
* @param writeSheet * @param writeSheet
* @param writeTable * @param writeTable

31
src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java

@ -25,16 +25,19 @@ public class ExcelBuilderImpl implements ExcelBuilder {
private ExcelWriteFillExecutor excelWriteFillExecutor; private ExcelWriteFillExecutor excelWriteFillExecutor;
private ExcelWriteAddExecutor excelWriteAddExecutor; private ExcelWriteAddExecutor excelWriteAddExecutor;
static {
// Create temporary cache directory at initialization time to avoid POI concurrent write bugs
FileUtils.createPoiFilesDirectory();
}
public ExcelBuilderImpl(WriteWorkbook writeWorkbook) { public ExcelBuilderImpl(WriteWorkbook writeWorkbook) {
try { try {
// Create temporary cache directory at initialization time to avoid POI concurrent write bugs
FileUtils.createPoiFilesDirectory();
context = new WriteContextImpl(writeWorkbook); context = new WriteContextImpl(writeWorkbook);
} catch (RuntimeException e) { } catch (RuntimeException e) {
finish(); finishOnException();
throw e; throw e;
} catch (Throwable e) { } catch (Throwable e) {
finish(); finishOnException();
throw new ExcelGenerateException(e); throw new ExcelGenerateException(e);
} }
} }
@ -57,10 +60,10 @@ public class ExcelBuilderImpl implements ExcelBuilder {
} }
excelWriteAddExecutor.add(data); excelWriteAddExecutor.add(data);
} catch (RuntimeException e) { } catch (RuntimeException e) {
finish(); finishOnException();
throw e; throw e;
} catch (Throwable e) { } catch (Throwable e) {
finish(); finishOnException();
throw new ExcelGenerateException(e); throw new ExcelGenerateException(e);
} }
} }
@ -80,18 +83,22 @@ public class ExcelBuilderImpl implements ExcelBuilder {
} }
excelWriteFillExecutor.fill(data, fillConfig); excelWriteFillExecutor.fill(data, fillConfig);
} catch (RuntimeException e) { } catch (RuntimeException e) {
finish(); finishOnException();
throw e; throw e;
} catch (Throwable e) { } catch (Throwable e) {
finish(); finishOnException();
throw new ExcelGenerateException(e); throw new ExcelGenerateException(e);
} }
} }
private void finishOnException() {
finish(true);
}
@Override @Override
public void finish() { public void finish(boolean onException) {
if (context != null) { if (context != null) {
context.finish(); context.finish(onException);
} }
} }
@ -108,10 +115,10 @@ public class ExcelBuilderImpl implements ExcelBuilder {
} }
excelWriteAddExecutor.add(data); excelWriteAddExecutor.add(data);
} catch (RuntimeException e) { } catch (RuntimeException e) {
finish(); finishOnException();
throw e; throw e;
} catch (Throwable e) { } catch (Throwable e) {
finish(); finishOnException();
throw new ExcelGenerateException(e); throw new ExcelGenerateException(e);
} }
} }

104
src/main/java/com/alibaba/excel/write/builder/AbstractExcelWriterParameterBuilder.java

@ -0,0 +1,104 @@
package com.alibaba.excel.write.builder;
import java.util.ArrayList;
import java.util.Collection;
import com.alibaba.excel.metadata.AbstractParameterBuilder;
import com.alibaba.excel.write.handler.WriteHandler;
import com.alibaba.excel.write.metadata.WriteBasicParameter;
/**
* Build ExcelBuilder
*
* @author Jiaju Zhuang
*/
public abstract class AbstractExcelWriterParameterBuilder<T extends AbstractExcelWriterParameterBuilder,
C extends WriteBasicParameter> extends AbstractParameterBuilder<T, C> {
/**
* Writes the head relative to the existing contents of the sheet. Indexes are zero-based.
*
* @param relativeHeadRowIndex
* @return
*/
public T relativeHeadRowIndex(Integer relativeHeadRowIndex) {
parameter().setRelativeHeadRowIndex(relativeHeadRowIndex);
return self();
}
/**
* Need Head
*/
public T needHead(Boolean needHead) {
parameter().setNeedHead(needHead);
return self();
}
/**
* Custom write handler
*
* @param writeHandler
* @return
*/
public T registerWriteHandler(WriteHandler writeHandler) {
if (parameter().getCustomWriteHandlerList() == null) {
parameter().setCustomWriteHandlerList(new ArrayList<WriteHandler>());
}
parameter().getCustomWriteHandlerList().add(writeHandler);
return self();
}
/**
* Use the default style.Default is true.
*
* @param useDefaultStyle
* @return
*/
public T useDefaultStyle(Boolean useDefaultStyle) {
parameter().setUseDefaultStyle(useDefaultStyle);
return self();
}
/**
* Whether to automatically merge headers.Default is true.
*
* @param automaticMergeHead
* @return
*/
public T automaticMergeHead(Boolean automaticMergeHead) {
parameter().setAutomaticMergeHead(automaticMergeHead);
return self();
}
/**
* Ignore the custom columns.
*/
public T excludeColumnIndexes(Collection<Integer> excludeColumnIndexes) {
parameter().setExcludeColumnIndexes(excludeColumnIndexes);
return self();
}
/**
* Ignore the custom columns.
*/
public T excludeColumnFiledNames(Collection<String> excludeColumnFiledNames) {
parameter().setExcludeColumnFiledNames(excludeColumnFiledNames);
return self();
}
/**
* Only output the custom columns.
*/
public T includeColumnIndexes(Collection<Integer> includeColumnIndexes) {
parameter().setIncludeColumnIndexes(includeColumnIndexes);
return self();
}
/**
* Only output the custom columns.
*/
public T includeColumnFiledNames(Collection<String> includeColumnFiledNames) {
parameter().setIncludeColumnFiledNames(includeColumnFiledNames);
return self();
}
}

103
src/main/java/com/alibaba/excel/write/builder/ExcelWriterBuilder.java

@ -3,11 +3,8 @@ package com.alibaba.excel.write.builder;
import java.io.File; import java.io.File;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import com.alibaba.excel.ExcelWriter; import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.support.ExcelTypeEnum; import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.handler.WriteHandler; import com.alibaba.excel.write.handler.WriteHandler;
import com.alibaba.excel.write.metadata.WriteWorkbook; import com.alibaba.excel.write.metadata.WriteWorkbook;
@ -17,7 +14,7 @@ import com.alibaba.excel.write.metadata.WriteWorkbook;
* *
* @author Jiaju Zhuang * @author Jiaju Zhuang
*/ */
public class ExcelWriterBuilder { public class ExcelWriterBuilder extends AbstractExcelWriterParameterBuilder<ExcelWriterBuilder, WriteWorkbook> {
/** /**
* Workbook * Workbook
*/ */
@ -27,47 +24,6 @@ public class ExcelWriterBuilder {
this.writeWorkbook = new WriteWorkbook(); this.writeWorkbook = new WriteWorkbook();
} }
/**
* Writes the head relative to the existing contents of the sheet. Indexes are zero-based.
*
* @param relativeHeadRowIndex
* @return
*/
public ExcelWriterBuilder relativeHeadRowIndex(Integer relativeHeadRowIndex) {
writeWorkbook.setRelativeHeadRowIndex(relativeHeadRowIndex);
return this;
}
/**
* You can only choose one of the {@link ExcelWriterBuilder#head(List)} and {@link ExcelWriterBuilder#head(Class)}
*
* @param head
* @return
*/
public ExcelWriterBuilder head(List<List<String>> head) {
writeWorkbook.setHead(head);
return this;
}
/**
* You can only choose one of the {@link ExcelWriterBuilder#head(List)} and {@link ExcelWriterBuilder#head(Class)}
*
* @param clazz
* @return
*/
public ExcelWriterBuilder head(Class clazz) {
writeWorkbook.setClazz(clazz);
return this;
}
/**
* Need Head
*/
public ExcelWriterBuilder needHead(Boolean needHead) {
writeWorkbook.setNeedHead(needHead);
return this;
}
/** /**
* Default true * Default true
* *
@ -79,17 +35,6 @@ public class ExcelWriterBuilder {
return this; return this;
} }
/**
* Use the default style.Default is true.
*
* @param useDefaultStyle
* @return
*/
public ExcelWriterBuilder useDefaultStyle(Boolean useDefaultStyle) {
writeWorkbook.setUseDefaultStyle(useDefaultStyle);
return this;
}
/** /**
* Whether the encryption. * Whether the encryption.
* <p> * <p>
@ -104,46 +49,36 @@ public class ExcelWriterBuilder {
} }
/** /**
* The default is all excel objects.if true , you can use {@link com.alibaba.excel.annotation.ExcelIgnore} ignore a * Write excel in memory. Default false,the cache file is created and finally written to excel.
* field. if false , you must use {@link com.alibaba.excel.annotation.ExcelProperty} to use a filed.
* <p> * <p>
* Default true * Comment and RichTextString are only supported in memory mode.
*
* @param convertAllFiled
* @return
* @deprecated Just to be compatible with historical data, The default is always going to be convert all filed.
*/ */
@Deprecated public ExcelWriterBuilder inMemory(Boolean inMemory) {
public ExcelWriterBuilder convertAllFiled(Boolean convertAllFiled) { writeWorkbook.setInMemory(inMemory);
writeWorkbook.setConvertAllFiled(convertAllFiled);
return this; return this;
} }
/** /**
* Custom type conversions override the default. * Excel is also written in the event of an exception being thrown.The default false.
*
* @param converter
* @return
*/ */
public ExcelWriterBuilder registerConverter(Converter converter) { public ExcelWriterBuilder writeExcelOnException(Boolean writeExcelOnException) {
if (writeWorkbook.getCustomConverterList() == null) { writeWorkbook.setWriteExcelOnException(writeExcelOnException);
writeWorkbook.setCustomConverterList(new ArrayList<Converter>());
}
writeWorkbook.getCustomConverterList().add(converter);
return this; return this;
} }
/** /**
* Custom write handler * The default is all excel objects.if true , you can use {@link com.alibaba.excel.annotation.ExcelIgnore} ignore a
* field. if false , you must use {@link com.alibaba.excel.annotation.ExcelProperty} to use a filed.
* <p>
* Default true
* *
* @param writeHandler * @param convertAllFiled
* @return * @return
* @deprecated Just to be compatible with historical data, The default is always going to be convert all filed.
*/ */
public ExcelWriterBuilder registerWriteHandler(WriteHandler writeHandler) { @Deprecated
if (writeWorkbook.getCustomWriteHandlerList() == null) { public ExcelWriterBuilder convertAllFiled(Boolean convertAllFiled) {
writeWorkbook.setCustomWriteHandlerList(new ArrayList<WriteHandler>()); writeWorkbook.setConvertAllFiled(convertAllFiled);
}
writeWorkbook.getCustomWriteHandlerList().add(writeHandler);
return this; return this;
} }
@ -219,4 +154,8 @@ public class ExcelWriterBuilder {
return excelWriterSheetBuilder; return excelWriterSheetBuilder;
} }
@Override
protected WriteWorkbook parameter() {
return writeWorkbook;
}
} }

92
src/main/java/com/alibaba/excel/write/builder/ExcelWriterSheetBuilder.java

@ -1,12 +1,9 @@
package com.alibaba.excel.write.builder; package com.alibaba.excel.write.builder;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import com.alibaba.excel.ExcelWriter; import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.exception.ExcelGenerateException; import com.alibaba.excel.exception.ExcelGenerateException;
import com.alibaba.excel.write.handler.WriteHandler;
import com.alibaba.excel.write.metadata.WriteSheet; import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillConfig; import com.alibaba.excel.write.metadata.fill.FillConfig;
@ -15,7 +12,7 @@ import com.alibaba.excel.write.metadata.fill.FillConfig;
* *
* @author Jiaju Zhuang * @author Jiaju Zhuang
*/ */
public class ExcelWriterSheetBuilder { public class ExcelWriterSheetBuilder extends AbstractExcelWriterParameterBuilder<ExcelWriterSheetBuilder, WriteSheet> {
private ExcelWriter excelWriter; private ExcelWriter excelWriter;
/** /**
* Sheet * Sheet
@ -31,88 +28,6 @@ public class ExcelWriterSheetBuilder {
this.excelWriter = excelWriter; this.excelWriter = excelWriter;
} }
/**
* Writes the head relative to the existing contents of the sheet. Indexes are zero-based.
*
* @param relativeHeadRowIndex
* @return
*/
public ExcelWriterSheetBuilder relativeHeadRowIndex(Integer relativeHeadRowIndex) {
writeSheet.setRelativeHeadRowIndex(relativeHeadRowIndex);
return this;
}
/**
* You can only choose one of the {@link ExcelWriterSheetBuilder#head(List)} and
* {@link ExcelWriterSheetBuilder#head(Class)}
*
* @param head
* @return
*/
public ExcelWriterSheetBuilder head(List<List<String>> head) {
writeSheet.setHead(head);
return this;
}
/**
* You can only choose one of the {@link ExcelWriterSheetBuilder#head(List)} and
* {@link ExcelWriterSheetBuilder#head(Class)}
*
* @param clazz
* @return
*/
public ExcelWriterSheetBuilder head(Class clazz) {
writeSheet.setClazz(clazz);
return this;
}
/**
* Need Head
*/
public ExcelWriterSheetBuilder needHead(Boolean needHead) {
writeSheet.setNeedHead(needHead);
return this;
}
/**
* Use the default style.Default is true.
*
* @param useDefaultStyle
* @return
*/
public ExcelWriterSheetBuilder useDefaultStyle(Boolean useDefaultStyle) {
writeSheet.setUseDefaultStyle(useDefaultStyle);
return this;
}
/**
* Custom type conversions override the default.
*
* @param converter
* @return
*/
public ExcelWriterSheetBuilder registerConverter(Converter converter) {
if (writeSheet.getCustomConverterList() == null) {
writeSheet.setCustomConverterList(new ArrayList<Converter>());
}
writeSheet.getCustomConverterList().add(converter);
return this;
}
/**
* Custom write handler
*
* @param writeHandler
* @return
*/
public ExcelWriterSheetBuilder registerWriteHandler(WriteHandler writeHandler) {
if (writeSheet.getCustomWriteHandlerList() == null) {
writeSheet.setCustomWriteHandlerList(new ArrayList<WriteHandler>());
}
writeSheet.getCustomWriteHandlerList().add(writeHandler);
return this;
}
/** /**
* Starting from 0 * Starting from 0
* *
@ -171,4 +86,9 @@ public class ExcelWriterSheetBuilder {
return excelWriterTableBuilder; return excelWriterTableBuilder;
} }
@Override
protected WriteSheet parameter() {
return writeSheet;
}
} }

91
src/main/java/com/alibaba/excel/write/builder/ExcelWriterTableBuilder.java

@ -1,12 +1,9 @@
package com.alibaba.excel.write.builder; package com.alibaba.excel.write.builder;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import com.alibaba.excel.ExcelWriter; import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.exception.ExcelGenerateException; import com.alibaba.excel.exception.ExcelGenerateException;
import com.alibaba.excel.write.handler.WriteHandler;
import com.alibaba.excel.write.metadata.WriteSheet; import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.WriteTable; import com.alibaba.excel.write.metadata.WriteTable;
@ -15,7 +12,7 @@ import com.alibaba.excel.write.metadata.WriteTable;
* *
* @author Jiaju Zhuang * @author Jiaju Zhuang
*/ */
public class ExcelWriterTableBuilder { public class ExcelWriterTableBuilder extends AbstractExcelWriterParameterBuilder<ExcelWriterTableBuilder, WriteTable> {
private ExcelWriter excelWriter; private ExcelWriter excelWriter;
@ -35,88 +32,6 @@ public class ExcelWriterTableBuilder {
this.writeTable = new WriteTable(); this.writeTable = new WriteTable();
} }
/**
* Writes the head relative to the existing contents of the sheet. Indexes are zero-based.
*
* @param relativeHeadRowIndex
* @return
*/
public ExcelWriterTableBuilder relativeHeadRowIndex(Integer relativeHeadRowIndex) {
writeTable.setRelativeHeadRowIndex(relativeHeadRowIndex);
return this;
}
/**
* You can only choose one of the {@link ExcelWriterTableBuilder#head(List)} and
* {@link ExcelWriterTableBuilder#head(Class)}
*
* @param head
* @return
*/
public ExcelWriterTableBuilder head(List<List<String>> head) {
writeTable.setHead(head);
return this;
}
/**
* You can only choose one of the {@link ExcelWriterTableBuilder#head(List)} and
* {@link ExcelWriterTableBuilder#head(Class)}
*
* @param clazz
* @return
*/
public ExcelWriterTableBuilder head(Class clazz) {
writeTable.setClazz(clazz);
return this;
}
/**
* Need Head
*/
public ExcelWriterTableBuilder needHead(Boolean needHead) {
writeTable.setNeedHead(needHead);
return this;
}
/**
* Use the default style.Default is true.
*
* @param useDefaultStyle
* @return
*/
public ExcelWriterTableBuilder useDefaultStyle(Boolean useDefaultStyle) {
writeTable.setUseDefaultStyle(useDefaultStyle);
return this;
}
/**
* Custom type conversions override the default.
*
* @param converter
* @return
*/
public ExcelWriterTableBuilder registerConverter(Converter converter) {
if (writeTable.getCustomConverterList() == null) {
writeTable.setCustomConverterList(new ArrayList<Converter>());
}
writeTable.getCustomConverterList().add(converter);
return this;
}
/**
* Custom write handler
*
* @param writeHandler
* @return
*/
public ExcelWriterTableBuilder registerWriteHandler(WriteHandler writeHandler) {
if (writeTable.getCustomWriteHandlerList() == null) {
writeTable.setCustomWriteHandlerList(new ArrayList<WriteHandler>());
}
writeTable.getCustomWriteHandlerList().add(writeHandler);
return this;
}
/** /**
* Starting from 0 * Starting from 0
* *
@ -140,4 +55,8 @@ public class ExcelWriterTableBuilder {
excelWriter.finish(); excelWriter.finish();
} }
@Override
protected WriteTable parameter() {
return writeTable;
}
} }

23
src/main/java/com/alibaba/excel/write/executor/AbstractExcelWriteExecutor.java

@ -31,7 +31,7 @@ public abstract class AbstractExcelWriteExecutor implements ExcelWriteExecutor {
protected CellData converterAndSet(WriteHolder currentWriteHolder, Class clazz, Cell cell, Object value, protected CellData converterAndSet(WriteHolder currentWriteHolder, Class clazz, Cell cell, Object value,
ExcelContentProperty excelContentProperty) { ExcelContentProperty excelContentProperty) {
if (value == null) { if (value == null) {
return new CellData(); return new CellData(CellDataTypeEnum.EMPTY);
} }
if (value instanceof String && currentWriteHolder.globalConfiguration().getAutoTrim()) { if (value instanceof String && currentWriteHolder.globalConfiguration().getAutoTrim()) {
value = ((String)value).trim(); value = ((String)value).trim();
@ -40,6 +40,9 @@ public abstract class AbstractExcelWriteExecutor implements ExcelWriteExecutor {
if (cellData.getFormula() != null && cellData.getFormula()) { if (cellData.getFormula() != null && cellData.getFormula()) {
cell.setCellFormula(cellData.getFormulaValue()); cell.setCellFormula(cellData.getFormulaValue());
} }
if (cellData.getType() == null) {
cellData.setType(CellDataTypeEnum.EMPTY);
}
switch (cellData.getType()) { switch (cellData.getType()) {
case STRING: case STRING:
cell.setCellValue(cellData.getStringValue()); cell.setCellValue(cellData.getStringValue());
@ -56,15 +59,16 @@ public abstract class AbstractExcelWriteExecutor implements ExcelWriteExecutor {
case EMPTY: case EMPTY:
return cellData; return cellData;
default: default:
throw new ExcelDataConvertException("Not supported data:" + value + " return type:" + cell.getCellType() throw new ExcelDataConvertException(cell.getRow().getRowNum(), cell.getColumnIndex(), cellData,
+ "at row:" + cell.getRow().getRowNum()); excelContentProperty, "Not supported data:" + value + " return type:" + cell.getCellType()
+ "at row:" + cell.getRow().getRowNum());
} }
} }
protected CellData convert(WriteHolder currentWriteHolder, Class clazz, Cell cell, Object value, protected CellData convert(WriteHolder currentWriteHolder, Class clazz, Cell cell, Object value,
ExcelContentProperty excelContentProperty) { ExcelContentProperty excelContentProperty) {
if (value == null) { if (value == null) {
return new CellData(); return new CellData(CellDataTypeEnum.EMPTY);
} }
// This means that the user has defined the data. // This means that the user has defined the data.
if (value instanceof CellData) { if (value instanceof CellData) {
@ -99,7 +103,8 @@ public abstract class AbstractExcelWriteExecutor implements ExcelWriteExecutor {
converter = currentWriteHolder.converterMap().get(ConverterKeyBuild.buildKey(clazz)); converter = currentWriteHolder.converterMap().get(ConverterKeyBuild.buildKey(clazz));
} }
if (converter == null) { if (converter == null) {
throw new ExcelDataConvertException( throw new ExcelDataConvertException(cell.getRow().getRowNum(), cell.getColumnIndex(),
new CellData(CellDataTypeEnum.EMPTY), excelContentProperty,
"Can not find 'Converter' support class " + clazz.getSimpleName() + "."); "Can not find 'Converter' support class " + clazz.getSimpleName() + ".");
} }
CellData cellData; CellData cellData;
@ -107,11 +112,13 @@ public abstract class AbstractExcelWriteExecutor implements ExcelWriteExecutor {
cellData = cellData =
converter.convertToExcelData(value, excelContentProperty, currentWriteHolder.globalConfiguration()); converter.convertToExcelData(value, excelContentProperty, currentWriteHolder.globalConfiguration());
} catch (Exception e) { } catch (Exception e) {
throw new ExcelDataConvertException("Convert data:" + value + " error,at row:" + cell.getRow().getRowNum(), throw new ExcelDataConvertException(cell.getRow().getRowNum(), cell.getColumnIndex(),
e); new CellData(CellDataTypeEnum.EMPTY), excelContentProperty,
"Convert data:" + value + " error,at row:" + cell.getRow().getRowNum(), e);
} }
if (cellData == null || cellData.getType() == null) { if (cellData == null || cellData.getType() == null) {
throw new ExcelDataConvertException( throw new ExcelDataConvertException(cell.getRow().getRowNum(), cell.getColumnIndex(),
new CellData(CellDataTypeEnum.EMPTY), excelContentProperty,
"Convert data:" + value + " return null,at row:" + cell.getRow().getRowNum()); "Convert data:" + value + " return null,at row:" + cell.getRow().getRowNum());
} }
return cellData; return cellData;

26
src/main/java/com/alibaba/excel/write/executor/ExcelWriteAddExecutor.java

@ -2,7 +2,6 @@ package com.alibaba.excel.write.executor;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -13,10 +12,10 @@ import org.apache.poi.ss.usermodel.Row;
import com.alibaba.excel.context.WriteContext; import com.alibaba.excel.context.WriteContext;
import com.alibaba.excel.enums.HeadKindEnum; import com.alibaba.excel.enums.HeadKindEnum;
import com.alibaba.excel.metadata.BaseRowModel;
import com.alibaba.excel.metadata.CellData; import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.Head; import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.property.ExcelContentProperty; import com.alibaba.excel.metadata.property.ExcelContentProperty;
import com.alibaba.excel.util.ClassUtils;
import com.alibaba.excel.util.CollectionUtils; import com.alibaba.excel.util.CollectionUtils;
import com.alibaba.excel.util.WorkBookUtil; import com.alibaba.excel.util.WorkBookUtil;
import com.alibaba.excel.util.WriteHandlerUtils; import com.alibaba.excel.util.WriteHandlerUtils;
@ -98,6 +97,9 @@ public class ExcelWriteAddExecutor extends AbstractExcelWriteExecutor {
private void doAddBasicTypeToExcel(List<Object> oneRowData, Head head, Row row, int relativeRowIndex, int dataIndex, private void doAddBasicTypeToExcel(List<Object> oneRowData, Head head, Row row, int relativeRowIndex, int dataIndex,
int cellIndex) { int cellIndex) {
if (writeContext.currentWriteHolder().ignore(null, cellIndex)) {
return;
}
WriteHandlerUtils.beforeCellCreate(writeContext, row, head, cellIndex, relativeRowIndex, Boolean.FALSE); WriteHandlerUtils.beforeCellCreate(writeContext, row, head, cellIndex, relativeRowIndex, Boolean.FALSE);
Cell cell = WorkBookUtil.createCell(row, cellIndex); Cell cell = WorkBookUtil.createCell(row, cellIndex);
WriteHandlerUtils.afterCellCreate(writeContext, cell, head, relativeRowIndex, Boolean.FALSE); WriteHandlerUtils.afterCellCreate(writeContext, cell, head, relativeRowIndex, Boolean.FALSE);
@ -121,6 +123,9 @@ public class ExcelWriteAddExecutor extends AbstractExcelWriteExecutor {
cellIndex = entry.getKey(); cellIndex = entry.getKey();
ExcelContentProperty excelContentProperty = entry.getValue(); ExcelContentProperty excelContentProperty = entry.getValue();
String name = excelContentProperty.getField().getName(); String name = excelContentProperty.getField().getName();
if (writeContext.currentWriteHolder().ignore(name, cellIndex)) {
continue;
}
if (!beanMap.containsKey(name)) { if (!beanMap.containsKey(name)) {
continue; continue;
} }
@ -147,18 +152,16 @@ public class ExcelWriteAddExecutor extends AbstractExcelWriteExecutor {
for (Field field : fieldList) { for (Field field : fieldList) {
String filedName = field.getName(); String filedName = field.getName();
boolean uselessData = !beanMap.containsKey(filedName) || beanMapHandledSet.contains(filedName) boolean uselessData = !beanMap.containsKey(filedName) || beanMapHandledSet.contains(filedName)
|| ignoreMap.containsKey(filedName); || ignoreMap.containsKey(filedName) || writeContext.currentWriteHolder().ignore(filedName, cellIndex);
if (uselessData) { if (uselessData) {
continue; continue;
} }
Object value = beanMap.get(filedName); Object value = beanMap.get(filedName);
if (value == null) {
continue;
}
WriteHandlerUtils.beforeCellCreate(writeContext, row, null, cellIndex, relativeRowIndex, Boolean.FALSE); WriteHandlerUtils.beforeCellCreate(writeContext, row, null, cellIndex, relativeRowIndex, Boolean.FALSE);
Cell cell = WorkBookUtil.createCell(row, cellIndex++); Cell cell = WorkBookUtil.createCell(row, cellIndex++);
WriteHandlerUtils.afterCellCreate(writeContext, cell, null, relativeRowIndex, Boolean.FALSE); WriteHandlerUtils.afterCellCreate(writeContext, cell, null, relativeRowIndex, Boolean.FALSE);
CellData cellData = converterAndSet(currentWriteHolder, value.getClass(), cell, value, null); CellData cellData =
converterAndSet(currentWriteHolder, value == null ? null : value.getClass(), cell, value, null);
WriteHandlerUtils.afterCellDispose(writeContext, cellData, cell, null, relativeRowIndex, Boolean.FALSE); WriteHandlerUtils.afterCellDispose(writeContext, cellData, cell, null, relativeRowIndex, Boolean.FALSE);
} }
} }
@ -167,13 +170,8 @@ public class ExcelWriteAddExecutor extends AbstractExcelWriteExecutor {
if (!fieldList.isEmpty()) { if (!fieldList.isEmpty()) {
return; return;
} }
Class tempClass = clazz; ClassUtils.declaredFields(clazz, fieldList,
while (tempClass != null) { writeContext.writeWorkbookHolder().getWriteWorkbook().getConvertAllFiled());
if (tempClass != BaseRowModel.class) {
Collections.addAll(fieldList, tempClass.getDeclaredFields());
}
tempClass = tempClass.getSuperclass();
}
} }
} }

164
src/main/java/com/alibaba/excel/write/executor/ExcelWriteFillExecutor.java

@ -3,21 +3,24 @@ package com.alibaba.excel.write.executor;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Sheet;
import com.alibaba.excel.context.WriteContext; import com.alibaba.excel.context.WriteContext;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.enums.WriteDirectionEnum; import com.alibaba.excel.enums.WriteDirectionEnum;
import com.alibaba.excel.enums.WriteTemplateAnalysisCellTypeEnum; import com.alibaba.excel.enums.WriteTemplateAnalysisCellTypeEnum;
import com.alibaba.excel.exception.ExcelGenerateException; import com.alibaba.excel.exception.ExcelGenerateException;
import com.alibaba.excel.metadata.CellData; import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.property.ExcelContentProperty; import com.alibaba.excel.metadata.property.ExcelContentProperty;
import com.alibaba.excel.util.CollectionUtils; import com.alibaba.excel.util.CollectionUtils;
import com.alibaba.excel.util.StringUtils; import com.alibaba.excel.util.StringUtils;
@ -55,12 +58,18 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor {
*/ */
private Map<Integer, Map<AnalysisCell, CellStyle>> collectionFieldStyleCache = private Map<Integer, Map<AnalysisCell, CellStyle>> collectionFieldStyleCache =
new HashMap<Integer, Map<AnalysisCell, CellStyle>>(8); new HashMap<Integer, Map<AnalysisCell, CellStyle>>(8);
/**
* Row height cache for collection
*/
private Map<Integer, Short> collectionRowHeightCache = new HashMap<Integer, Short>(8);
/** /**
* Last index cache for collection fields * Last index cache for collection fields
*/ */
private Map<Integer, Map<AnalysisCell, Integer>> collectionLastIndexCache = private Map<Integer, Map<AnalysisCell, Integer>> collectionLastIndexCache =
new HashMap<Integer, Map<AnalysisCell, Integer>>(8); new HashMap<Integer, Map<AnalysisCell, Integer>>(8);
private Map<Integer, Integer> relativeRowIndexMap = new HashMap<Integer, Integer>(8);
public ExcelWriteFillExecutor(WriteContext writeContext) { public ExcelWriteFillExecutor(WriteContext writeContext) {
super(writeContext); super(writeContext);
} }
@ -81,10 +90,10 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor {
shiftRows(collectionData.size(), analysisCellList); shiftRows(collectionData.size(), analysisCellList);
} }
while (iterator.hasNext()) { while (iterator.hasNext()) {
doFill(analysisCellList, iterator.next(), fillConfig); doFill(analysisCellList, iterator.next(), fillConfig, getRelativeRowIndex());
} }
} else { } else {
doFill(readTemplateData(templateAnalysisCache), data, fillConfig); doFill(readTemplateData(templateAnalysisCache), data, fillConfig, null);
} }
} }
@ -119,7 +128,10 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor {
if (collectionLastIndexMap == null) { if (collectionLastIndexMap == null) {
number--; number--;
} }
sheet.shiftRows(maxRowIndex + 1, lastRowIndex, number); if (number <= 0) {
return;
}
sheet.shiftRows(maxRowIndex + 1, lastRowIndex, number, true, false);
for (AnalysisCell analysisCell : templateAnalysisCache.get(writeContext.writeSheetHolder().getSheetNo())) { for (AnalysisCell analysisCell : templateAnalysisCache.get(writeContext.writeSheetHolder().getSheetNo())) {
if (analysisCell.getRowIndex() > maxRowIndex) { if (analysisCell.getRowIndex() > maxRowIndex) {
analysisCell.setRowIndex(analysisCell.getRowIndex() + number); analysisCell.setRowIndex(analysisCell.getRowIndex() + number);
@ -127,7 +139,8 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor {
} }
} }
private void doFill(List<AnalysisCell> analysisCellList, Object oneRowData, FillConfig fillConfig) { private void doFill(List<AnalysisCell> analysisCellList, Object oneRowData, FillConfig fillConfig,
Integer relativeRowIndex) {
Map dataMap; Map dataMap;
if (oneRowData instanceof Map) { if (oneRowData instanceof Map) {
dataMap = (Map)oneRowData; dataMap = (Map)oneRowData;
@ -141,19 +154,25 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor {
Cell cell = getOneCell(analysisCell, fillConfig); Cell cell = getOneCell(analysisCell, fillConfig);
if (analysisCell.getOnlyOneVariable()) { if (analysisCell.getOnlyOneVariable()) {
String variable = analysisCell.getVariableList().get(0); String variable = analysisCell.getVariableList().get(0);
if (writeContext.currentWriteHolder().ignore(variable, analysisCell.getColumnIndex())) {
continue;
}
if (!dataMap.containsKey(variable)) { if (!dataMap.containsKey(variable)) {
continue; continue;
} }
Object value = dataMap.get(variable); Object value = dataMap.get(variable);
CellData cellData = converterAndSet(writeSheetHolder, value == null ? null : value.getClass(), cell, CellData cellData = converterAndSet(writeSheetHolder, value == null ? null : value.getClass(), cell,
value, fieldNameContentPropertyMap.get(variable)); value, fieldNameContentPropertyMap.get(variable));
WriteHandlerUtils.afterCellDispose(writeContext, cellData, cell, null, null, Boolean.FALSE); WriteHandlerUtils.afterCellDispose(writeContext, cellData, cell, null, relativeRowIndex, Boolean.FALSE);
} else { } else {
StringBuilder cellValueBuild = new StringBuilder(); StringBuilder cellValueBuild = new StringBuilder();
int index = 0; int index = 0;
List<CellData> cellDataList = new ArrayList<CellData>(); List<CellData> cellDataList = new ArrayList<CellData>();
for (String variable : analysisCell.getVariableList()) { for (String variable : analysisCell.getVariableList()) {
cellValueBuild.append(analysisCell.getPrepareDataList().get(index++)); cellValueBuild.append(analysisCell.getPrepareDataList().get(index++));
if (writeContext.currentWriteHolder().ignore(variable, analysisCell.getColumnIndex())) {
continue;
}
if (!dataMap.containsKey(variable)) { if (!dataMap.containsKey(variable)) {
continue; continue;
} }
@ -161,27 +180,43 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor {
CellData cellData = convert(writeSheetHolder, value == null ? null : value.getClass(), cell, value, CellData cellData = convert(writeSheetHolder, value == null ? null : value.getClass(), cell, value,
fieldNameContentPropertyMap.get(variable)); fieldNameContentPropertyMap.get(variable));
cellDataList.add(cellData); cellDataList.add(cellData);
switch (cellData.getType()) { CellDataTypeEnum type = cellData.getType();
case STRING: if (type != null) {
cellValueBuild.append(cellData.getStringValue()); switch (type) {
break; case STRING:
case BOOLEAN: cellValueBuild.append(cellData.getStringValue());
cellValueBuild.append(cellData.getBooleanValue()); break;
break; case BOOLEAN:
case NUMBER: cellValueBuild.append(cellData.getBooleanValue());
cellValueBuild.append(cellData.getNumberValue()); break;
break; case NUMBER:
default: cellValueBuild.append(cellData.getNumberValue());
break; break;
default:
break;
}
} }
} }
cellValueBuild.append(analysisCell.getPrepareDataList().get(index)); cellValueBuild.append(analysisCell.getPrepareDataList().get(index));
cell.setCellValue(cellValueBuild.toString()); cell.setCellValue(cellValueBuild.toString());
WriteHandlerUtils.afterCellDispose(writeContext, cellDataList, cell, null, null, Boolean.FALSE); WriteHandlerUtils.afterCellDispose(writeContext, cellDataList, cell, null, relativeRowIndex,
Boolean.FALSE);
} }
} }
} }
private Integer getRelativeRowIndex() {
Integer sheetNo = writeContext.writeSheetHolder().getSheetNo();
Integer relativeRowIndex = relativeRowIndexMap.get(sheetNo);
if (relativeRowIndex == null) {
relativeRowIndex = 0;
} else {
relativeRowIndex++;
}
relativeRowIndexMap.put(sheetNo, relativeRowIndex);
return relativeRowIndex;
}
private Cell getOneCell(AnalysisCell analysisCell, FillConfig fillConfig) { private Cell getOneCell(AnalysisCell analysisCell, FillConfig fillConfig) {
Sheet cachedSheet = writeContext.writeSheetHolder().getCachedSheet(); Sheet cachedSheet = writeContext.writeSheetHolder().getCachedSheet();
if (WriteTemplateAnalysisCellTypeEnum.COMMON.equals(analysisCell.getCellType())) { if (WriteTemplateAnalysisCellTypeEnum.COMMON.equals(analysisCell.getCellType())) {
@ -234,7 +269,10 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor {
} else { } else {
row = sheet.createRow(lastRowIndex); row = sheet.createRow(lastRowIndex);
} }
checkRowHeight(analysisCell, fillConfig, isOriginalCell, row, sheetNo);
WriteHandlerUtils.afterRowCreate(writeContext, row, null, Boolean.FALSE); WriteHandlerUtils.afterRowCreate(writeContext, row, null, Boolean.FALSE);
} else {
checkRowHeight(analysisCell, fillConfig, isOriginalCell, row, sheetNo);
} }
} }
Cell cell = row.getCell(lastColumnIndex); Cell cell = row.getCell(lastColumnIndex);
@ -260,6 +298,21 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor {
return cell; return cell;
} }
private void checkRowHeight(AnalysisCell analysisCell, FillConfig fillConfig, boolean isOriginalCell, Row row,
Integer sheetNo) {
if (!analysisCell.getFirstColumn() || !WriteDirectionEnum.VERTICAL.equals(fillConfig.getDirection())) {
return;
}
if (isOriginalCell) {
collectionRowHeightCache.put(sheetNo, row.getHeight());
return;
}
Short rowHeight = collectionRowHeightCache.get(sheetNo);
if (rowHeight != null) {
row.setHeight(rowHeight);
}
}
private List<AnalysisCell> readTemplateData(Map<Integer, List<AnalysisCell>> analysisCache) { private List<AnalysisCell> readTemplateData(Map<Integer, List<AnalysisCell>> analysisCache) {
Integer sheetNo = writeContext.writeSheetHolder().getSheetNo(); Integer sheetNo = writeContext.writeSheetHolder().getSheetNo();
List<AnalysisCell> analysisCellList = analysisCache.get(sheetNo); List<AnalysisCell> analysisCellList = analysisCache.get(sheetNo);
@ -269,6 +322,7 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor {
Sheet sheet = writeContext.writeSheetHolder().getCachedSheet(); Sheet sheet = writeContext.writeSheetHolder().getCachedSheet();
analysisCellList = new ArrayList<AnalysisCell>(); analysisCellList = new ArrayList<AnalysisCell>();
List<AnalysisCell> collectionAnalysisCellList = new ArrayList<AnalysisCell>(); List<AnalysisCell> collectionAnalysisCellList = new ArrayList<AnalysisCell>();
Set<Integer> firstColumnCache = new HashSet<Integer>();
for (int i = 0; i <= sheet.getLastRowNum(); i++) { for (int i = 0; i <= sheet.getLastRowNum(); i++) {
Row row = sheet.getRow(i); Row row = sheet.getRow(i);
if (row == null) { if (row == null) {
@ -279,7 +333,12 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor {
if (cell == null) { if (cell == null) {
continue; continue;
} }
prepareData(cell.getStringCellValue(), analysisCellList, collectionAnalysisCellList, i, j); String preparedData =
prepareData(cell, analysisCellList, collectionAnalysisCellList, i, j, firstColumnCache);
// Prevent empty data from not being replaced
if (preparedData != null) {
cell.setCellValue(preparedData);
}
} }
} }
templateAnalysisCache.put(sheetNo, analysisCellList); templateAnalysisCache.put(sheetNo, analysisCellList);
@ -287,12 +346,29 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor {
return analysisCache.get(sheetNo); return analysisCache.get(sheetNo);
} }
private void prepareData(String value, List<AnalysisCell> analysisCellList, /**
List<AnalysisCell> collectionAnalysisCellList, int rowIndex, int columnIndex) { * To prepare data
*
* @param cell
* @param analysisCellList
* @param collectionAnalysisCellList
* @param rowIndex
* @param columnIndex
* @param firstColumnCache
* @return Returns the data that the cell needs to replace
*/
private String prepareData(Cell cell, List<AnalysisCell> analysisCellList,
List<AnalysisCell> collectionAnalysisCellList, int rowIndex, int columnIndex, Set<Integer> firstColumnCache) {
if (!CellType.STRING.equals(cell.getCellTypeEnum())) {
return null;
}
String value = cell.getStringCellValue();
if (StringUtils.isEmpty(value)) { if (StringUtils.isEmpty(value)) {
return; return null;
} }
StringBuilder preparedData = new StringBuilder();
AnalysisCell analysisCell = null; AnalysisCell analysisCell = null;
int startIndex = 0; int startIndex = 0;
int length = value.length(); int length = value.length();
int lastPrepareDataIndex = 0; int lastPrepareDataIndex = 0;
@ -322,15 +398,7 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor {
} }
} }
if (analysisCell == null) { if (analysisCell == null) {
analysisCell = new AnalysisCell(); analysisCell = initAnalysisCell(rowIndex, columnIndex);
analysisCell.setRowIndex(rowIndex);
analysisCell.setColumnIndex(columnIndex);
analysisCell.setOnlyOneVariable(Boolean.TRUE);
List<String> variableList = new ArrayList<String>();
analysisCell.setVariableList(variableList);
List<String> prepareDataList = new ArrayList<String>();
analysisCell.setPrepareDataList(prepareDataList);
analysisCell.setCellType(WriteTemplateAnalysisCellTypeEnum.COMMON);
} }
String variable = value.substring(prefixIndex + 1, suffixIndex); String variable = value.substring(prefixIndex + 1, suffixIndex);
if (StringUtils.isEmpty(variable)) { if (StringUtils.isEmpty(variable)) {
@ -347,12 +415,20 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor {
if (lastPrepareDataIndex == prefixIndex) { if (lastPrepareDataIndex == prefixIndex) {
analysisCell.getPrepareDataList().add(StringUtils.EMPTY); analysisCell.getPrepareDataList().add(StringUtils.EMPTY);
} else { } else {
analysisCell.getPrepareDataList() String data = convertPrepareData(value.substring(lastPrepareDataIndex, prefixIndex));
.add(convertPrepareData(value.substring(lastPrepareDataIndex, prefixIndex))); preparedData.append(data);
analysisCell.getPrepareDataList().add(data);
analysisCell.setOnlyOneVariable(Boolean.FALSE); analysisCell.setOnlyOneVariable(Boolean.FALSE);
} }
lastPrepareDataIndex = suffixIndex + 1; lastPrepareDataIndex = suffixIndex + 1;
} }
return dealAnalysisCell(analysisCell, value, rowIndex, lastPrepareDataIndex, length, analysisCellList,
collectionAnalysisCellList, firstColumnCache, preparedData);
}
private String dealAnalysisCell(AnalysisCell analysisCell, String value, int rowIndex, int lastPrepareDataIndex,
int length, List<AnalysisCell> analysisCellList, List<AnalysisCell> collectionAnalysisCellList,
Set<Integer> firstColumnCache, StringBuilder preparedData) {
if (analysisCell != null) { if (analysisCell != null) {
if (lastPrepareDataIndex == length) { if (lastPrepareDataIndex == length) {
analysisCell.getPrepareDataList().add(StringUtils.EMPTY); analysisCell.getPrepareDataList().add(StringUtils.EMPTY);
@ -363,9 +439,29 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor {
if (WriteTemplateAnalysisCellTypeEnum.COMMON.equals(analysisCell.getCellType())) { if (WriteTemplateAnalysisCellTypeEnum.COMMON.equals(analysisCell.getCellType())) {
analysisCellList.add(analysisCell); analysisCellList.add(analysisCell);
} else { } else {
if (!firstColumnCache.contains(rowIndex)) {
analysisCell.setFirstColumn(Boolean.TRUE);
firstColumnCache.add(rowIndex);
}
collectionAnalysisCellList.add(analysisCell); collectionAnalysisCellList.add(analysisCell);
} }
return preparedData.toString();
} }
return null;
}
private AnalysisCell initAnalysisCell(Integer rowIndex, Integer columnIndex) {
AnalysisCell analysisCell = new AnalysisCell();
analysisCell.setRowIndex(rowIndex);
analysisCell.setColumnIndex(columnIndex);
analysisCell.setOnlyOneVariable(Boolean.TRUE);
List<String> variableList = new ArrayList<String>();
analysisCell.setVariableList(variableList);
List<String> prepareDataList = new ArrayList<String>();
analysisCell.setPrepareDataList(prepareDataList);
analysisCell.setCellType(WriteTemplateAnalysisCellTypeEnum.COMMON);
analysisCell.setFirstColumn(Boolean.FALSE);
return analysisCell;
} }
private String convertPrepareData(String prepareData) { private String convertPrepareData(String prepareData) {

2
src/main/java/com/alibaba/excel/write/merge/AbstractMergeStrategy.java

@ -45,5 +45,5 @@ public abstract class AbstractMergeStrategy implements CellWriteHandler {
* @param head * @param head
* @param relativeRowIndex * @param relativeRowIndex
*/ */
protected abstract void merge(Sheet sheet, Cell cell, Head head, int relativeRowIndex); protected abstract void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex);
} }

29
src/main/java/com/alibaba/excel/write/merge/LoopMergeStrategy.java

@ -13,25 +13,46 @@ import com.alibaba.excel.metadata.Head;
*/ */
public class LoopMergeStrategy extends AbstractMergeStrategy { public class LoopMergeStrategy extends AbstractMergeStrategy {
private int eachRow; private int eachRow;
private int columnCount;
private int columnIndex; private int columnIndex;
public LoopMergeStrategy(int eachRow, int columnIndex) { public LoopMergeStrategy(int eachRow, int columnIndex) {
this(eachRow, 1, columnIndex);
}
public LoopMergeStrategy(int eachRow, int columnCount, int columnIndex) {
if (eachRow < 1) { if (eachRow < 1) {
throw new IllegalArgumentException("EachRows must be greater than 1"); throw new IllegalArgumentException("EachRows must be greater than 1");
} }
if (columnCount < 1) {
throw new IllegalArgumentException("ColumnCount must be greater than 1");
}
if (columnCount == 1 && eachRow == 1) {
throw new IllegalArgumentException("ColumnCount or eachRows must be greater than 1");
}
if (columnIndex < 0) { if (columnIndex < 0) {
throw new IllegalArgumentException("ColumnIndex must be greater than 0"); throw new IllegalArgumentException("ColumnIndex must be greater than 0");
} }
this.eachRow = eachRow; this.eachRow = eachRow;
this.columnCount = columnCount;
this.columnIndex = columnIndex; this.columnIndex = columnIndex;
} }
@Override @Override
protected void merge(Sheet sheet, Cell cell, Head head, int relativeRowIndex) { protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) {
if (head.getColumnIndex() == columnIndex && relativeRowIndex % eachRow == 0) { if (relativeRowIndex == null) {
return;
}
Integer currentColumnIndex;
if (head != null) {
currentColumnIndex = head.getColumnIndex();
} else {
currentColumnIndex = cell.getColumnIndex();
}
if (currentColumnIndex == columnIndex && relativeRowIndex % eachRow == 0) {
CellRangeAddress cellRangeAddress = new CellRangeAddress(cell.getRowIndex(), CellRangeAddress cellRangeAddress = new CellRangeAddress(cell.getRowIndex(),
cell.getRowIndex() + eachRow - 1, cell.getColumnIndex(), cell.getColumnIndex()); cell.getRowIndex() + eachRow - 1, cell.getColumnIndex(), cell.getColumnIndex() + columnCount - 1);
sheet.addMergedRegion(cellRangeAddress); sheet.addMergedRegionUnsafe(cellRangeAddress);
} }
} }
} }

4
src/main/java/com/alibaba/excel/write/merge/OnceAbsoluteMergeStrategy.java

@ -29,11 +29,11 @@ public class OnceAbsoluteMergeStrategy extends AbstractMergeStrategy {
} }
@Override @Override
protected void merge(Sheet sheet, Cell cell, Head head, int relativeRowIndex) { protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) {
if (cell.getRowIndex() == firstRowIndex && cell.getColumnIndex() == firstColumnIndex) { if (cell.getRowIndex() == firstRowIndex && cell.getColumnIndex() == firstColumnIndex) {
CellRangeAddress cellRangeAddress = CellRangeAddress cellRangeAddress =
new CellRangeAddress(firstRowIndex, lastRowIndex, firstColumnIndex, lastColumnIndex); new CellRangeAddress(firstRowIndex, lastRowIndex, firstColumnIndex, lastColumnIndex);
sheet.addMergedRegion(cellRangeAddress); sheet.addMergedRegionUnsafe(cellRangeAddress);
} }
} }
} }

62
src/main/java/com/alibaba/excel/write/metadata/WriteBasicParameter.java

@ -1,6 +1,7 @@
package com.alibaba.excel.write.metadata; package com.alibaba.excel.write.metadata;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.List; import java.util.List;
import com.alibaba.excel.metadata.BasicParameter; import com.alibaba.excel.metadata.BasicParameter;
@ -28,6 +29,26 @@ public class WriteBasicParameter extends BasicParameter {
* Use the default style.Default is true. * Use the default style.Default is true.
*/ */
private Boolean useDefaultStyle; private Boolean useDefaultStyle;
/**
* Whether to automatically merge headers.Default is true.
*/
private Boolean automaticMergeHead;
/**
* Ignore the custom columns.
*/
private Collection<Integer> excludeColumnIndexes;
/**
* Ignore the custom columns.
*/
private Collection<String> excludeColumnFiledNames;
/**
* Only output the custom columns.
*/
private Collection<Integer> includeColumnIndexes;
/**
* Only output the custom columns.
*/
private Collection<String> includeColumnFiledNames;
public Integer getRelativeHeadRowIndex() { public Integer getRelativeHeadRowIndex() {
return relativeHeadRowIndex; return relativeHeadRowIndex;
@ -60,4 +81,45 @@ public class WriteBasicParameter extends BasicParameter {
public void setUseDefaultStyle(Boolean useDefaultStyle) { public void setUseDefaultStyle(Boolean useDefaultStyle) {
this.useDefaultStyle = useDefaultStyle; this.useDefaultStyle = useDefaultStyle;
} }
public Boolean getAutomaticMergeHead() {
return automaticMergeHead;
}
public void setAutomaticMergeHead(Boolean automaticMergeHead) {
this.automaticMergeHead = automaticMergeHead;
}
public Collection<Integer> getExcludeColumnIndexes() {
return excludeColumnIndexes;
}
public void setExcludeColumnIndexes(Collection<Integer> excludeColumnIndexes) {
this.excludeColumnIndexes = excludeColumnIndexes;
}
public Collection<String> getExcludeColumnFiledNames() {
return excludeColumnFiledNames;
}
public void setExcludeColumnFiledNames(Collection<String> excludeColumnFiledNames) {
this.excludeColumnFiledNames = excludeColumnFiledNames;
}
public Collection<Integer> getIncludeColumnIndexes() {
return includeColumnIndexes;
}
public void setIncludeColumnIndexes(Collection<Integer> includeColumnIndexes) {
this.includeColumnIndexes = includeColumnIndexes;
}
public Collection<String> getIncludeColumnFiledNames() {
return includeColumnFiledNames;
}
public void setIncludeColumnFiledNames(Collection<String> includeColumnFiledNames) {
this.includeColumnFiledNames = includeColumnFiledNames;
}
} }

28
src/main/java/com/alibaba/excel/write/metadata/WriteWorkbook.java

@ -43,7 +43,7 @@ public class WriteWorkbook extends WriteBasicParameter {
*/ */
private File templateFile; private File templateFile;
/** /**
* Default trueuseDefaultStyle * Default true.
*/ */
private Boolean autoCloseStream; private Boolean autoCloseStream;
/** /**
@ -57,6 +57,16 @@ public class WriteWorkbook extends WriteBasicParameter {
* *
*/ */
private String password; private String password;
/**
* Write excel in memory. Default false,the cache file is created and finally written to excel.
* <p>
* Comment and RichTextString are only supported in memory mode.
*/
private Boolean inMemory;
/**
* Excel is also written in the event of an exception being thrown.The default false.
*/
private Boolean writeExcelOnException;
/** /**
* The default is all excel objects.Default is true. * The default is all excel objects.Default is true.
* <p> * <p>
@ -155,4 +165,20 @@ public class WriteWorkbook extends WriteBasicParameter {
public void setPassword(String password) { public void setPassword(String password) {
this.password = password; this.password = password;
} }
public Boolean getInMemory() {
return inMemory;
}
public void setInMemory(Boolean inMemory) {
this.inMemory = inMemory;
}
public Boolean getWriteExcelOnException() {
return writeExcelOnException;
}
public void setWriteExcelOnException(Boolean writeExcelOnException) {
this.writeExcelOnException = writeExcelOnException;
}
} }

9
src/main/java/com/alibaba/excel/write/metadata/fill/AnalysisCell.java

@ -16,6 +16,7 @@ public class AnalysisCell {
private List<String> prepareDataList; private List<String> prepareDataList;
private Boolean onlyOneVariable; private Boolean onlyOneVariable;
private WriteTemplateAnalysisCellTypeEnum cellType; private WriteTemplateAnalysisCellTypeEnum cellType;
private Boolean firstColumn;
public int getColumnIndex() { public int getColumnIndex() {
return columnIndex; return columnIndex;
@ -65,6 +66,14 @@ public class AnalysisCell {
this.cellType = cellType; this.cellType = cellType;
} }
public Boolean getFirstColumn() {
return firstColumn;
}
public void setFirstColumn(Boolean firstColumn) {
this.firstColumn = firstColumn;
}
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) { if (this == o) {

125
src/main/java/com/alibaba/excel/write/metadata/holder/AbstractWriteHolder.java

@ -1,6 +1,7 @@
package com.alibaba.excel.write.metadata.holder; package com.alibaba.excel.write.metadata.holder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@ -65,6 +66,26 @@ public abstract class AbstractWriteHolder extends AbstractHolder implements Writ
* Use the default style.Default is true. * Use the default style.Default is true.
*/ */
private Boolean useDefaultStyle; private Boolean useDefaultStyle;
/**
* Whether to automatically merge headers.Default is true.
*/
private Boolean automaticMergeHead;
/**
* Ignore the custom columns.
*/
private Collection<Integer> excludeColumnIndexes;
/**
* Ignore the custom columns.
*/
private Collection<String> excludeColumnFiledNames;
/**
* Only output the custom columns.
*/
private Collection<Integer> includeColumnIndexes;
/**
* Only output the custom columns.
*/
private Collection<String> includeColumnFiledNames;
public AbstractWriteHolder(WriteBasicParameter writeBasicParameter, AbstractWriteHolder parentAbstractWriteHolder, public AbstractWriteHolder(WriteBasicParameter writeBasicParameter, AbstractWriteHolder parentAbstractWriteHolder,
Boolean convertAllFiled) { Boolean convertAllFiled) {
@ -110,8 +131,39 @@ public abstract class AbstractWriteHolder extends AbstractHolder implements Writ
this.useDefaultStyle = writeBasicParameter.getUseDefaultStyle(); this.useDefaultStyle = writeBasicParameter.getUseDefaultStyle();
} }
if (writeBasicParameter.getAutomaticMergeHead() == null) {
if (parentAbstractWriteHolder == null) {
this.automaticMergeHead = Boolean.TRUE;
} else {
this.automaticMergeHead = parentAbstractWriteHolder.getAutomaticMergeHead();
}
} else {
this.automaticMergeHead = writeBasicParameter.getAutomaticMergeHead();
}
if (writeBasicParameter.getExcludeColumnFiledNames() == null && parentAbstractWriteHolder != null) {
this.excludeColumnFiledNames = parentAbstractWriteHolder.getExcludeColumnFiledNames();
} else {
this.excludeColumnFiledNames = writeBasicParameter.getExcludeColumnFiledNames();
}
if (writeBasicParameter.getExcludeColumnIndexes() == null && parentAbstractWriteHolder != null) {
this.excludeColumnIndexes = parentAbstractWriteHolder.getExcludeColumnIndexes();
} else {
this.excludeColumnIndexes = writeBasicParameter.getExcludeColumnIndexes();
}
if (writeBasicParameter.getIncludeColumnFiledNames() == null && parentAbstractWriteHolder != null) {
this.includeColumnFiledNames = parentAbstractWriteHolder.getIncludeColumnFiledNames();
} else {
this.includeColumnFiledNames = writeBasicParameter.getIncludeColumnFiledNames();
}
if (writeBasicParameter.getIncludeColumnIndexes() == null && parentAbstractWriteHolder != null) {
this.includeColumnIndexes = parentAbstractWriteHolder.getIncludeColumnIndexes();
} else {
this.includeColumnIndexes = writeBasicParameter.getIncludeColumnIndexes();
}
// Initialization property // Initialization property
this.excelWriteHeadProperty = new ExcelWriteHeadProperty(getClazz(), getHead(), convertAllFiled); this.excelWriteHeadProperty = new ExcelWriteHeadProperty(this, getClazz(), getHead(), convertAllFiled);
// Compatible with old code // Compatible with old code
compatibleOldCode(writeBasicParameter); compatibleOldCode(writeBasicParameter);
@ -148,7 +200,6 @@ public abstract class AbstractWriteHolder extends AbstractHolder implements Writ
getConverterMap().put(ConverterKeyBuild.buildKey(converter.supportJavaTypeKey()), converter); getConverterMap().put(ConverterKeyBuild.buildKey(converter.supportJavaTypeKey()), converter);
} }
} }
} }
/** /**
@ -214,7 +265,7 @@ public abstract class AbstractWriteHolder extends AbstractHolder implements Writ
} }
writeBasicParameter.getCustomWriteHandlerList().add(new AbstractHeadColumnWidthStyleStrategy() { writeBasicParameter.getCustomWriteHandlerList().add(new AbstractHeadColumnWidthStyleStrategy() {
@Override @Override
protected Integer columnWidth(Head head) { protected Integer columnWidth(Head head, Integer columnIndex) {
if (columnWidthMap.containsKey(head.getColumnIndex())) { if (columnWidthMap.containsKey(head.getColumnIndex())) {
return columnWidthMap.get(head.getColumnIndex()) / 256; return columnWidthMap.get(head.getColumnIndex()) / 256;
} }
@ -263,7 +314,7 @@ public abstract class AbstractWriteHolder extends AbstractHolder implements Writ
private void dealColumnWidth(List<WriteHandler> handlerList) { private void dealColumnWidth(List<WriteHandler> handlerList) {
WriteHandler columnWidthStyleStrategy = new AbstractHeadColumnWidthStyleStrategy() { WriteHandler columnWidthStyleStrategy = new AbstractHeadColumnWidthStyleStrategy() {
@Override @Override
protected Integer columnWidth(Head head) { protected Integer columnWidth(Head head, Integer columnIndex) {
if (head == null) { if (head == null) {
return null; return null;
} }
@ -343,6 +394,27 @@ public abstract class AbstractWriteHolder extends AbstractHolder implements Writ
return result; return result;
} }
@Override
public boolean ignore(String fieldName, Integer columnIndex) {
if (fieldName != null) {
if (includeColumnFiledNames != null && !includeColumnFiledNames.contains(fieldName)) {
return true;
}
if (excludeColumnFiledNames != null && excludeColumnFiledNames.contains(fieldName)) {
return true;
}
}
if (columnIndex != null) {
if (includeColumnIndexes != null && !includeColumnIndexes.contains(columnIndex)) {
return true;
}
if (excludeColumnIndexes != null && excludeColumnIndexes.contains(columnIndex)) {
return true;
}
}
return false;
}
public Boolean getNeedHead() { public Boolean getNeedHead() {
return needHead; return needHead;
} }
@ -383,6 +455,46 @@ public abstract class AbstractWriteHolder extends AbstractHolder implements Writ
this.useDefaultStyle = useDefaultStyle; this.useDefaultStyle = useDefaultStyle;
} }
public Boolean getAutomaticMergeHead() {
return automaticMergeHead;
}
public void setAutomaticMergeHead(Boolean automaticMergeHead) {
this.automaticMergeHead = automaticMergeHead;
}
public Collection<Integer> getExcludeColumnIndexes() {
return excludeColumnIndexes;
}
public void setExcludeColumnIndexes(Collection<Integer> excludeColumnIndexes) {
this.excludeColumnIndexes = excludeColumnIndexes;
}
public Collection<String> getExcludeColumnFiledNames() {
return excludeColumnFiledNames;
}
public void setExcludeColumnFiledNames(Collection<String> excludeColumnFiledNames) {
this.excludeColumnFiledNames = excludeColumnFiledNames;
}
public Collection<Integer> getIncludeColumnIndexes() {
return includeColumnIndexes;
}
public void setIncludeColumnIndexes(Collection<Integer> includeColumnIndexes) {
this.includeColumnIndexes = includeColumnIndexes;
}
public Collection<String> getIncludeColumnFiledNames() {
return includeColumnFiledNames;
}
public void setIncludeColumnFiledNames(Collection<String> includeColumnFiledNames) {
this.includeColumnFiledNames = includeColumnFiledNames;
}
@Override @Override
public ExcelWriteHeadProperty excelWriteHeadProperty() { public ExcelWriteHeadProperty excelWriteHeadProperty() {
return getExcelWriteHeadProperty(); return getExcelWriteHeadProperty();
@ -402,4 +514,9 @@ public abstract class AbstractWriteHolder extends AbstractHolder implements Writ
public int relativeHeadRowIndex() { public int relativeHeadRowIndex() {
return getRelativeHeadRowIndex(); return getRelativeHeadRowIndex();
} }
@Override
public boolean automaticMergeHead() {
return getAutomaticMergeHead();
}
} }

16
src/main/java/com/alibaba/excel/write/metadata/holder/WriteHolder.java

@ -28,6 +28,15 @@ public interface WriteHolder extends ConfigurationHolder {
*/ */
Map<Class<? extends WriteHandler>, List<WriteHandler>> writeHandlerMap(); Map<Class<? extends WriteHandler>, List<WriteHandler>> writeHandlerMap();
/**
* Is to determine if a field needs to be ignored
*
* @param fieldName
* @param columnIndex
* @return
*/
boolean ignore(String fieldName, Integer columnIndex);
/** /**
* Whether a header is required for the currently operated cell * Whether a header is required for the currently operated cell
* *
@ -35,6 +44,13 @@ public interface WriteHolder extends ConfigurationHolder {
*/ */
boolean needHead(); boolean needHead();
/**
* Whether need automatic merge headers.
*
* @return
*/
boolean automaticMergeHead();
/** /**
* Writes the head relative to the existing contents of the sheet. Indexes are zero-based. * Writes the head relative to the existing contents of the sheet. Indexes are zero-based.
* *

5
src/main/java/com/alibaba/excel/write/metadata/holder/WriteSheetHolder.java

@ -156,6 +156,11 @@ public class WriteSheetHolder extends AbstractWriteHolder {
int newRowIndex = 0; int newRowIndex = 0;
switch (writeLastRowTypeEnum) { switch (writeLastRowTypeEnum) {
case TEMPLATE_EMPTY: case TEMPLATE_EMPTY:
newRowIndex = Math.max(sheet.getLastRowNum(), cachedSheet.getLastRowNum());
if (newRowIndex != 0 || cachedSheet.getRow(0) != null) {
newRowIndex++;
}
break;
case HAS_DATA: case HAS_DATA:
newRowIndex = Math.max(sheet.getLastRowNum(), cachedSheet.getLastRowNum()); newRowIndex = Math.max(sheet.getLastRowNum(), cachedSheet.getLastRowNum());
newRowIndex++; newRowIndex++;

43
src/main/java/com/alibaba/excel/write/metadata/holder/WriteWorkbookHolder.java

@ -97,6 +97,16 @@ public class WriteWorkbookHolder extends AbstractWriteHolder {
* Whether the encryption * Whether the encryption
*/ */
private String password; private String password;
/**
* Write excel in memory. Default false,the cache file is created and finally written to excel.
* <p>
* Comment and RichTextString are only supported in memory mode.
*/
private Boolean inMemory;
/**
* Excel is also written in the event of an exception being thrown.The default false.
*/
private Boolean writeExcelOnException;
public WriteWorkbookHolder(WriteWorkbook writeWorkbook) { public WriteWorkbookHolder(WriteWorkbook writeWorkbook) {
super(writeWorkbook, null, writeWorkbook.getConvertAllFiled()); super(writeWorkbook, null, writeWorkbook.getConvertAllFiled());
@ -122,7 +132,10 @@ public class WriteWorkbookHolder extends AbstractWriteHolder {
throw new ExcelGenerateException("Copy template failure.", e); throw new ExcelGenerateException("Copy template failure.", e);
} }
if (writeWorkbook.getExcelType() == null) { if (writeWorkbook.getExcelType() == null) {
if (file != null && file.getName().endsWith(ExcelTypeEnum.XLS.getValue())) { boolean isXls = (file != null && file.getName().endsWith(ExcelTypeEnum.XLS.getValue()))
|| (writeWorkbook.getTemplateFile() != null
&& writeWorkbook.getTemplateFile().getName().endsWith(ExcelTypeEnum.XLS.getValue()));
if (isXls) {
this.excelType = ExcelTypeEnum.XLS; this.excelType = ExcelTypeEnum.XLS;
} else { } else {
this.excelType = ExcelTypeEnum.XLSX; this.excelType = ExcelTypeEnum.XLSX;
@ -137,6 +150,16 @@ public class WriteWorkbookHolder extends AbstractWriteHolder {
} }
this.hasBeenInitializedSheet = new HashMap<Integer, WriteSheetHolder>(); this.hasBeenInitializedSheet = new HashMap<Integer, WriteSheetHolder>();
this.password = writeWorkbook.getPassword(); this.password = writeWorkbook.getPassword();
if (writeWorkbook.getInMemory() == null) {
this.inMemory = Boolean.FALSE;
} else {
this.inMemory = writeWorkbook.getInMemory();
}
if (writeWorkbook.getWriteExcelOnException() == null) {
this.writeExcelOnException = Boolean.FALSE;
} else {
this.writeExcelOnException = writeWorkbook.getWriteExcelOnException();
}
} }
private void copyTemplate() throws IOException { private void copyTemplate() throws IOException {
@ -146,7 +169,7 @@ public class WriteWorkbookHolder extends AbstractWriteHolder {
byte[] templateFileByte = null; byte[] templateFileByte = null;
if (writeWorkbook.getTemplateFile() != null) { if (writeWorkbook.getTemplateFile() != null) {
templateFileByte = FileUtils.readFileToByteArray(writeWorkbook.getTemplateFile()); templateFileByte = FileUtils.readFileToByteArray(writeWorkbook.getTemplateFile());
} else if (writeWorkbook.getTemplateInputStream() == null) { } else if (writeWorkbook.getTemplateInputStream() != null) {
try { try {
templateFileByte = IoUtils.toByteArray(writeWorkbook.getTemplateInputStream()); templateFileByte = IoUtils.toByteArray(writeWorkbook.getTemplateInputStream());
} finally { } finally {
@ -262,6 +285,22 @@ public class WriteWorkbookHolder extends AbstractWriteHolder {
this.password = password; this.password = password;
} }
public Boolean getInMemory() {
return inMemory;
}
public void setInMemory(Boolean inMemory) {
this.inMemory = inMemory;
}
public Boolean getWriteExcelOnException() {
return writeExcelOnException;
}
public void setWriteExcelOnException(Boolean writeExcelOnException) {
this.writeExcelOnException = writeExcelOnException;
}
@Override @Override
public HolderEnum holderType() { public HolderEnum holderType() {
return HolderEnum.WORKBOOK; return HolderEnum.WORKBOOK;

5
src/main/java/com/alibaba/excel/write/property/ExcelWriteHeadProperty.java

@ -17,6 +17,7 @@ import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.enums.HeadKindEnum; import com.alibaba.excel.enums.HeadKindEnum;
import com.alibaba.excel.metadata.CellRange; import com.alibaba.excel.metadata.CellRange;
import com.alibaba.excel.metadata.Head; import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.Holder;
import com.alibaba.excel.metadata.property.ColumnWidthProperty; import com.alibaba.excel.metadata.property.ColumnWidthProperty;
import com.alibaba.excel.metadata.property.ExcelContentProperty; import com.alibaba.excel.metadata.property.ExcelContentProperty;
import com.alibaba.excel.metadata.property.ExcelHeadProperty; import com.alibaba.excel.metadata.property.ExcelHeadProperty;
@ -31,8 +32,8 @@ public class ExcelWriteHeadProperty extends ExcelHeadProperty {
private RowHeightProperty headRowHeightProperty; private RowHeightProperty headRowHeightProperty;
private RowHeightProperty contentRowHeightProperty; private RowHeightProperty contentRowHeightProperty;
public ExcelWriteHeadProperty(Class headClazz, List<List<String>> head, Boolean convertAllFiled) { public ExcelWriteHeadProperty(Holder holder, Class headClazz, List<List<String>> head, Boolean convertAllFiled) {
super(headClazz, head, convertAllFiled); super(holder, headClazz, head, convertAllFiled);
if (getHeadKind() != HeadKindEnum.CLASS) { if (getHeadKind() != HeadKindEnum.CLASS) {
return; return;
} }

8
src/main/java/com/alibaba/excel/write/style/column/AbstractHeadColumnWidthStyleStrategy.java

@ -22,7 +22,7 @@ public abstract class AbstractHeadColumnWidthStyleStrategy extends AbstractColum
if (!needSetWidth) { if (!needSetWidth) {
return; return;
} }
Integer width = columnWidth(head); Integer width = columnWidth(head, cell.getColumnIndex());
if (width != null) { if (width != null) {
width = width * 256; width = width * 256;
writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), width); writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), width);
@ -36,9 +36,11 @@ public abstract class AbstractHeadColumnWidthStyleStrategy extends AbstractColum
* if return null,ignore * if return null,ignore
* *
* @param head * @param head
* Nullable * Nullable.
* @param columnIndex
* Not null.
* @return * @return
*/ */
protected abstract Integer columnWidth(Head head); protected abstract Integer columnWidth(Head head, Integer columnIndex);
} }

9
src/main/java/com/alibaba/excel/write/style/column/LongestMatchColumnWidthStyleStrategy.java

@ -6,6 +6,7 @@ import java.util.Map;
import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Cell;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.CellData; import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.Head; import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.util.CollectionUtils; import com.alibaba.excel.util.CollectionUtils;
@ -21,7 +22,7 @@ import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
*/ */
public class LongestMatchColumnWidthStyleStrategy extends AbstractColumnWidthStyleStrategy { public class LongestMatchColumnWidthStyleStrategy extends AbstractColumnWidthStyleStrategy {
private static final int MAX_COLUMN_WIDTH = 256; private static final int MAX_COLUMN_WIDTH = 255;
private static final Map<Integer, Map<Integer, Integer>> CACHE = new HashMap<Integer, Map<Integer, Integer>>(8); private static final Map<Integer, Map<Integer, Integer>> CACHE = new HashMap<Integer, Map<Integer, Integer>>(8);
@ -56,7 +57,11 @@ public class LongestMatchColumnWidthStyleStrategy extends AbstractColumnWidthSty
return cell.getStringCellValue().getBytes().length; return cell.getStringCellValue().getBytes().length;
} }
CellData cellData = cellDataList.get(0); CellData cellData = cellDataList.get(0);
switch (cellData.getType()) { CellDataTypeEnum type = cellData.getType();
if (type == null) {
return -1;
}
switch (type) {
case STRING: case STRING:
return cellData.getStringValue().getBytes().length; return cellData.getStringValue().getBytes().length;
case BOOLEAN: case BOOLEAN:

2
src/main/java/com/alibaba/excel/write/style/column/SimpleColumnWidthStyleStrategy.java

@ -19,7 +19,7 @@ public class SimpleColumnWidthStyleStrategy extends AbstractHeadColumnWidthStyle
} }
@Override @Override
protected Integer columnWidth(Head head) { protected Integer columnWidth(Head head, Integer columnIndex) {
return columnWidth; return columnWidth;
} }
} }

16
src/test/java/com/alibaba/easyexcel/test/core/dataformat/DateFormatData.java

@ -0,0 +1,16 @@
package com.alibaba.easyexcel.test.core.dataformat;
import lombok.Data;
/**
* @author Jiaju Zhuang
*/
@Data
public class DateFormatData {
private String date;
private String dateStringCn;
private String dateStringUs;
private String number;
private String numberStringCn;
private String numberStringUs;
}

61
src/test/java/com/alibaba/easyexcel/test/core/dataformat/DateFormatTest.java

@ -0,0 +1,61 @@
package com.alibaba.easyexcel.test.core.dataformat;
import java.io.File;
import java.util.List;
import java.util.Locale;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.easyexcel.test.util.TestFileUtil;
import com.alibaba.excel.EasyExcel;
/**
*
* @author Jiaju Zhuang
*/
public class DateFormatTest {
private static final Logger LOGGER = LoggerFactory.getLogger(DateFormatTest.class);
private static File file07;
private static File file03;
@BeforeClass
public static void init() {
file07 = TestFileUtil.readFile("dataformat" + File.separator + "dataformat.xlsx");
file03 = TestFileUtil.readFile("dataformat" + File.separator + "dataformat.xls");
}
@Test
public void t01Read07() {
readCn(file07);
readUs(file07);
}
@Test
public void t02Read03() {
readCn(file03);
readUs(file03);
}
private void readCn(File file) {
List<DateFormatData> list =
EasyExcel.read(file, DateFormatData.class, null).locale(Locale.CHINA).sheet().doReadSync();
for (DateFormatData data : list) {
Assert.assertEquals(data.getDate(), data.getDateStringCn());
Assert.assertEquals(data.getNumber(), data.getNumberStringCn());
}
}
private void readUs(File file) {
List<DateFormatData> list =
EasyExcel.read(file, DateFormatData.class, null).locale(Locale.US).sheet().doReadSync();
for (DateFormatData data : list) {
Assert.assertEquals(data.getDate(), data.getDateStringUs());
Assert.assertEquals(data.getNumber(), data.getNumberStringUs());
}
}
}

3
src/test/java/com/alibaba/easyexcel/test/core/fill/FillData.java

@ -14,5 +14,6 @@ public class FillData {
private String name; private String name;
@NumberFormat("#") @NumberFormat("#")
@ExcelProperty(converter = DoubleStringConverter.class) @ExcelProperty(converter = DoubleStringConverter.class)
private double number; private Double number;
private String empty;
} }

18
src/test/java/com/alibaba/easyexcel/test/core/fill/FillDataTest.java

@ -6,35 +6,19 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.junit.Assert; import org.junit.Assert;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.FixMethodOrder; import org.junit.FixMethodOrder;
import org.junit.Test; import org.junit.Test;
import org.junit.runners.MethodSorters; import org.junit.runners.MethodSorters;
import com.alibaba.easyexcel.test.core.style.StyleData;
import com.alibaba.easyexcel.test.core.style.StyleDataListener;
import com.alibaba.easyexcel.test.util.TestFileUtil; import com.alibaba.easyexcel.test.util.TestFileUtil;
import com.alibaba.excel.EasyExcel; import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter; import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.enums.WriteDirectionEnum; import com.alibaba.excel.enums.WriteDirectionEnum;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.write.merge.LoopMergeStrategy; import com.alibaba.excel.write.merge.LoopMergeStrategy;
import com.alibaba.excel.write.merge.OnceAbsoluteMergeStrategy;
import com.alibaba.excel.write.metadata.WriteSheet; import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillConfig; import com.alibaba.excel.write.metadata.fill.FillConfig;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.AbstractVerticalCellStyleStrategy;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.alibaba.excel.write.style.column.SimpleColumnWidthStyleStrategy;
import com.alibaba.excel.write.style.row.SimpleRowHeightStyleStrategy;
/** /**
* *
@ -121,7 +105,7 @@ public class FillDataTest {
private void complexFill(File file, File template) { private void complexFill(File file, File template) {
ExcelWriter excelWriter = EasyExcel.write(file).withTemplate(template).build(); ExcelWriter excelWriter = EasyExcel.write(file).withTemplate(template).build();
WriteSheet writeSheet = EasyExcel.writerSheet().build(); WriteSheet writeSheet = EasyExcel.writerSheet().registerWriteHandler(new LoopMergeStrategy(2, 0)).build();
FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
excelWriter.fill(data(), fillConfig, writeSheet); excelWriter.fill(data(), fillConfig, writeSheet);
excelWriter.fill(data(), fillConfig, writeSheet); excelWriter.fill(data(), fillConfig, writeSheet);

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save