From 5ae29e36055ecde1541516265c9651ad867350b9 Mon Sep 17 00:00:00 2001 From: Jiaju Zhuang Date: Wed, 9 Oct 2019 19:46:45 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E6=8C=81=E9=80=9A=E8=BF=87=E6=A8=A1?= =?UTF-8?q?=E6=9D=BF=E5=A1=AB=E5=85=85=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/LARGEREAD.md | 6 +- img/readme/quickstart/fill/complexFill.png | Bin 0 -> 5119 bytes .../quickstart/fill/complexFillTemplate.png | Bin 0 -> 1938 bytes .../quickstart/fill/complexFillWithTable.png | Bin 0 -> 4917 bytes .../fill/complexFillWithTableTemplate.png | Bin 0 -> 1621 bytes img/readme/quickstart/fill/horizontalFill.png | Bin 0 -> 4608 bytes .../fill/horizontalFillTemplate.png | Bin 0 -> 1553 bytes img/readme/quickstart/fill/listFill.png | Bin 0 -> 1690 bytes .../quickstart/fill/listFillTemplate.png | Bin 0 -> 811 bytes img/readme/quickstart/fill/simpleFill.png | Bin 0 -> 1680 bytes .../quickstart/fill/simpleFillTemplate.png | Bin 0 -> 1841 bytes quickstart.md | 223 ++++++++- .../java/com/alibaba/excel/ExcelReader.java | 10 +- .../java/com/alibaba/excel/ExcelWriter.java | 15 +- .../alibaba/excel/analysis/ExcelAnalyser.java | 2 +- .../excel/analysis/ExcelAnalyserImpl.java | 18 +- ...elExecutor.java => ExcelReadExecutor.java} | 2 +- .../excel/analysis/v03/XlsSaxAnalyser.java | 4 +- .../excel/analysis/v07/XlsxSaxAnalyser.java | 7 +- .../excel/context/AnalysisContext.java | 6 +- .../excel/context/AnalysisContextImpl.java | 20 +- .../alibaba/excel/context/WriteContext.java | 7 +- .../excel/context/WriteContextImpl.java | 183 ++------ .../excel/enums/WriteDirectionEnum.java | 17 + ...RowType.java => WriteLastRowTypeEnum.java} | 2 +- .../WriteTemplateAnalysisCellTypeEnum.java | 17 + .../alibaba/excel/enums/WriteTypeEnum.java | 17 + .../metadata/property/ExcelHeadProperty.java | 18 +- .../com/alibaba/excel/util/StringUtils.java | 3 +- .../com/alibaba/excel/util/WorkBookUtil.java | 44 +- .../alibaba/excel/util/WriteHandlerUtils.java | 196 ++++++++ .../com/alibaba/excel/write/ExcelBuilder.java | 4 +- .../alibaba/excel/write/ExcelBuilderImpl.java | 437 +----------------- .../builder/ExcelWriterSheetBuilder.java | 7 +- .../executor/AbstractExcelWriteExecutor.java | 140 ++++++ .../write/executor/ExcelWriteAddExecutor.java | 179 +++++++ .../write/executor/ExcelWriteExecutor.java | 9 + .../executor/ExcelWriteFillExecutor.java | 377 +++++++++++++++ .../excel/write/handler/CellWriteHandler.java | 44 +- .../excel/write/handler/RowWriteHandler.java | 33 +- .../write/handler/SheetWriteHandler.java | 4 +- .../write/handler/WorkbookWriteHandler.java | 11 +- .../write/merge/AbstractMergeStrategy.java | 12 +- .../write/metadata/fill/AnalysisCell.java | 52 ++- .../excel/write/metadata/fill/FillConfig.java | 83 ++++ .../metadata/holder/WriteSheetHolder.java | 62 +-- .../metadata/holder/WriteWorkbookHolder.java | 80 ++-- .../style/AbstractCellStyleStrategy.java | 40 +- .../AbstractVerticalCellStyleStrategy.java | 4 +- .../style/HorizontalCellStyleStrategy.java | 4 +- .../AbstractColumnWidthStyleStrategy.java | 19 +- .../AbstractHeadColumnWidthStyleStrategy.java | 11 +- .../LongestMatchColumnWidthStyleStrategy.java | 20 +- .../row/AbstractRowHeightStyleStrategy.java | 15 +- .../easyexcel/test/core/fill/FillData.java | 5 +- .../test/core/fill/FillDataTest.java | 113 ++++- .../test/core/large/LargeDataTest.java | 63 ++- .../easyexcel/test/demo/fill/FillData.java | 12 + .../easyexcel/test/demo/fill/FillTest.java | 181 ++++++++ .../demo/write/CustomCellWriteHandler.java | 14 +- .../easyexcel/test/temp/large/LargeData.java | 2 + .../easyexcel/test/temp/poi/PoiTest.java | 87 +++- .../easyexcel/test/temp/poi/PoiWriteTest.java | 26 ++ .../easyexcel/test/temp/simple/HgTest.java | 2 +- src/test/resources/demo/fill/complex.xlsx | Bin 0 -> 10297 bytes .../demo/fill/complexFillWithTable.xlsx | Bin 0 -> 10290 bytes src/test/resources/demo/fill/horizontal.xlsx | Bin 0 -> 10307 bytes src/test/resources/demo/fill/list.xlsx | Bin 0 -> 10029 bytes src/test/resources/demo/fill/simple.xlsx | Bin 0 -> 10135 bytes src/test/resources/fill/complex.xls | Bin 0 -> 19456 bytes src/test/resources/fill/complex.xlsx | Bin 0 -> 10297 bytes src/test/resources/fill/horizontal.xls | Bin 0 -> 19968 bytes src/test/resources/fill/horizontal.xlsx | Bin 0 -> 10307 bytes src/test/resources/fill/simple.xls | Bin 0 -> 19456 bytes src/test/resources/fill/simple.xlsx | Bin 10112 -> 10138 bytes src/test/resources/large/fill.xlsx | Bin 0 -> 11475 bytes 76 files changed, 2163 insertions(+), 806 deletions(-) create mode 100644 img/readme/quickstart/fill/complexFill.png create mode 100644 img/readme/quickstart/fill/complexFillTemplate.png create mode 100644 img/readme/quickstart/fill/complexFillWithTable.png create mode 100644 img/readme/quickstart/fill/complexFillWithTableTemplate.png create mode 100644 img/readme/quickstart/fill/horizontalFill.png create mode 100644 img/readme/quickstart/fill/horizontalFillTemplate.png create mode 100644 img/readme/quickstart/fill/listFill.png create mode 100644 img/readme/quickstart/fill/listFillTemplate.png create mode 100644 img/readme/quickstart/fill/simpleFill.png create mode 100644 img/readme/quickstart/fill/simpleFillTemplate.png rename src/main/java/com/alibaba/excel/analysis/{ExcelExecutor.java => ExcelReadExecutor.java} (90%) create mode 100644 src/main/java/com/alibaba/excel/enums/WriteDirectionEnum.java rename src/main/java/com/alibaba/excel/enums/{WriteLastRowType.java => WriteLastRowTypeEnum.java} (91%) create mode 100644 src/main/java/com/alibaba/excel/enums/WriteTemplateAnalysisCellTypeEnum.java create mode 100644 src/main/java/com/alibaba/excel/enums/WriteTypeEnum.java create mode 100644 src/main/java/com/alibaba/excel/util/WriteHandlerUtils.java create mode 100644 src/main/java/com/alibaba/excel/write/executor/AbstractExcelWriteExecutor.java create mode 100644 src/main/java/com/alibaba/excel/write/executor/ExcelWriteAddExecutor.java create mode 100644 src/main/java/com/alibaba/excel/write/executor/ExcelWriteExecutor.java create mode 100644 src/main/java/com/alibaba/excel/write/executor/ExcelWriteFillExecutor.java create mode 100644 src/main/java/com/alibaba/excel/write/metadata/fill/FillConfig.java create mode 100644 src/test/java/com/alibaba/easyexcel/test/demo/fill/FillData.java create mode 100644 src/test/java/com/alibaba/easyexcel/test/demo/fill/FillTest.java create mode 100644 src/test/resources/demo/fill/complex.xlsx create mode 100644 src/test/resources/demo/fill/complexFillWithTable.xlsx create mode 100644 src/test/resources/demo/fill/horizontal.xlsx create mode 100644 src/test/resources/demo/fill/list.xlsx create mode 100644 src/test/resources/demo/fill/simple.xlsx create mode 100644 src/test/resources/fill/complex.xls create mode 100644 src/test/resources/fill/complex.xlsx create mode 100644 src/test/resources/fill/horizontal.xls create mode 100644 src/test/resources/fill/horizontal.xlsx create mode 100644 src/test/resources/fill/simple.xls create mode 100644 src/test/resources/large/fill.xlsx diff --git a/docs/LARGEREAD.md b/docs/LARGEREAD.md index 512596d4..86147277 100644 --- a/docs/LARGEREAD.md +++ b/docs/LARGEREAD.md @@ -9,6 +9,7 @@ ```java // 强制使用内存存储,这样大概一个20M的excel使用150M(很多临时对象,所以100M会一直GC)的内存 // 这样效率会比上面的复杂的策略高很多 + // 这里再说明下 就是加了个readCache(new MapCache()) 参数而已,其他的参照其他demo写 这里没有写全 EasyExcel.read().readCache(new MapCache()); ``` ### 对并发要求较高,而且都是经常有超级大文件 @@ -16,7 +17,10 @@ // 第一个参数的意思是 多少M共享字符串以后 采用文件存储 单位MB 默认5M // 第二个参数 文件存储时,内存存放多少M缓存数据 默认20M // 比如 你希望用100M内存(这里说的是解析过程中的永久占用,临时对象不算)来解析excel,前面算过了 大概是 20M+90M 所以设置参数为:20 和 90 + // 这里再说明下 就是加了个readCacheSelector(new SimpleReadCacheSelector(5, 20))参数而已,其他的参照其他demo写 这里没有写全 EasyExcel.read().readCacheSelector(new SimpleReadCacheSelector(5, 20)); ``` ### 关于maxCacheActivateSize 也就是前面第二个参数的详细说明 -easyexcel在使用文件存储的时候,会把共享字符串拆分成1000条一批,然后放到文件存储。然后excel来读取共享字符串大概率是按照顺序的,所以默认20M的1000条的数据放在内存,命中后直接返回,没命中去读文件。所以不能设置太小,太小了,很难命中,一直去读取文件,太大了的话会占用过多的内存。 \ No newline at end of file +easyexcel在使用文件存储的时候,会把共享字符串拆分成1000条一批,然后放到文件存储。然后excel来读取共享字符串大概率是按照顺序的,所以默认20M的1000条的数据放在内存,命中后直接返回,没命中去读文件。所以不能设置太小,太小了,很难命中,一直去读取文件,太大了的话会占用过多的内存。 +### 如何判断 maxCacheActivateSize是否需要调整 +开启debug日志会输出`Already put :4000000` 最后一次输出,大概可以得出值为400W,然后看`Cache misses count:4001`得到值为4K,400W/4K=1000 这代表已经`maxCacheActivateSize` 已经非常合理了。如果小于500 问题就非常大了,500到1000 应该都还行。 diff --git a/img/readme/quickstart/fill/complexFill.png b/img/readme/quickstart/fill/complexFill.png new file mode 100644 index 0000000000000000000000000000000000000000..d37bb0f9be8c8907f226d5111ab9b7313411e240 GIT binary patch literal 5119 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 b%*ZR6B}ogslCpSkrPB`f7h literal 0 HcmV?d00001 diff --git a/img/readme/quickstart/fill/complexFillTemplate.png b/img/readme/quickstart/fill/complexFillTemplate.png new file mode 100644 index 0000000000000000000000000000000000000000..14e26b95176f0516826fb5670cf972fc8c6d9102 GIT binary patch literal 1938 zcmV;D2W|L?P)MuYxVX4?cXxN4!or<*oxJ7 z<=5BO$H&LQ!otjqw&R&>$1~ZEXR;m7WILY8c07~qcqXzl zhXIx|f^26!vK`MoB0JYGz;Z^A?W{+(HdVh=(4+#$ZT*>*d|9_j5A->CLsb*_3dTmbsotTfNo!=KEC9^4MU z?ZkEpLH2>&*i_H98U%x#8b&HI%+Q0`8F+333auSUR}EQ0(ADk_e5Lf+{H5)vhy312 z-9wge;Lp)eoRHH>X6Rl@ry!933e4GjCf{OvkN7glp=EYzq+yL87(uqP9zni^CDL$) z9~eQlvmTM1DN>N_Bq7`J-6OKIMhdc>M1*d#-!B$*y)W%8CqCITmmLIQD2O&> zTxg8Z|NmKwjL;ODZVC=`PV9r2xH*~I%FVKK2}TLqQRX~$z===a<|mLE?x?}UjM$DG^SJ|OM-~M5My(1$Vh6UP z0}w}gYjbvF_{iXZE1Mvb3co8b`w-irX;*YGLTr?sA^hL~zpaMtC}TUyja1u-U|vB= zxArP%s`%Eg9Yk_Fq+q9FpnU*e&wlM>VTq$=huF?ecDUnAQ*}FK9jH zj7iZBQ0nkaYx)@G)pxd**+H?g0ma^UDmDMob~dt8uWU$!tnD~E|2%h63t5YN#CF>M z->FwNf=XrMrXkmRzjjvHAvHVnhefI@+q=CpXl@$>qA)>khH0mha}K@a(*FN{*fL%Q z8zfd}m&`ie7c3+tkstX4BCI9Wj^v#FZxp}{4+czRETTN(H}>yIPfTvp)#(ggPN-Fz34Dy6zg-BPVnwWTlKnZ~Jt*GXYnxmjLDP5~cofM{ z8H@hB53W0HPvpWClbkCWkBmp5Oq6jzrwPf{H^%&VMb1@w)Qm@=Oq6lZXY0d#*XV$p zD_8o+cofP+8OLJrR#>I$6qe1}$4qt~;B6 z&QZ%24ABWBF`bkeIu~0upr-?+mJMkBDqq>MjeP_tv}|u2os@H0lth^F-qUmd)J1+} zHniFF*?>tp0LlWC@?vqHZR`X2ReS(#pA9%g$H&;b%ETT|0U}IVGo~tH{oB+S8#OlvSE{ z5mcw6MyH?U)W_Lu=y_wKF3Yh|zvtR=^v&rwohdz~j~AYm?+JW(-LdS$=~(vRbPW6W Y2kuj%@nx5kApigX07*qoM6N<$f}~B-(EtDd literal 0 HcmV?d00001 diff --git a/img/readme/quickstart/fill/complexFillWithTable.png b/img/readme/quickstart/fill/complexFillWithTable.png new file mode 100644 index 0000000000000000000000000000000000000000..c107520113287c13bdffb801826fecd563c21be1 GIT binary patch literal 4917 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 J_V47;NC1cPavWqeHeVMY1 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 literal 0 HcmV?d00001 diff --git a/img/readme/quickstart/fill/horizontalFillTemplate.png b/img/readme/quickstart/fill/horizontalFillTemplate.png new file mode 100644 index 0000000000000000000000000000000000000000..462b86ebb56544c8d5cece22dffedd80aa55be18 GIT binary patch literal 1553 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 literal 0 HcmV?d00001 diff --git a/img/readme/quickstart/fill/listFillTemplate.png b/img/readme/quickstart/fill/listFillTemplate.png new file mode 100644 index 0000000000000000000000000000000000000000..010c8c1665a547af3f340b0dc620230ab6a63a18 GIT binary patch literal 811 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 literal 0 HcmV?d00001 diff --git a/quickstart.md b/quickstart.md index db95048c..728f373d 100644 --- a/quickstart.md +++ b/quickstart.md @@ -47,6 +47,14 @@ DEMO代码地址:[https://github.com/alibaba/easyexcel/blob/master/src/test/ja * [自定义拦截器(下拉,超链接等上面几点都不符合但是要对单元格进行操作的参照这个)](#customHandlerWrite) * [web中的写](#webWrite) +### 填充 +DEMO代码地址:[https://github.com/alibaba/easyexcel/blob/master/src/test/java/com/alibaba/easyexcel/test/demo/fill/FillTest.java](/src/test/java/com/alibaba/easyexcel/test/demo/fill/FillTest.java) +* [最简单的填充](#simpleFill) +* [填充列表](#listFill) +* [复杂的填充](#complexFill) +* [数据量大的复杂填充](#complexFillWithTable) +* [横向的填充](#horizontalFill) + ## 读excel样例 ### 最简单的读 ##### excel示例 @@ -1021,13 +1029,19 @@ public class CustomCellWriteHandler implements CellWriteHandler { @Override public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, - Head head, int relativeRowIndex, boolean isHead) { + Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) { + + } + + @Override + public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, + Head head, Integer relativeRowIndex, Boolean isHead) { } @Override - public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, - Cell cell, Head head, int relativeRowIndex, boolean isHead) { + public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, + List cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { // 这里可以对cell进行任何操作 LOGGER.info("第{}行,第{}列写入完成。", cell.getRowIndex(), cell.getColumnIndex()); if (isHead && cell.getColumnIndex() == 0) { @@ -1118,6 +1132,209 @@ DEMO代码地址:[https://github.com/alibaba/easyexcel/blob/master/src/test/ja EasyExcel.write(response.getOutputStream(), DownloadData.class).sheet("模板").doWrite(data()); } ``` + +## 填充excel样例 +### 最简单的填充 +##### 模板 +![img](img/readme/quickstart/fill/simpleFillTemplate.png) +##### 最终效果 +![img](img/readme/quickstart/fill/simpleFill.png) +##### 对象 +```java +@Data +public class FillData { + private String name; + private double number; +} +``` +##### 代码 +```java + /** + * 最简单的填充 + */ + @Test + public void simpleFill() { + // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 + String templateFileName = + TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "simple.xlsx"; + + // 方案1 根据对象填充 + String fileName = TestFileUtil.getPath() + "simpleFill" + System.currentTimeMillis() + ".xlsx"; + // 这里 会填充到第一个sheet, 然后文件流会自动关闭 + FillData fillData = new FillData(); + fillData.setName("张三"); + fillData.setNumber(5.2); + EasyExcel.write(fileName).withTemplate(templateFileName).sheet().doFill(fillData); + + // 方案2 根据Map填充 + fileName = TestFileUtil.getPath() + "simpleFill" + System.currentTimeMillis() + ".xlsx"; + // 这里 会填充到第一个sheet, 然后文件流会自动关闭 + Map map = new HashMap(); + map.put("name", "张三"); + map.put("number", 5.2); + EasyExcel.write(fileName).withTemplate(templateFileName).sheet().doFill(map); + } +``` + +### 填充列表 +##### 模板 +![img](img/readme/quickstart/fill/listFillTemplate.png) +##### 最终效果 +![img](img/readme/quickstart/fill/listFill.png) +##### 对象 +参照:[对象](#simpleFillObject) +##### 代码 +```java + /** + * 填充列表 + */ + @Test + public void listFill() { + // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 + // 填充list 的时候还要注意 模板中{.} 多了个点 表示list + String templateFileName = + TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "list.xlsx"; + + // 方案1 一下子全部放到内存里面 并填充 + String fileName = TestFileUtil.getPath() + "listFill" + System.currentTimeMillis() + ".xlsx"; + // 这里 会填充到第一个sheet, 然后文件流会自动关闭 + EasyExcel.write(fileName).withTemplate(templateFileName).sheet().doFill(data()); + + // 方案2 分多次 填充 会使用文件缓存(省内存) + fileName = TestFileUtil.getPath() + "listFill" + System.currentTimeMillis() + ".xlsx"; + ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build(); + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + excelWriter.fill(data(), writeSheet); + excelWriter.fill(data(), writeSheet); + // 千万别忘记关闭流 + excelWriter.finish(); + } +``` + +### 复杂的填充 +##### 模板 +![img](img/readme/quickstart/fill/complexFillTemplate.png) +##### 最终效果 +![img](img/readme/quickstart/fill/complexFill.png) +##### 对象 +参照:[对象](#simpleFillObject) +##### 代码 +```java + /** + * 复杂的填充 + */ + @Test + public void complexFill() { + // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 + // {} 代表普通变量 {.} 代表是list的变量 + String templateFileName = + TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "complex.xlsx"; + + String fileName = TestFileUtil.getPath() + "complexFill" + System.currentTimeMillis() + ".xlsx"; + ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build(); + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + // 这里注意 入参用了forceNewRow 代表在写入list的时候不管list下面有没有空行 都会创建一行,然后下面的数据往后移动。默认 是false,会直接使用下一行,如果没有则创建。 + // forceNewRow 如果设置了true,有个缺点 就是他会把所有的数据都放到内存了,所以慎用 + // 简单的说 如果你的模板有list,且list不是最后一行,下面还有数据需要填充 就必须设置 forceNewRow=true 但是这个就会把所有数据放到内存 会很耗内存 + // 如果数据量大 list不是最后一行 参照下一个 + FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); + excelWriter.fill(data(), fillConfig, writeSheet); + excelWriter.fill(data(), fillConfig, writeSheet); + Map map = new HashMap(); + map.put("date", "2019年10月9日13:28:28"); + map.put("total", 1000); + excelWriter.fill(map, writeSheet); + excelWriter.finish(); + } +``` + +### 数据量大的复杂填充 +##### 模板 +![img](img/readme/quickstart/fill/complexFillWithTableTemplate.png) +##### 最终效果 +![img](img/readme/quickstart/fill/complexFillWithTable.png) +##### 对象 +参照:[对象](#simpleFillObject) +##### 代码 +```java + /** + * 数据量大的复杂填充 + *

+ * 这里的解决方案是 确保模板list为最后一行,然后再拼接table.还有03版没救,只能刚正面加内存。 + */ + @Test + public void complexFillWithTable() { + // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 + // {} 代表普通变量 {.} 代表是list的变量 + // 这里模板 删除了list以后的数据,也就是统计的这一行 + String templateFileName = + TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "complexFillWithTable.xlsx"; + + String fileName = TestFileUtil.getPath() + "complexFillWithTable" + System.currentTimeMillis() + ".xlsx"; + ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build(); + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + // 直接写入数据 + excelWriter.fill(data(), writeSheet); + excelWriter.fill(data(), writeSheet); + + // 写入list之前的数据 + Map map = new HashMap(); + map.put("date", "2019年10月9日13:28:28"); + excelWriter.fill(map, writeSheet); + + // list 后面还有个统计 想办法手动写入 + // 这里偷懒直接用list 也可以用对象 + List> totalListList = new ArrayList>(); + List totalList = new ArrayList(); + totalListList.add(totalList); + totalList.add(null); + totalList.add(null); + totalList.add(null); + // 第四列 + totalList.add("统计:1000"); + // 这里是write 别和fill 搞错了 + excelWriter.write(totalListList, writeSheet); + excelWriter.finish(); + // 总体上写法比较复杂 但是也没有想到好的版本 异步的去写入excel 不支持行的删除和移动,也不支持备注这种的写入,所以也排除了可以 + // 新建一个 然后一点点复制过来的方案,最后导致list需要新增行的时候,后面的列的数据没法后移,后续会继续想想解决方案 + } +``` + +### 横向的填充 +##### 模板 +![img](img/readme/quickstart/fill/horizontalFillTemplate.png) +##### 最终效果 +![img](img/readme/quickstart/fill/horizontalFill.png) +##### 对象 +参照:[对象](#simpleFillObject) +##### 代码 +```java + /** + * 横向的填充 + */ + @Test + public void horizontalFill() { + // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 + // {} 代表普通变量 {.} 代表是list的变量 + String templateFileName = + TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "horizontal.xlsx"; + + String fileName = TestFileUtil.getPath() + "horizontalFill" + System.currentTimeMillis() + ".xlsx"; + ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build(); + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + FillConfig fillConfig = FillConfig.builder().direction(WriteDirectionEnum.HORIZONTAL).build(); + excelWriter.fill(data(), fillConfig, writeSheet); + excelWriter.fill(data(), fillConfig, writeSheet); + + Map map = new HashMap(); + map.put("date", "2019年10月9日13:28:28"); + excelWriter.fill(map, writeSheet); + + // 别忘记关闭流 + excelWriter.finish(); + } +``` + ## 测试数据分析 ![POI usermodel PK easyexcel(Excel 2003).png](http://ata2-img.cn-hangzhou.img-pub.aliyun-inc.com/02c4bfbbab99a649788523d04f84a42f.png) ![POI usermodel PK easyexcel(Excel 2007).png](http://ata2-img.cn-hangzhou.img-pub.aliyun-inc.com/f6a8a19ec959f0eb564e652de523fc9e.png) diff --git a/src/main/java/com/alibaba/excel/ExcelReader.java b/src/main/java/com/alibaba/excel/ExcelReader.java index 3cb6b01e..808ee569 100644 --- a/src/main/java/com/alibaba/excel/ExcelReader.java +++ b/src/main/java/com/alibaba/excel/ExcelReader.java @@ -9,7 +9,7 @@ import org.slf4j.LoggerFactory; import com.alibaba.excel.analysis.ExcelAnalyser; import com.alibaba.excel.analysis.ExcelAnalyserImpl; -import com.alibaba.excel.analysis.ExcelExecutor; +import com.alibaba.excel.analysis.ExcelReadExecutor; import com.alibaba.excel.cache.MapCache; import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.event.AnalysisEventListener; @@ -149,12 +149,12 @@ public class ExcelReader { * Parse all sheet content by default */ public void read() { - ExcelExecutor excelExecutor = excelAnalyser.excelExecutor(); - if (excelExecutor.sheetList().isEmpty()) { + ExcelReadExecutor excelReadExecutor = excelAnalyser.excelExecutor(); + if (excelReadExecutor.sheetList().isEmpty()) { LOGGER.warn("Excel doesn't have any sheets."); return; } - for (ReadSheet readSheet : excelExecutor.sheetList()) { + for (ReadSheet readSheet : excelReadExecutor.sheetList()) { read(readSheet); } } @@ -225,7 +225,7 @@ public class ExcelReader { * * @return */ - public ExcelExecutor excelExecutor() { + public ExcelReadExecutor excelExecutor() { checkFinished(); return excelAnalyser.excelExecutor(); } diff --git a/src/main/java/com/alibaba/excel/ExcelWriter.java b/src/main/java/com/alibaba/excel/ExcelWriter.java index 6ee5b6f7..65a99fe5 100644 --- a/src/main/java/com/alibaba/excel/ExcelWriter.java +++ b/src/main/java/com/alibaba/excel/ExcelWriter.java @@ -17,6 +17,7 @@ import com.alibaba.excel.write.merge.OnceAbsoluteMergeStrategy; import com.alibaba.excel.write.metadata.WriteSheet; import com.alibaba.excel.write.metadata.WriteTable; import com.alibaba.excel.write.metadata.WriteWorkbook; +import com.alibaba.excel.write.metadata.fill.FillConfig; /** * Excel Writer This tool is used to write value out to Excel via POI. This object can perform the following two @@ -164,7 +165,19 @@ public class ExcelWriter { * @return */ public ExcelWriter fill(Object data, WriteSheet writeSheet) { - excelBuilder.fill(data, writeSheet); + return fill(data, null, writeSheet); + } + + /** + * Fill value to a sheet + * + * @param data + * @param fillConfig + * @param writeSheet + * @return + */ + public ExcelWriter fill(Object data, FillConfig fillConfig, WriteSheet writeSheet) { + excelBuilder.fill(data, fillConfig, writeSheet); return this; } diff --git a/src/main/java/com/alibaba/excel/analysis/ExcelAnalyser.java b/src/main/java/com/alibaba/excel/analysis/ExcelAnalyser.java index 8d987eaf..6d014bbc 100644 --- a/src/main/java/com/alibaba/excel/analysis/ExcelAnalyser.java +++ b/src/main/java/com/alibaba/excel/analysis/ExcelAnalyser.java @@ -27,7 +27,7 @@ public interface ExcelAnalyser { * * @return Excel file Executor */ - ExcelExecutor excelExecutor(); + ExcelReadExecutor excelExecutor(); /** * get the analysis context. diff --git a/src/main/java/com/alibaba/excel/analysis/ExcelAnalyserImpl.java b/src/main/java/com/alibaba/excel/analysis/ExcelAnalyserImpl.java index cf2dff97..a924b21a 100644 --- a/src/main/java/com/alibaba/excel/analysis/ExcelAnalyserImpl.java +++ b/src/main/java/com/alibaba/excel/analysis/ExcelAnalyserImpl.java @@ -29,7 +29,7 @@ public class ExcelAnalyserImpl implements ExcelAnalyser { private AnalysisContext analysisContext; - private ExcelExecutor excelExecutor; + private ExcelReadExecutor excelReadExecutor; public ExcelAnalyserImpl(ReadWorkbook readWorkbook) { try { @@ -48,7 +48,7 @@ public class ExcelAnalyserImpl implements ExcelAnalyser { ReadWorkbookHolder readWorkbookHolder = analysisContext.readWorkbookHolder(); ExcelTypeEnum excelType = readWorkbookHolder.getExcelType(); if (excelType == null) { - excelExecutor = new XlsxSaxAnalyser(analysisContext, null); + excelReadExecutor = new XlsxSaxAnalyser(analysisContext, null); return; } switch (excelType) { @@ -65,7 +65,7 @@ public class ExcelAnalyserImpl implements ExcelAnalyser { try { decryptedStream = DocumentFactoryHelper.getDecryptedStream(poifsFileSystem.getRoot().getFileSystem(), null); - excelExecutor = new XlsxSaxAnalyser(analysisContext, decryptedStream); + excelReadExecutor = new XlsxSaxAnalyser(analysisContext, decryptedStream); return; } finally { IOUtils.closeQuietly(decryptedStream); @@ -74,10 +74,10 @@ public class ExcelAnalyserImpl implements ExcelAnalyser { poifsFileSystem.close(); } } - excelExecutor = new XlsSaxAnalyser(analysisContext, poifsFileSystem); + excelReadExecutor = new XlsSaxAnalyser(analysisContext, poifsFileSystem); break; case XLSX: - excelExecutor = new XlsxSaxAnalyser(analysisContext, null); + excelReadExecutor = new XlsxSaxAnalyser(analysisContext, null); break; default: } @@ -86,9 +86,9 @@ public class ExcelAnalyserImpl implements ExcelAnalyser { @Override public void analysis(ReadSheet readSheet) { try { - analysisContext.currentSheet(excelExecutor, readSheet); + analysisContext.currentSheet(excelReadExecutor, readSheet); try { - excelExecutor.execute(); + excelReadExecutor.execute(); } catch (ExcelAnalysisStopException e) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Custom stop!"); @@ -153,8 +153,8 @@ public class ExcelAnalyserImpl implements ExcelAnalyser { } @Override - public com.alibaba.excel.analysis.ExcelExecutor excelExecutor() { - return excelExecutor; + public ExcelReadExecutor excelExecutor() { + return excelReadExecutor; } @Override diff --git a/src/main/java/com/alibaba/excel/analysis/ExcelExecutor.java b/src/main/java/com/alibaba/excel/analysis/ExcelReadExecutor.java similarity index 90% rename from src/main/java/com/alibaba/excel/analysis/ExcelExecutor.java rename to src/main/java/com/alibaba/excel/analysis/ExcelReadExecutor.java index 8868d2ee..515eeb96 100644 --- a/src/main/java/com/alibaba/excel/analysis/ExcelExecutor.java +++ b/src/main/java/com/alibaba/excel/analysis/ExcelReadExecutor.java @@ -9,7 +9,7 @@ import com.alibaba.excel.read.metadata.ReadSheet; * * @author Jiaju Zhuang */ -public interface ExcelExecutor { +public interface ExcelReadExecutor { /** * Returns the actual sheet in excel diff --git a/src/main/java/com/alibaba/excel/analysis/v03/XlsSaxAnalyser.java b/src/main/java/com/alibaba/excel/analysis/v03/XlsSaxAnalyser.java index 36d2b74b..7134e6f8 100644 --- a/src/main/java/com/alibaba/excel/analysis/v03/XlsSaxAnalyser.java +++ b/src/main/java/com/alibaba/excel/analysis/v03/XlsSaxAnalyser.java @@ -22,7 +22,7 @@ import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.alibaba.excel.analysis.ExcelExecutor; +import com.alibaba.excel.analysis.ExcelReadExecutor; import com.alibaba.excel.analysis.v03.handlers.BlankOrErrorRecordHandler; import com.alibaba.excel.analysis.v03.handlers.BofRecordHandler; import com.alibaba.excel.analysis.v03.handlers.FormulaRecordHandler; @@ -56,7 +56,7 @@ import com.alibaba.excel.util.CollectionUtils; * * @author jipengfei */ -public class XlsSaxAnalyser implements HSSFListener, ExcelExecutor { +public class XlsSaxAnalyser implements HSSFListener, ExcelReadExecutor { private static final Logger LOGGER = LoggerFactory.getLogger(XlsSaxAnalyser.class); private POIFSFileSystem poifsFileSystem; diff --git a/src/main/java/com/alibaba/excel/analysis/v07/XlsxSaxAnalyser.java b/src/main/java/com/alibaba/excel/analysis/v07/XlsxSaxAnalyser.java index 932fb650..7a34e6cd 100644 --- a/src/main/java/com/alibaba/excel/analysis/v07/XlsxSaxAnalyser.java +++ b/src/main/java/com/alibaba/excel/analysis/v07/XlsxSaxAnalyser.java @@ -13,6 +13,7 @@ import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.apache.poi.openxml4j.opc.OPCPackage; +import org.apache.poi.openxml4j.opc.PackageAccess; import org.apache.poi.openxml4j.opc.PackagePart; import org.apache.poi.xssf.eventusermodel.XSSFReader; import org.apache.poi.xssf.model.StylesTable; @@ -24,7 +25,7 @@ import org.xml.sax.ContentHandler; import org.xml.sax.InputSource; import org.xml.sax.XMLReader; -import com.alibaba.excel.analysis.ExcelExecutor; +import com.alibaba.excel.analysis.ExcelReadExecutor; import com.alibaba.excel.cache.ReadCache; import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.exception.ExcelAnalysisException; @@ -37,7 +38,7 @@ import com.alibaba.excel.util.FileUtils; * * @author jipengfei */ -public class XlsxSaxAnalyser implements ExcelExecutor { +public class XlsxSaxAnalyser implements ExcelReadExecutor { private AnalysisContext analysisContext; private List sheetList; @@ -135,7 +136,7 @@ public class XlsxSaxAnalyser implements ExcelExecutor { } else { FileUtils.writeToFile(tempFile, readWorkbookHolder.getInputStream()); } - return OPCPackage.open(tempFile); + return OPCPackage.open(tempFile, PackageAccess.READ); } @Override diff --git a/src/main/java/com/alibaba/excel/context/AnalysisContext.java b/src/main/java/com/alibaba/excel/context/AnalysisContext.java index 7b8ba86c..5c918f27 100644 --- a/src/main/java/com/alibaba/excel/context/AnalysisContext.java +++ b/src/main/java/com/alibaba/excel/context/AnalysisContext.java @@ -2,7 +2,7 @@ package com.alibaba.excel.context; import java.io.InputStream; -import com.alibaba.excel.analysis.ExcelExecutor; +import com.alibaba.excel.analysis.ExcelReadExecutor; import com.alibaba.excel.event.AnalysisEventListener; import com.alibaba.excel.metadata.Sheet; import com.alibaba.excel.read.metadata.ReadSheet; @@ -22,12 +22,12 @@ public interface AnalysisContext { /** * Select the current table * - * @param excelExecutor + * @param excelReadExecutor * Excel file Executor * @param readSheet * sheet to read */ - void currentSheet(ExcelExecutor excelExecutor, ReadSheet readSheet); + void currentSheet(ExcelReadExecutor excelReadExecutor, ReadSheet readSheet); /** * All information about the workbook you are currently working on diff --git a/src/main/java/com/alibaba/excel/context/AnalysisContextImpl.java b/src/main/java/com/alibaba/excel/context/AnalysisContextImpl.java index d9d7ded5..999aa50e 100644 --- a/src/main/java/com/alibaba/excel/context/AnalysisContextImpl.java +++ b/src/main/java/com/alibaba/excel/context/AnalysisContextImpl.java @@ -5,7 +5,7 @@ import java.io.InputStream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.alibaba.excel.analysis.ExcelExecutor; +import com.alibaba.excel.analysis.ExcelReadExecutor; import com.alibaba.excel.analysis.v07.XlsxSaxAnalyser; import com.alibaba.excel.exception.ExcelAnalysisException; import com.alibaba.excel.metadata.Sheet; @@ -53,13 +53,13 @@ public class AnalysisContextImpl implements AnalysisContext { } @Override - public void currentSheet(ExcelExecutor excelExecutor, ReadSheet readSheet) { + public void currentSheet(ExcelReadExecutor excelReadExecutor, ReadSheet readSheet) { if (readSheet == null) { throw new IllegalArgumentException("Sheet argument cannot be null."); } readSheetHolder = new ReadSheetHolder(readSheet, readWorkbookHolder); currentReadHolder = readSheetHolder; - selectSheet(excelExecutor); + selectSheet(excelReadExecutor); if (readWorkbookHolder.getHasReadSheet().contains(readSheetHolder.getSheetNo())) { throw new ExcelAnalysisException("Cannot read sheet repeatedly."); } @@ -69,9 +69,9 @@ public class AnalysisContextImpl implements AnalysisContext { } } - private void selectSheet(ExcelExecutor excelExecutor) { - if (excelExecutor instanceof XlsxSaxAnalyser) { - selectSheet07(excelExecutor); + private void selectSheet(ExcelReadExecutor excelReadExecutor) { + if (excelReadExecutor instanceof XlsxSaxAnalyser) { + selectSheet07(excelReadExecutor); } else { selectSheet03(); } @@ -87,9 +87,9 @@ public class AnalysisContextImpl implements AnalysisContext { readSheetHolder.setSheetNo(0); } - private void selectSheet07(ExcelExecutor excelExecutor) { + private void selectSheet07(ExcelReadExecutor excelReadExecutor) { if (readSheetHolder.getSheetNo() != null && readSheetHolder.getSheetNo() >= 0) { - for (ReadSheet readSheetExcel : excelExecutor.sheetList()) { + for (ReadSheet readSheetExcel : excelReadExecutor.sheetList()) { if (readSheetExcel.getSheetNo().equals(readSheetHolder.getSheetNo())) { readSheetHolder.setSheetName(readSheetExcel.getSheetName()); return; @@ -98,7 +98,7 @@ public class AnalysisContextImpl implements AnalysisContext { throw new ExcelAnalysisException("Can not find sheet:" + readSheetHolder.getSheetNo()); } if (!StringUtils.isEmpty(readSheetHolder.getSheetName())) { - for (ReadSheet readSheetExcel : excelExecutor.sheetList()) { + for (ReadSheet readSheetExcel : excelReadExecutor.sheetList()) { String sheetName = readSheetExcel.getSheetName(); if (sheetName == null) { continue; @@ -112,7 +112,7 @@ public class AnalysisContextImpl implements AnalysisContext { } } } - ReadSheet readSheetExcel = excelExecutor.sheetList().get(0); + ReadSheet readSheetExcel = excelReadExecutor.sheetList().get(0); readSheetHolder.setSheetNo(readSheetExcel.getSheetNo()); readSheetHolder.setSheetName(readSheetExcel.getSheetName()); } diff --git a/src/main/java/com/alibaba/excel/context/WriteContext.java b/src/main/java/com/alibaba/excel/context/WriteContext.java index 17db4ca2..e5613196 100644 --- a/src/main/java/com/alibaba/excel/context/WriteContext.java +++ b/src/main/java/com/alibaba/excel/context/WriteContext.java @@ -5,6 +5,7 @@ import java.io.OutputStream; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; +import com.alibaba.excel.enums.WriteTypeEnum; import com.alibaba.excel.write.metadata.WriteSheet; import com.alibaba.excel.write.metadata.WriteTable; import com.alibaba.excel.write.metadata.holder.WriteHolder; @@ -21,9 +22,11 @@ public interface WriteContext { /** * If the current sheet already exists, select it; if not, create it * - * @param writeSheet Current sheet + * @param writeSheet + * Current sheet + * @param writeType */ - void currentSheet(WriteSheet writeSheet); + void currentSheet(WriteSheet writeSheet, WriteTypeEnum writeType); /** * If the current table already exists, select it; if not, create it diff --git a/src/main/java/com/alibaba/excel/context/WriteContextImpl.java b/src/main/java/com/alibaba/excel/context/WriteContextImpl.java index 34b90bae..c71d08d7 100644 --- a/src/main/java/com/alibaba/excel/context/WriteContextImpl.java +++ b/src/main/java/com/alibaba/excel/context/WriteContextImpl.java @@ -1,7 +1,6 @@ package com.alibaba.excel.context; import java.io.OutputStream; -import java.util.List; import java.util.Map; import org.apache.poi.ss.usermodel.Cell; @@ -10,18 +9,15 @@ import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.xssf.streaming.SXSSFWorkbook; -import org.apache.poi.xssf.usermodel.XSSFSheet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.alibaba.excel.enums.WriteTypeEnum; import com.alibaba.excel.exception.ExcelGenerateException; +import com.alibaba.excel.metadata.CellData; import com.alibaba.excel.metadata.Head; import com.alibaba.excel.util.WorkBookUtil; -import com.alibaba.excel.write.handler.CellWriteHandler; -import com.alibaba.excel.write.handler.RowWriteHandler; -import com.alibaba.excel.write.handler.SheetWriteHandler; -import com.alibaba.excel.write.handler.WorkbookWriteHandler; -import com.alibaba.excel.write.handler.WriteHandler; +import com.alibaba.excel.util.WriteHandlerUtils; import com.alibaba.excel.write.metadata.WriteSheet; import com.alibaba.excel.write.metadata.WriteTable; import com.alibaba.excel.write.metadata.WriteWorkbook; @@ -65,42 +61,18 @@ public class WriteContextImpl implements WriteContext { LOGGER.debug("Begin to Initialization 'WriteContextImpl'"); } initCurrentWorkbookHolder(writeWorkbook); - beforeWorkbookCreate(); + WriteHandlerUtils.beforeWorkbookCreate(this); try { - writeWorkbookHolder.setWorkbook(WorkBookUtil.createWorkBook(writeWorkbookHolder)); + WorkBookUtil.createWorkBook(writeWorkbookHolder); } catch (Exception e) { throw new ExcelGenerateException("Create workbook failure", e); } - afterWorkbookCreate(); + WriteHandlerUtils.afterWorkbookCreate(this); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Initialization 'WriteContextImpl' complete"); } } - private void beforeWorkbookCreate() { - List handlerList = currentWriteHolder.writeHandlerMap().get(WorkbookWriteHandler.class); - if (handlerList == null || handlerList.isEmpty()) { - return; - } - for (WriteHandler writeHandler : handlerList) { - if (writeHandler instanceof WorkbookWriteHandler) { - ((WorkbookWriteHandler)writeHandler).beforeWorkbookCreate(); - } - } - } - - private void afterWorkbookCreate() { - List handlerList = currentWriteHolder.writeHandlerMap().get(WorkbookWriteHandler.class); - if (handlerList == null || handlerList.isEmpty()) { - return; - } - for (WriteHandler writeHandler : handlerList) { - if (writeHandler instanceof WorkbookWriteHandler) { - ((WorkbookWriteHandler)writeHandler).afterWorkbookCreate(writeWorkbookHolder); - } - } - } - private void initCurrentWorkbookHolder(WriteWorkbook writeWorkbook) { writeWorkbookHolder = new WriteWorkbookHolder(writeWorkbook); currentWriteHolder = writeWorkbookHolder; @@ -113,7 +85,7 @@ public class WriteContextImpl implements WriteContext { * @param writeSheet */ @Override - public void currentSheet(WriteSheet writeSheet) { + public void currentSheet(WriteSheet writeSheet, WriteTypeEnum writeType) { if (writeSheet == null) { throw new IllegalArgumentException("Sheet argument cannot be null"); } @@ -137,38 +109,10 @@ public class WriteContextImpl implements WriteContext { return; } initCurrentSheetHolder(writeSheet); - beforeSheetCreate(); + WriteHandlerUtils.beforeSheetCreate(this); // Initialization current sheet - initSheet(); - afterSheetCreate(); - } - - private void beforeSheetCreate() { - List handlerList = currentWriteHolder.writeHandlerMap().get(SheetWriteHandler.class); - if (handlerList == null || handlerList.isEmpty()) { - return; - } - for (WriteHandler writeHandler : handlerList) { - if (writeHandler instanceof SheetWriteHandler) { - ((SheetWriteHandler)writeHandler).beforeSheetCreate(writeWorkbookHolder, writeSheetHolder); - } - } - } - - private void afterSheetCreate() { - List handlerList = currentWriteHolder.writeHandlerMap().get(SheetWriteHandler.class); - if (handlerList == null || handlerList.isEmpty()) { - return; - } - for (WriteHandler writeHandler : handlerList) { - if (writeHandler instanceof SheetWriteHandler) { - ((SheetWriteHandler)writeHandler).afterSheetCreate(writeWorkbookHolder, writeSheetHolder); - } - } - if (null != writeWorkbookHolder.getWriteWorkbook().getWriteHandler()) { - writeWorkbookHolder.getWriteWorkbook().getWriteHandler().sheet(writeSheetHolder.getSheetNo(), - writeSheetHolder.getSheet()); - } + initSheet(writeType); + WriteHandlerUtils.afterSheetCreate(this); } private void initCurrentSheetHolder(WriteSheet writeSheet) { @@ -181,29 +125,24 @@ public class WriteContextImpl implements WriteContext { } } - private void initSheet() { - try { - if (writeWorkbookHolder.getXssfWorkbook() != null) { - writeSheetHolder - .setXssfSheet(writeWorkbookHolder.getXssfWorkbook().getSheetAt(writeSheetHolder.getSheetNo())); - } - } catch (Exception e) { - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Can not find XSSFSheet:{}.", writeSheetHolder.getSheetNo()); - } - } + private void initSheet(WriteTypeEnum writeType) { Sheet currentSheet; try { currentSheet = writeWorkbookHolder.getWorkbook().getSheetAt(writeSheetHolder.getSheetNo()); + writeSheetHolder + .setCachedSheet(writeWorkbookHolder.getCachedWorkbook().getSheetAt(writeSheetHolder.getSheetNo())); } catch (Exception e) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Can not find sheet:{} ,now create it", writeSheetHolder.getSheetNo()); } currentSheet = WorkBookUtil.createSheet(writeWorkbookHolder.getWorkbook(), writeSheetHolder.getSheetName()); + writeSheetHolder.setCachedSheet(currentSheet); } writeSheetHolder.setSheet(currentSheet); - // Initialization head - initHead(writeSheetHolder.excelWriteHeadProperty()); + if (WriteTypeEnum.ADD.equals(writeType)) { + // Initialization head + initHead(writeSheetHolder.excelWriteHeadProperty()); + } } public void initHead(ExcelWriteHeadProperty excelWriteHeadProperty) { @@ -216,42 +155,13 @@ public class WriteContextImpl implements WriteContext { addMergedRegionToCurrentSheet(excelWriteHeadProperty, newRowIndex); for (int relativeRowIndex = 0, i = newRowIndex; i < excelWriteHeadProperty.getHeadRowNumber() + newRowIndex; i++, relativeRowIndex++) { - beforeRowCreate(newRowIndex, relativeRowIndex); + WriteHandlerUtils.beforeRowCreate(this, newRowIndex, relativeRowIndex, Boolean.TRUE); Row row = WorkBookUtil.createRow(writeSheetHolder.getSheet(), i); - afterRowCreate(row, relativeRowIndex); + WriteHandlerUtils.afterRowCreate(this, row, relativeRowIndex, Boolean.TRUE); addOneRowOfHeadDataToExcel(row, excelWriteHeadProperty.getHeadMap(), relativeRowIndex); } } - private void beforeRowCreate(int rowIndex, int relativeRowIndex) { - List handlerList = currentWriteHolder.writeHandlerMap().get(RowWriteHandler.class); - if (handlerList == null || handlerList.isEmpty()) { - return; - } - for (WriteHandler writeHandler : handlerList) { - if (writeHandler instanceof RowWriteHandler) { - ((RowWriteHandler)writeHandler).beforeRowCreate(writeSheetHolder, writeTableHolder, rowIndex, - relativeRowIndex, true); - } - } - } - - private void afterRowCreate(Row row, int relativeRowIndex) { - List handlerList = currentWriteHolder.writeHandlerMap().get(RowWriteHandler.class); - if (handlerList == null || handlerList.isEmpty()) { - return; - } - for (WriteHandler writeHandler : handlerList) { - if (writeHandler instanceof RowWriteHandler) { - ((RowWriteHandler)writeHandler).afterRowCreate(writeSheetHolder, writeTableHolder, row, - relativeRowIndex, true); - } - } - if (null != writeWorkbookHolder.getWriteWorkbook().getWriteHandler()) { - writeWorkbookHolder.getWriteWorkbook().getWriteHandler().row(row.getRowNum(), row); - } - } - private void addMergedRegionToCurrentSheet(ExcelWriteHeadProperty excelWriteHeadProperty, int rowIndex) { for (com.alibaba.excel.metadata.CellRange cellRangeModel : excelWriteHeadProperty.headCellRangeList()) { writeSheetHolder.getSheet().addMergedRegion(new CellRangeAddress(cellRangeModel.getFirstRow() + rowIndex, @@ -262,38 +172,13 @@ public class WriteContextImpl implements WriteContext { private void addOneRowOfHeadDataToExcel(Row row, Map headMap, int relativeRowIndex) { for (Map.Entry entry : headMap.entrySet()) { Head head = entry.getValue(); - beforeCellCreate(row, head, relativeRowIndex); - Cell cell = WorkBookUtil.createCell(row, entry.getKey(), head.getHeadNameList().get(relativeRowIndex)); - afterCellCreate(head, cell, relativeRowIndex); - } - } - - private void beforeCellCreate(Row row, Head head, int relativeRowIndex) { - List handlerList = currentWriteHolder.writeHandlerMap().get(CellWriteHandler.class); - if (handlerList == null || handlerList.isEmpty()) { - return; - } - for (WriteHandler writeHandler : handlerList) { - if (writeHandler instanceof CellWriteHandler) { - ((CellWriteHandler)writeHandler).beforeCellCreate(writeSheetHolder, writeTableHolder, row, head, - relativeRowIndex, true); - } - } - } - - private void afterCellCreate(Head head, Cell cell, int relativeRowIndex) { - List handlerList = currentWriteHolder.writeHandlerMap().get(CellWriteHandler.class); - if (handlerList == null || handlerList.isEmpty()) { - return; - } - for (WriteHandler writeHandler : handlerList) { - if (writeHandler instanceof CellWriteHandler) { - ((CellWriteHandler)writeHandler).afterCellCreate(writeSheetHolder, writeTableHolder, null, cell, head, - relativeRowIndex, true); - } - } - if (null != writeWorkbookHolder.getWriteWorkbook().getWriteHandler()) { - writeWorkbookHolder.getWriteWorkbook().getWriteHandler().cell(cell.getRowIndex(), cell); + int columnIndex = entry.getKey(); + WriteHandlerUtils.beforeCellCreate(this, row, head, columnIndex, relativeRowIndex, Boolean.TRUE); + Cell cell = row.createCell(columnIndex); + WriteHandlerUtils.afterCellCreate(this, cell, head, relativeRowIndex, Boolean.TRUE); + cell.setCellValue(head.getHeadNameList().get(relativeRowIndex)); + CellData cellData = null; + WriteHandlerUtils.afterCellDispose(this, cellData, cell, head, relativeRowIndex, Boolean.TRUE); } } @@ -352,10 +237,10 @@ public class WriteContextImpl implements WriteContext { @Override public void finish() { + WriteHandlerUtils.afterWorkbookDispose(this); if (writeWorkbookHolder == null) { return; } - try { writeWorkbookHolder.getWorkbook().write(writeWorkbookHolder.getOutputStream()); writeWorkbookHolder.getWorkbook().close(); @@ -370,13 +255,6 @@ public class WriteContextImpl implements WriteContext { } catch (Throwable t) { throwCanNotCloseIo(t); } - try { - if (writeWorkbookHolder.getTempTemplateInputStream() != null) { - writeWorkbookHolder.getTempTemplateInputStream().close(); - } - } catch (Throwable t) { - throwCanNotCloseIo(t); - } try { if (writeWorkbookHolder.getAutoCloseStream() && writeWorkbookHolder.getOutputStream() != null) { writeWorkbookHolder.getOutputStream().close(); @@ -385,9 +263,8 @@ public class WriteContextImpl implements WriteContext { throwCanNotCloseIo(t); } try { - if (!writeWorkbookHolder.getAutoCloseStream() && writeWorkbookHolder.getFile() != null - && writeWorkbookHolder.getOutputStream() != null) { - writeWorkbookHolder.getOutputStream().close(); + if (writeWorkbookHolder.getTempTemplateInputStream() != null) { + writeWorkbookHolder.getTempTemplateInputStream().close(); } } catch (Throwable t) { throwCanNotCloseIo(t); diff --git a/src/main/java/com/alibaba/excel/enums/WriteDirectionEnum.java b/src/main/java/com/alibaba/excel/enums/WriteDirectionEnum.java new file mode 100644 index 00000000..78803f18 --- /dev/null +++ b/src/main/java/com/alibaba/excel/enums/WriteDirectionEnum.java @@ -0,0 +1,17 @@ +package com.alibaba.excel.enums; + +/** + * Direction of writing + * + * @author Jiaju Zhuang + **/ +public enum WriteDirectionEnum { + /** + * Vertical write. + */ + VERTICAL, + /** + * Horizontal write. + */ + HORIZONTAL,; +} diff --git a/src/main/java/com/alibaba/excel/enums/WriteLastRowType.java b/src/main/java/com/alibaba/excel/enums/WriteLastRowTypeEnum.java similarity index 91% rename from src/main/java/com/alibaba/excel/enums/WriteLastRowType.java rename to src/main/java/com/alibaba/excel/enums/WriteLastRowTypeEnum.java index bd477bdd..d9a41aed 100644 --- a/src/main/java/com/alibaba/excel/enums/WriteLastRowType.java +++ b/src/main/java/com/alibaba/excel/enums/WriteLastRowTypeEnum.java @@ -5,7 +5,7 @@ package com.alibaba.excel.enums; * * @author Jiaju Zhuang **/ -public enum WriteLastRowType { +public enum WriteLastRowTypeEnum { /** * Excel are created without templates ,And any data has been written; */ diff --git a/src/main/java/com/alibaba/excel/enums/WriteTemplateAnalysisCellTypeEnum.java b/src/main/java/com/alibaba/excel/enums/WriteTemplateAnalysisCellTypeEnum.java new file mode 100644 index 00000000..e9d22a8b --- /dev/null +++ b/src/main/java/com/alibaba/excel/enums/WriteTemplateAnalysisCellTypeEnum.java @@ -0,0 +1,17 @@ +package com.alibaba.excel.enums; + +/** + * Type of template to read when writing + * + * @author Jiaju Zhuang + **/ +public enum WriteTemplateAnalysisCellTypeEnum { + /** + * Common field. + */ + COMMON, + /** + * A collection of fields. + */ + COLLECTION,; +} diff --git a/src/main/java/com/alibaba/excel/enums/WriteTypeEnum.java b/src/main/java/com/alibaba/excel/enums/WriteTypeEnum.java new file mode 100644 index 00000000..9dbbd314 --- /dev/null +++ b/src/main/java/com/alibaba/excel/enums/WriteTypeEnum.java @@ -0,0 +1,17 @@ +package com.alibaba.excel.enums; + +/** + * Enumeration of write methods + * + * @author Jiaju Zhuang + **/ +public enum WriteTypeEnum { + /** + * Add. + */ + ADD, + /** + * Fill. + */ + FILL,; +} diff --git a/src/main/java/com/alibaba/excel/metadata/property/ExcelHeadProperty.java b/src/main/java/com/alibaba/excel/metadata/property/ExcelHeadProperty.java index a1200d5c..09d55603 100644 --- a/src/main/java/com/alibaba/excel/metadata/property/ExcelHeadProperty.java +++ b/src/main/java/com/alibaba/excel/metadata/property/ExcelHeadProperty.java @@ -52,6 +52,10 @@ public class ExcelHeadProperty { * Configuration column information */ private Map contentPropertyMap; + /** + * Configuration column information + */ + private Map fieldNameContentPropertyMap; /** * Fields ignored */ @@ -61,6 +65,7 @@ public class ExcelHeadProperty { this.headClazz = headClazz; headMap = new TreeMap(); contentPropertyMap = new TreeMap(); + fieldNameContentPropertyMap = new HashMap(); ignoreMap = new HashMap(16); headKind = HeadKindEnum.NONE; headRowNumber = 0; @@ -78,10 +83,6 @@ public class ExcelHeadProperty { if (LOGGER.isDebugEnabled()) { LOGGER.debug("The initialization sheet/table 'ExcelHeadProperty' is complete , head kind is {}", headKind); } - if (!hasHead()) { - LOGGER.warn( - "The table has no header set and all annotations will not be read.If you want to use annotations, please use set head class in ExcelWriterBuilder/ExcelWriterSheetBuilder/ExcelWriterTableBuilder"); - } } private void initHeadRowNumber() { @@ -190,6 +191,7 @@ public class ExcelHeadProperty { .setNumberFormatProperty(NumberFormatProperty.build(field.getAnnotation(NumberFormat.class))); headMap.put(index, head); contentPropertyMap.put(index, excelContentProperty); + fieldNameContentPropertyMap.put(field.getName(), excelContentProperty); } public Class getHeadClazz() { @@ -236,6 +238,14 @@ public class ExcelHeadProperty { this.contentPropertyMap = contentPropertyMap; } + public Map getFieldNameContentPropertyMap() { + return fieldNameContentPropertyMap; + } + + public void setFieldNameContentPropertyMap(Map fieldNameContentPropertyMap) { + this.fieldNameContentPropertyMap = fieldNameContentPropertyMap; + } + public Map getIgnoreMap() { return ignoreMap; } diff --git a/src/main/java/com/alibaba/excel/util/StringUtils.java b/src/main/java/com/alibaba/excel/util/StringUtils.java index 25ed127d..948ae652 100644 --- a/src/main/java/com/alibaba/excel/util/StringUtils.java +++ b/src/main/java/com/alibaba/excel/util/StringUtils.java @@ -6,10 +6,11 @@ package com.alibaba.excel.util; * @author jipengfei */ public class StringUtils { + public static final String EMPTY = ""; private StringUtils() {} public static boolean isEmpty(Object str) { - return (str == null || "".equals(str)); + return (str == null || EMPTY.equals(str)); } } diff --git a/src/main/java/com/alibaba/excel/util/WorkBookUtil.java b/src/main/java/com/alibaba/excel/util/WorkBookUtil.java index 78cbd279..949c3da5 100644 --- a/src/main/java/com/alibaba/excel/util/WorkBookUtil.java +++ b/src/main/java/com/alibaba/excel/util/WorkBookUtil.java @@ -2,6 +2,7 @@ package com.alibaba.excel.util; import java.io.IOException; +import org.apache.poi.hssf.usermodel.HSSFFont; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.poifs.filesystem.POIFSFileSystem; @@ -22,36 +23,31 @@ import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder; */ public class WorkBookUtil { + private static final int ROW_ACCESS_WINDOW_SIZE = 500; + private WorkBookUtil() {} - public static Workbook createWorkBook(WriteWorkbookHolder writeWorkbookHolder) - throws IOException, InvalidFormatException { + public static void createWorkBook(WriteWorkbookHolder writeWorkbookHolder) throws IOException { if (ExcelTypeEnum.XLSX.equals(writeWorkbookHolder.getExcelType())) { - XSSFWorkbook xssfWorkbook = null; - if (writeWorkbookHolder.getTemplateFile() != null) { - xssfWorkbook = new XSSFWorkbook(writeWorkbookHolder.getTemplateFile()); - } - if (writeWorkbookHolder.getTemplateInputStream() != null) { - xssfWorkbook = new XSSFWorkbook(writeWorkbookHolder.getTemplateInputStream()); + if (writeWorkbookHolder.getTempTemplateInputStream() != null) { + XSSFWorkbook xssfWorkbook = new XSSFWorkbook(writeWorkbookHolder.getTempTemplateInputStream()); + writeWorkbookHolder.setCachedWorkbook(xssfWorkbook); + writeWorkbookHolder.setWorkbook(new SXSSFWorkbook(xssfWorkbook, ROW_ACCESS_WINDOW_SIZE)); + return; } - // When using SXSSFWorkbook, you can't get the actual last line.But we need to read the last line when we - // are using the template, so we cache it - if (xssfWorkbook != null) { - writeWorkbookHolder.setXssfWorkbook(xssfWorkbook); - for (int i = 0; i < xssfWorkbook.getNumberOfSheets(); i++) { - writeWorkbookHolder.getTemplateLastRowMap().put(i, xssfWorkbook.getSheetAt(i).getLastRowNum()); - } - return new SXSSFWorkbook(xssfWorkbook); - } - return new SXSSFWorkbook(500); - } - if (writeWorkbookHolder.getTemplateFile() != null) { - return new HSSFWorkbook(new POIFSFileSystem(writeWorkbookHolder.getTemplateFile())); + SXSSFWorkbook sxssWorkbook = new SXSSFWorkbook(ROW_ACCESS_WINDOW_SIZE); + writeWorkbookHolder.setCachedWorkbook(sxssWorkbook); + writeWorkbookHolder.setWorkbook(sxssWorkbook); + return; } - if (writeWorkbookHolder.getTemplateInputStream() != null) { - return new HSSFWorkbook(new POIFSFileSystem(writeWorkbookHolder.getTemplateInputStream())); + HSSFWorkbook hssfWorkbook; + if (writeWorkbookHolder.getTempTemplateInputStream() != null) { + hssfWorkbook = new HSSFWorkbook(new POIFSFileSystem(writeWorkbookHolder.getTempTemplateInputStream())); + } else { + hssfWorkbook = new HSSFWorkbook(); } - return new HSSFWorkbook(); + writeWorkbookHolder.setCachedWorkbook(hssfWorkbook); + writeWorkbookHolder.setWorkbook(hssfWorkbook); } public static Sheet createSheet(Workbook workbook, String sheetName) { diff --git a/src/main/java/com/alibaba/excel/util/WriteHandlerUtils.java b/src/main/java/com/alibaba/excel/util/WriteHandlerUtils.java new file mode 100644 index 00000000..b303a31e --- /dev/null +++ b/src/main/java/com/alibaba/excel/util/WriteHandlerUtils.java @@ -0,0 +1,196 @@ +package com.alibaba.excel.util; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Row; + +import com.alibaba.excel.context.WriteContext; +import com.alibaba.excel.metadata.CellData; +import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.write.handler.CellWriteHandler; +import com.alibaba.excel.write.handler.RowWriteHandler; +import com.alibaba.excel.write.handler.SheetWriteHandler; +import com.alibaba.excel.write.handler.WorkbookWriteHandler; +import com.alibaba.excel.write.handler.WriteHandler; + +/** + * Write handler utils + * + * @author Jiaju Zhuang + */ +public class WriteHandlerUtils { + + private WriteHandlerUtils() {} + + public static void beforeWorkbookCreate(WriteContext writeContext) { + List handlerList = + writeContext.writeWorkbookHolder().writeHandlerMap().get(WorkbookWriteHandler.class); + if (handlerList == null || handlerList.isEmpty()) { + return; + } + for (WriteHandler writeHandler : handlerList) { + if (writeHandler instanceof WorkbookWriteHandler) { + ((WorkbookWriteHandler)writeHandler).beforeWorkbookCreate(); + } + } + } + + public static void afterWorkbookCreate(WriteContext writeContext) { + List handlerList = + writeContext.writeWorkbookHolder().writeHandlerMap().get(WorkbookWriteHandler.class); + if (handlerList == null || handlerList.isEmpty()) { + return; + } + for (WriteHandler writeHandler : handlerList) { + if (writeHandler instanceof WorkbookWriteHandler) { + ((WorkbookWriteHandler)writeHandler).afterWorkbookCreate(writeContext.writeWorkbookHolder()); + } + } + } + + public static void afterWorkbookDispose(WriteContext writeContext) { + List handlerList = + writeContext.writeWorkbookHolder().writeHandlerMap().get(WorkbookWriteHandler.class); + if (handlerList == null || handlerList.isEmpty()) { + return; + } + for (WriteHandler writeHandler : handlerList) { + if (writeHandler instanceof WorkbookWriteHandler) { + ((WorkbookWriteHandler)writeHandler).afterWorkbookDispose(writeContext.writeWorkbookHolder()); + } + } + } + + public static void beforeSheetCreate(WriteContext writeContext) { + List handlerList = writeContext.writeSheetHolder().writeHandlerMap().get(SheetWriteHandler.class); + if (handlerList == null || handlerList.isEmpty()) { + return; + } + for (WriteHandler writeHandler : handlerList) { + if (writeHandler instanceof SheetWriteHandler) { + ((SheetWriteHandler)writeHandler).beforeSheetCreate(writeContext.writeWorkbookHolder(), + writeContext.writeSheetHolder()); + } + } + } + + public static void afterSheetCreate(WriteContext writeContext) { + List handlerList = writeContext.writeSheetHolder().writeHandlerMap().get(SheetWriteHandler.class); + if (handlerList == null || handlerList.isEmpty()) { + return; + } + for (WriteHandler writeHandler : handlerList) { + if (writeHandler instanceof SheetWriteHandler) { + ((SheetWriteHandler)writeHandler).afterSheetCreate(writeContext.writeWorkbookHolder(), + writeContext.writeSheetHolder()); + } + } + if (null != writeContext.writeWorkbookHolder().getWriteWorkbook().getWriteHandler()) { + writeContext.writeWorkbookHolder().getWriteWorkbook().getWriteHandler() + .sheet(writeContext.writeSheetHolder().getSheetNo(), writeContext.writeSheetHolder().getSheet()); + } + } + + public static void beforeCellCreate(WriteContext writeContext, Row row, Head head, Integer columnIndex, + Integer relativeRowIndex, Boolean isHead) { + List handlerList = + writeContext.currentWriteHolder().writeHandlerMap().get(CellWriteHandler.class); + if (handlerList == null || handlerList.isEmpty()) { + return; + } + for (WriteHandler writeHandler : handlerList) { + if (writeHandler instanceof CellWriteHandler) { + ((CellWriteHandler)writeHandler).beforeCellCreate(writeContext.writeSheetHolder(), + writeContext.writeTableHolder(), row, head, columnIndex, relativeRowIndex, isHead); + } + } + } + + public static void afterCellCreate(WriteContext writeContext, Cell cell, Head head, Integer relativeRowIndex, + Boolean isHead) { + List handlerList = + writeContext.currentWriteHolder().writeHandlerMap().get(CellWriteHandler.class); + if (handlerList == null || handlerList.isEmpty()) { + return; + } + for (WriteHandler writeHandler : handlerList) { + if (writeHandler instanceof CellWriteHandler) { + ((CellWriteHandler)writeHandler).afterCellCreate(writeContext.writeSheetHolder(), + writeContext.writeTableHolder(), cell, head, relativeRowIndex, isHead); + } + } + } + + public static void afterCellDispose(WriteContext writeContext, CellData cellData, Cell cell, Head head, + Integer relativeRowIndex, Boolean isHead) { + List cellDataList = new ArrayList(); + if (cell != null) { + cellDataList.add(cellData); + } + afterCellDispose(writeContext, cellDataList, cell, head, relativeRowIndex, isHead); + } + + public static void afterCellDispose(WriteContext writeContext, List cellDataList, Cell cell, Head head, + Integer relativeRowIndex, Boolean isHead) { + List handlerList = + writeContext.currentWriteHolder().writeHandlerMap().get(CellWriteHandler.class); + if (handlerList == null || handlerList.isEmpty()) { + return; + } + for (WriteHandler writeHandler : handlerList) { + if (writeHandler instanceof CellWriteHandler) { + ((CellWriteHandler)writeHandler).afterCellDispose(writeContext.writeSheetHolder(), + writeContext.writeTableHolder(), cellDataList, cell, head, relativeRowIndex, isHead); + } + } + if (null != writeContext.writeWorkbookHolder().getWriteWorkbook().getWriteHandler()) { + writeContext.writeWorkbookHolder().getWriteWorkbook().getWriteHandler().cell(cell.getRowIndex(), cell); + } + } + + public static void beforeRowCreate(WriteContext writeContext, Integer rowIndex, Integer relativeRowIndex, + Boolean isHead) { + List handlerList = writeContext.currentWriteHolder().writeHandlerMap().get(RowWriteHandler.class); + if (handlerList == null || handlerList.isEmpty()) { + return; + } + for (WriteHandler writeHandler : handlerList) { + if (writeHandler instanceof RowWriteHandler) { + ((RowWriteHandler)writeHandler).beforeRowCreate(writeContext.writeSheetHolder(), + writeContext.writeTableHolder(), rowIndex, relativeRowIndex, isHead); + } + } + } + + public static void afterRowCreate(WriteContext writeContext, Row row, Integer relativeRowIndex, Boolean isHead) { + List handlerList = writeContext.currentWriteHolder().writeHandlerMap().get(RowWriteHandler.class); + if (handlerList == null || handlerList.isEmpty()) { + return; + } + for (WriteHandler writeHandler : handlerList) { + if (writeHandler instanceof RowWriteHandler) { + ((RowWriteHandler)writeHandler).afterRowCreate(writeContext.writeSheetHolder(), + writeContext.writeTableHolder(), row, relativeRowIndex, isHead); + } + } + + } + + public static void afterRowDispose(WriteContext writeContext, Row row, Integer relativeRowIndex, Boolean isHead) { + List handlerList = writeContext.currentWriteHolder().writeHandlerMap().get(RowWriteHandler.class); + if (handlerList == null || handlerList.isEmpty()) { + return; + } + for (WriteHandler writeHandler : handlerList) { + if (writeHandler instanceof RowWriteHandler) { + ((RowWriteHandler)writeHandler).afterRowDispose(writeContext.writeSheetHolder(), + writeContext.writeTableHolder(), row, relativeRowIndex, isHead); + } + } + if (null != writeContext.writeWorkbookHolder().getWriteWorkbook().getWriteHandler()) { + writeContext.writeWorkbookHolder().getWriteWorkbook().getWriteHandler().row(row.getRowNum(), row); + } + } +} diff --git a/src/main/java/com/alibaba/excel/write/ExcelBuilder.java b/src/main/java/com/alibaba/excel/write/ExcelBuilder.java index 6dcf9474..8a60444e 100644 --- a/src/main/java/com/alibaba/excel/write/ExcelBuilder.java +++ b/src/main/java/com/alibaba/excel/write/ExcelBuilder.java @@ -6,6 +6,7 @@ import com.alibaba.excel.context.WriteContext; import com.alibaba.excel.write.merge.OnceAbsoluteMergeStrategy; import com.alibaba.excel.write.metadata.WriteSheet; import com.alibaba.excel.write.metadata.WriteTable; +import com.alibaba.excel.write.metadata.fill.FillConfig; /** * @author jipengfei @@ -40,9 +41,10 @@ public interface ExcelBuilder { * WorkBook fill value * * @param data + * @param fillConfig * @param writeSheet */ - void fill(Object data, WriteSheet writeSheet); + void fill(Object data, FillConfig fillConfig, WriteSheet writeSheet); /** * Creates new cell range. Indexes are zero-based. diff --git a/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java b/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java index 3bbda250..3d8067a8 100644 --- a/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java +++ b/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java @@ -1,63 +1,29 @@ package com.alibaba.excel.write; -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import org.apache.poi.hssf.usermodel.HSSFWorkbook; -import org.apache.poi.ss.usermodel.Cell; -import org.apache.poi.ss.usermodel.ClientAnchor; -import org.apache.poi.ss.usermodel.CreationHelper; -import org.apache.poi.ss.usermodel.Drawing; -import org.apache.poi.ss.usermodel.Row; -import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.util.CellRangeAddress; -import org.apache.poi.xssf.usermodel.XSSFSheet; import com.alibaba.excel.context.WriteContext; import com.alibaba.excel.context.WriteContextImpl; -import com.alibaba.excel.converters.Converter; -import com.alibaba.excel.converters.ConverterKeyBuild; -import com.alibaba.excel.enums.CellDataTypeEnum; -import com.alibaba.excel.enums.HeadKindEnum; -import com.alibaba.excel.exception.ExcelDataConvertException; +import com.alibaba.excel.enums.WriteTypeEnum; import com.alibaba.excel.exception.ExcelGenerateException; -import com.alibaba.excel.metadata.BaseRowModel; -import com.alibaba.excel.metadata.CellData; -import com.alibaba.excel.metadata.Head; -import com.alibaba.excel.metadata.property.ExcelContentProperty; -import com.alibaba.excel.util.CollectionUtils; import com.alibaba.excel.util.FileUtils; -import com.alibaba.excel.util.WorkBookUtil; -import com.alibaba.excel.write.handler.CellWriteHandler; -import com.alibaba.excel.write.handler.RowWriteHandler; -import com.alibaba.excel.write.handler.WriteHandler; +import com.alibaba.excel.write.executor.ExcelWriteAddExecutor; +import com.alibaba.excel.write.executor.ExcelWriteFillExecutor; import com.alibaba.excel.write.metadata.WriteSheet; import com.alibaba.excel.write.metadata.WriteTable; import com.alibaba.excel.write.metadata.WriteWorkbook; -import com.alibaba.excel.write.metadata.fill.AnalysisCell; -import com.alibaba.excel.write.metadata.holder.WriteHolder; -import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; - -import net.sf.cglib.beans.BeanMap; +import com.alibaba.excel.write.metadata.fill.FillConfig; /** * @author jipengfei */ public class ExcelBuilderImpl implements ExcelBuilder { - private static final String FILL_PREFIX = "${"; - private static final String FILL_SUFFIX = "}"; - private static final Pattern FILL_PATTERN = Pattern.compile("\\$\\{[^}]+}"); - private WriteContext context; + private ExcelWriteFillExecutor excelWriteFillExecutor; + private ExcelWriteAddExecutor excelWriteAddExecutor; public ExcelBuilderImpl(WriteWorkbook writeWorkbook) { try { @@ -73,23 +39,6 @@ public class ExcelBuilderImpl implements ExcelBuilder { } } - private void doAddContent(List data) { - if (CollectionUtils.isEmpty(data)) { - return; - } - WriteSheetHolder writeSheetHolder = context.writeSheetHolder(); - int newRowIndex = writeSheetHolder.getNewRowIndexAndStartDoWrite(); - if (writeSheetHolder.isNew() && !writeSheetHolder.getExcelWriteHeadProperty().hasHead()) { - newRowIndex += context.currentWriteHolder().relativeHeadRowIndex(); - } - // BeanMap is out of order,so use fieldList - List fieldList = new ArrayList(); - for (int relativeRowIndex = 0; relativeRowIndex < data.size(); relativeRowIndex++) { - int n = relativeRowIndex + newRowIndex; - addOneRowOfDataToExcel(data.get(relativeRowIndex), n, relativeRowIndex, fieldList); - } - } - @Override public void addContent(List data, WriteSheet writeSheet) { addContent(data, writeSheet, null); @@ -98,9 +47,15 @@ public class ExcelBuilderImpl implements ExcelBuilder { @Override public void addContent(List data, WriteSheet writeSheet, WriteTable writeTable) { try { - context.currentSheet(writeSheet); + if (data == null) { + return; + } + context.currentSheet(writeSheet, WriteTypeEnum.ADD); context.currentTable(writeTable); - doAddContent(data); + if (excelWriteAddExecutor == null) { + excelWriteAddExecutor = new ExcelWriteAddExecutor(context); + } + excelWriteAddExecutor.add(data); } catch (RuntimeException e) { finish(); throw e; @@ -111,14 +66,19 @@ public class ExcelBuilderImpl implements ExcelBuilder { } @Override - public void fill(Object data, WriteSheet writeSheet) { + public void fill(Object data, FillConfig fillConfig, WriteSheet writeSheet) { try { - if (context.writeWorkbookHolder().getTemplateFile() == null - && context.writeWorkbookHolder().getTemplateInputStream() == null) { + if (data == null) { + return; + } + if (context.writeWorkbookHolder().getTempTemplateInputStream() == null) { throw new ExcelGenerateException("Calling the 'fill' method must use a template."); } - context.currentSheet(writeSheet); - doFill(data); + context.currentSheet(writeSheet, WriteTypeEnum.FILL); + if (excelWriteFillExecutor == null) { + excelWriteFillExecutor = new ExcelWriteFillExecutor(context); + } + excelWriteFillExecutor.fill(data, fillConfig); } catch (RuntimeException e) { finish(); throw e; @@ -128,73 +88,6 @@ public class ExcelBuilderImpl implements ExcelBuilder { } } - private void doFill(Object data) { - WriteSheetHolder writeSheetHolder = context.writeSheetHolder(); - XSSFSheet sheet = writeSheetHolder.getXssfSheet(); - Map templateLastRowMap = context.writeWorkbookHolder().getTemplateLastRowMap(); - if (sheet == null) { - throw new ExcelGenerateException( - "The corresponding table cannot be found,sheetNo:" + writeSheetHolder.getSheetNo()); - } - List analysisCellList = new ArrayList(); - for (int i = 0; i <= sheet.getLastRowNum(); i++) { - Row row = sheet.getRow(i); - for (int j = 0; j < row.getLastCellNum(); j++) { - Cell cell = row.getCell(j); - String value = cell.getStringCellValue(); - if (FILL_PATTERN.matcher(value).matches()) { - AnalysisCell analysisCell = new AnalysisCell(); - analysisCell.setRowIndex(i); - analysisCell.setColumnIndex(j); - List variableList = new ArrayList(); - analysisCell.setVariableList(variableList); - boolean matches = true; - int index = 0; - while (matches) { - Matcher matcher = FILL_PATTERN.matcher(value); - String variable = value.substring(matcher.regionStart() + 2, matcher.regionEnd() - 1); - variableList.add(variable); - value = matcher.replaceFirst("{" + index++ + "}"); - matches = FILL_PATTERN.matcher(value).matches(); - analysisCellList.add(analysisCell); - } - } - } - } - - if (data instanceof Collection) { - - } else if (data instanceof Map) { - - } else { - - } - BeanMap beanMap = BeanMap.create(data); - - for (AnalysisCell analysisCell : analysisCellList) { - Cell cell = sheet.getRow(analysisCell.getRowIndex()).getCell(analysisCell.getColumnIndex()); - if (analysisCell.getVariableList().size() == 1) { - Object value = beanMap.get(analysisCell.getVariableList().get(0)); - if (value == null) { - continue; - } - converterAndSet(writeSheetHolder, value.getClass(), cell, value, null); - } else { - List fileDataStringList = new ArrayList(); - for (String variable : analysisCell.getVariableList()) { - Object value = beanMap.get(variable); - CellData cellData = convert(writeSheetHolder, String.class, cell, value, null); - String fillDataString = cellData.getStringValue(); - if (fillDataString == null) { - fillDataString = ""; - } - fileDataStringList.add(fillDataString); - } - cell.setCellValue(String.format(analysisCell.getPrepareData(), fileDataStringList)); - } - } - } - @Override public void finish() { if (context != null) { @@ -212,286 +105,4 @@ public class ExcelBuilderImpl implements ExcelBuilder { public WriteContext writeContext() { return context; } - - private void addOneRowOfDataToExcel(Object oneRowData, int n, int relativeRowIndex, List fieldList) { - beforeRowCreate(n, relativeRowIndex); - Row row = WorkBookUtil.createRow(context.writeSheetHolder().getSheet(), n); - afterRowCreate(row, relativeRowIndex); - if (oneRowData instanceof List) { - addBasicTypeToExcel((List)oneRowData, row, relativeRowIndex); - } else { - addJavaObjectToExcel(oneRowData, row, relativeRowIndex, fieldList); - } - } - - private void beforeRowCreate(int rowIndex, int relativeRowIndex) { - List handlerList = context.currentWriteHolder().writeHandlerMap().get(RowWriteHandler.class); - if (handlerList == null || handlerList.isEmpty()) { - return; - } - for (WriteHandler writeHandler : handlerList) { - if (writeHandler instanceof RowWriteHandler) { - ((RowWriteHandler)writeHandler).beforeRowCreate(context.writeSheetHolder(), context.writeTableHolder(), - rowIndex, relativeRowIndex, false); - } - } - } - - private void afterRowCreate(Row row, int relativeRowIndex) { - List handlerList = context.currentWriteHolder().writeHandlerMap().get(RowWriteHandler.class); - if (handlerList == null || handlerList.isEmpty()) { - return; - } - for (WriteHandler writeHandler : handlerList) { - if (writeHandler instanceof RowWriteHandler) { - ((RowWriteHandler)writeHandler).afterRowCreate(context.writeSheetHolder(), context.writeTableHolder(), - row, relativeRowIndex, false); - } - } - if (null != context.writeWorkbookHolder().getWriteWorkbook().getWriteHandler()) { - context.writeWorkbookHolder().getWriteWorkbook().getWriteHandler().row(row.getRowNum(), row); - } - } - - private void addBasicTypeToExcel(List oneRowData, Row row, int relativeRowIndex) { - if (CollectionUtils.isEmpty(oneRowData)) { - return; - } - Map headMap = context.currentWriteHolder().excelWriteHeadProperty().getHeadMap(); - int dataIndex = 0; - int cellIndex = 0; - for (Map.Entry entry : headMap.entrySet()) { - if (dataIndex >= oneRowData.size()) { - return; - } - cellIndex = entry.getKey(); - Head head = entry.getValue(); - doAddBasicTypeToExcel(oneRowData, head, row, relativeRowIndex, dataIndex++, cellIndex); - } - // Finish - if (dataIndex >= oneRowData.size()) { - return; - } - if (cellIndex != 0) { - cellIndex++; - } - int size = oneRowData.size() - dataIndex; - for (int i = 0; i < size; i++) { - doAddBasicTypeToExcel(oneRowData, null, row, relativeRowIndex, dataIndex++, cellIndex++); - } - } - - private void doAddBasicTypeToExcel(List oneRowData, Head head, Row row, int relativeRowIndex, int dataIndex, - int cellIndex) { - beforeCellCreate(row, head, relativeRowIndex); - Cell cell = WorkBookUtil.createCell(row, cellIndex); - Object value = oneRowData.get(dataIndex); - CellData cellData = - converterAndSet(context.currentWriteHolder(), value == null ? null : value.getClass(), cell, value, null); - afterCellCreate(head, cellData, cell, relativeRowIndex); - } - - private void addJavaObjectToExcel(Object oneRowData, Row row, int relativeRowIndex, List fieldList) { - WriteHolder currentWriteHolder = context.currentWriteHolder(); - BeanMap beanMap = BeanMap.create(oneRowData); - Set beanMapHandledSet = new HashSet(); - int cellIndex = 0; - // If it's a class it needs to be cast by type - if (HeadKindEnum.CLASS.equals(context.currentWriteHolder().excelWriteHeadProperty().getHeadKind())) { - Map headMap = context.currentWriteHolder().excelWriteHeadProperty().getHeadMap(); - Map contentPropertyMap = - context.currentWriteHolder().excelWriteHeadProperty().getContentPropertyMap(); - for (Map.Entry entry : contentPropertyMap.entrySet()) { - cellIndex = entry.getKey(); - ExcelContentProperty excelContentProperty = entry.getValue(); - String name = excelContentProperty.getField().getName(); - if (!beanMap.containsKey(name)) { - continue; - } - Head head = headMap.get(cellIndex); - beforeCellCreate(row, head, relativeRowIndex); - Cell cell = WorkBookUtil.createCell(row, cellIndex); - Object value = beanMap.get(name); - CellData cellData = converterAndSet(currentWriteHolder, excelContentProperty.getField().getType(), cell, - value, excelContentProperty); - afterCellCreate(head, cellData, cell, relativeRowIndex); - beanMapHandledSet.add(name); - } - } - // Finish - if (beanMapHandledSet.size() == beanMap.size()) { - return; - } - if (cellIndex != 0) { - cellIndex++; - } - Map ignoreMap = context.currentWriteHolder().excelWriteHeadProperty().getIgnoreMap(); - initFieldList(oneRowData.getClass(), fieldList); - for (Field field : fieldList) { - String filedName = field.getName(); - boolean uselessData = !beanMap.containsKey(filedName) || beanMapHandledSet.contains(filedName) - || ignoreMap.containsKey(filedName); - if (uselessData) { - continue; - } - Object value = beanMap.get(filedName); - if (value == null) { - continue; - } - beforeCellCreate(row, null, relativeRowIndex); - Cell cell = WorkBookUtil.createCell(row, cellIndex++); - CellData cellData = converterAndSet(currentWriteHolder, value.getClass(), cell, value, null); - afterCellCreate(null, cellData, cell, relativeRowIndex); - } - } - - private void initFieldList(Class clazz, List fieldList) { - if (!fieldList.isEmpty()) { - return; - } - Class tempClass = clazz; - while (tempClass != null) { - if (tempClass != BaseRowModel.class) { - Collections.addAll(fieldList, tempClass.getDeclaredFields()); - } - tempClass = tempClass.getSuperclass(); - } - } - - private void beforeCellCreate(Row row, Head head, int relativeRowIndex) { - List handlerList = context.currentWriteHolder().writeHandlerMap().get(CellWriteHandler.class); - if (handlerList == null || handlerList.isEmpty()) { - return; - } - for (WriteHandler writeHandler : handlerList) { - if (writeHandler instanceof CellWriteHandler) { - ((CellWriteHandler)writeHandler).beforeCellCreate(context.writeSheetHolder(), - context.writeTableHolder(), row, head, relativeRowIndex, false); - } - } - - } - - private void afterCellCreate(Head head, CellData cellData, Cell cell, int relativeRowIndex) { - List handlerList = context.currentWriteHolder().writeHandlerMap().get(CellWriteHandler.class); - if (handlerList == null || handlerList.isEmpty()) { - return; - } - for (WriteHandler writeHandler : handlerList) { - if (writeHandler instanceof CellWriteHandler) { - ((CellWriteHandler)writeHandler).afterCellCreate(context.writeSheetHolder(), context.writeTableHolder(), - cellData, cell, head, relativeRowIndex, false); - } - } - if (null != context.writeWorkbookHolder().getWriteWorkbook().getWriteHandler()) { - context.writeWorkbookHolder().getWriteWorkbook().getWriteHandler().cell(cell.getRowIndex(), cell); - } - } - - private CellData converterAndSet(WriteHolder currentWriteHolder, Class clazz, Cell cell, Object value, - ExcelContentProperty excelContentProperty) { - if (value == null) { - return new CellData(); - } - if (value instanceof String && currentWriteHolder.globalConfiguration().getAutoTrim()) { - value = ((String)value).trim(); - } - CellData cellData = convert(currentWriteHolder, clazz, cell, value, excelContentProperty); - if (cellData.getFormula() != null && cellData.getFormula()) { - cell.setCellFormula(cellData.getFormulaValue()); - } - switch (cellData.getType()) { - case STRING: - cell.setCellValue(cellData.getStringValue()); - return cellData; - case BOOLEAN: - cell.setCellValue(cellData.getBooleanValue()); - return cellData; - case NUMBER: - cell.setCellValue(cellData.getNumberValue().doubleValue()); - return cellData; - case IMAGE: - setImageValue(cellData, cell); - return cellData; - case EMPTY: - return cellData; - default: - throw new ExcelDataConvertException("Not supported data:" + value + " return type:" + cell.getCellType() - + "at row:" + cell.getRow().getRowNum()); - } - } - - private void setImageValue(CellData cellData, Cell cell) { - Sheet sheet = cell.getSheet(); - int index = sheet.getWorkbook().addPicture(cellData.getImageValue(), HSSFWorkbook.PICTURE_TYPE_PNG); - Drawing drawing = sheet.getDrawingPatriarch(); - if (drawing == null) { - drawing = sheet.createDrawingPatriarch(); - } - CreationHelper helper = sheet.getWorkbook().getCreationHelper(); - ClientAnchor anchor = helper.createClientAnchor(); - anchor.setDx1(0); - anchor.setDx2(0); - anchor.setDy1(0); - anchor.setDy2(0); - anchor.setCol1(cell.getColumnIndex()); - anchor.setCol2(cell.getColumnIndex() + 1); - anchor.setRow1(cell.getRowIndex()); - anchor.setRow2(cell.getRowIndex() + 1); - anchor.setAnchorType(ClientAnchor.AnchorType.DONT_MOVE_AND_RESIZE); - drawing.createPicture(anchor, index); - } - - private CellData convert(WriteHolder currentWriteHolder, Class clazz, Cell cell, Object value, - ExcelContentProperty excelContentProperty) { - // This means that the user has defined the data. - if (value instanceof CellData) { - CellData cellDataValue = (CellData)value; - if (cellDataValue.getType() != null) { - return cellDataValue; - } else { - if (cellDataValue.getData() == null) { - cellDataValue.setType(CellDataTypeEnum.EMPTY); - return cellDataValue; - } - } - CellData cellDataReturn = doConvert(currentWriteHolder, cellDataValue.getData().getClass(), cell, - cellDataValue.getData(), excelContentProperty); - // The formula information is subject to user input - if (cellDataValue.getFormula() != null) { - cellDataReturn.setFormula(cellDataValue.getFormula()); - cellDataReturn.setFormulaValue(cellDataValue.getFormulaValue()); - } - return cellDataReturn; - } - return doConvert(currentWriteHolder, clazz, cell, value, excelContentProperty); - } - - private CellData doConvert(WriteHolder currentWriteHolder, Class clazz, Cell cell, Object value, - ExcelContentProperty excelContentProperty) { - Converter converter = null; - if (excelContentProperty != null) { - converter = excelContentProperty.getConverter(); - } - if (converter == null) { - converter = currentWriteHolder.converterMap().get(ConverterKeyBuild.buildKey(clazz)); - } - if (converter == null) { - throw new ExcelDataConvertException( - "Can not find 'Converter' support class " + clazz.getSimpleName() + "."); - } - CellData cellData; - try { - cellData = - converter.convertToExcelData(value, excelContentProperty, currentWriteHolder.globalConfiguration()); - } catch (Exception e) { - throw new ExcelDataConvertException("Convert data:" + value + " error,at row:" + cell.getRow().getRowNum(), - e); - } - if (cellData == null || cellData.getType() == null) { - throw new ExcelDataConvertException( - "Convert data:" + value + " return null,at row:" + cell.getRow().getRowNum()); - } - return cellData; - } } diff --git a/src/main/java/com/alibaba/excel/write/builder/ExcelWriterSheetBuilder.java b/src/main/java/com/alibaba/excel/write/builder/ExcelWriterSheetBuilder.java index 29e82773..8a9e0c66 100644 --- a/src/main/java/com/alibaba/excel/write/builder/ExcelWriterSheetBuilder.java +++ b/src/main/java/com/alibaba/excel/write/builder/ExcelWriterSheetBuilder.java @@ -8,6 +8,7 @@ import com.alibaba.excel.converters.Converter; 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.fill.FillConfig; /** * Build sheet @@ -136,10 +137,14 @@ public class ExcelWriterSheetBuilder { } public void doFill(Object data) { + doFill(data, null); + } + + public void doFill(Object data, FillConfig fillConfig) { if (excelWriter == null) { throw new ExcelGenerateException("Must use 'EasyExcelFactory.write().sheet()' to call this method"); } - excelWriter.fill(data, build()); + excelWriter.fill(data, fillConfig, build()); excelWriter.finish(); } diff --git a/src/main/java/com/alibaba/excel/write/executor/AbstractExcelWriteExecutor.java b/src/main/java/com/alibaba/excel/write/executor/AbstractExcelWriteExecutor.java new file mode 100644 index 00000000..9adcb625 --- /dev/null +++ b/src/main/java/com/alibaba/excel/write/executor/AbstractExcelWriteExecutor.java @@ -0,0 +1,140 @@ +package com.alibaba.excel.write.executor; + +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.ClientAnchor; +import org.apache.poi.ss.usermodel.CreationHelper; +import org.apache.poi.ss.usermodel.Drawing; +import org.apache.poi.ss.usermodel.Sheet; + +import com.alibaba.excel.context.WriteContext; +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.converters.ConverterKeyBuild; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.exception.ExcelDataConvertException; +import com.alibaba.excel.metadata.CellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.write.metadata.holder.WriteHolder; + +/** + * Excel write Executor + * + * @author Jiaju Zhuang + */ +public abstract class AbstractExcelWriteExecutor implements ExcelWriteExecutor { + protected WriteContext writeContext; + + public AbstractExcelWriteExecutor(WriteContext writeContext) { + this.writeContext = writeContext; + } + + protected CellData converterAndSet(WriteHolder currentWriteHolder, Class clazz, Cell cell, Object value, + ExcelContentProperty excelContentProperty) { + if (value == null) { + return new CellData(); + } + if (value instanceof String && currentWriteHolder.globalConfiguration().getAutoTrim()) { + value = ((String)value).trim(); + } + CellData cellData = convert(currentWriteHolder, clazz, cell, value, excelContentProperty); + if (cellData.getFormula() != null && cellData.getFormula()) { + cell.setCellFormula(cellData.getFormulaValue()); + } + switch (cellData.getType()) { + case STRING: + cell.setCellValue(cellData.getStringValue()); + return cellData; + case BOOLEAN: + cell.setCellValue(cellData.getBooleanValue()); + return cellData; + case NUMBER: + cell.setCellValue(cellData.getNumberValue().doubleValue()); + return cellData; + case IMAGE: + setImageValue(cellData, cell); + return cellData; + case EMPTY: + return cellData; + default: + throw new ExcelDataConvertException("Not supported data:" + value + " return type:" + cell.getCellType() + + "at row:" + cell.getRow().getRowNum()); + } + } + + protected CellData convert(WriteHolder currentWriteHolder, Class clazz, Cell cell, Object value, + ExcelContentProperty excelContentProperty) { + if (value == null) { + return new CellData(); + } + // This means that the user has defined the data. + if (value instanceof CellData) { + CellData cellDataValue = (CellData)value; + if (cellDataValue.getType() != null) { + return cellDataValue; + } else { + if (cellDataValue.getData() == null) { + cellDataValue.setType(CellDataTypeEnum.EMPTY); + return cellDataValue; + } + } + CellData cellDataReturn = doConvert(currentWriteHolder, cellDataValue.getData().getClass(), cell, + cellDataValue.getData(), excelContentProperty); + // The formula information is subject to user input + if (cellDataValue.getFormula() != null) { + cellDataReturn.setFormula(cellDataValue.getFormula()); + cellDataReturn.setFormulaValue(cellDataValue.getFormulaValue()); + } + return cellDataReturn; + } + return doConvert(currentWriteHolder, clazz, cell, value, excelContentProperty); + } + + private CellData doConvert(WriteHolder currentWriteHolder, Class clazz, Cell cell, Object value, + ExcelContentProperty excelContentProperty) { + Converter converter = null; + if (excelContentProperty != null) { + converter = excelContentProperty.getConverter(); + } + if (converter == null) { + converter = currentWriteHolder.converterMap().get(ConverterKeyBuild.buildKey(clazz)); + } + if (converter == null) { + throw new ExcelDataConvertException( + "Can not find 'Converter' support class " + clazz.getSimpleName() + "."); + } + CellData cellData; + try { + cellData = + converter.convertToExcelData(value, excelContentProperty, currentWriteHolder.globalConfiguration()); + } catch (Exception e) { + throw new ExcelDataConvertException("Convert data:" + value + " error,at row:" + cell.getRow().getRowNum(), + e); + } + if (cellData == null || cellData.getType() == null) { + throw new ExcelDataConvertException( + "Convert data:" + value + " return null,at row:" + cell.getRow().getRowNum()); + } + return cellData; + } + + private void setImageValue(CellData cellData, Cell cell) { + Sheet sheet = cell.getSheet(); + int index = sheet.getWorkbook().addPicture(cellData.getImageValue(), HSSFWorkbook.PICTURE_TYPE_PNG); + Drawing drawing = sheet.getDrawingPatriarch(); + if (drawing == null) { + drawing = sheet.createDrawingPatriarch(); + } + CreationHelper helper = sheet.getWorkbook().getCreationHelper(); + ClientAnchor anchor = helper.createClientAnchor(); + anchor.setDx1(0); + anchor.setDx2(0); + anchor.setDy1(0); + anchor.setDy2(0); + anchor.setCol1(cell.getColumnIndex()); + anchor.setCol2(cell.getColumnIndex() + 1); + anchor.setRow1(cell.getRowIndex()); + anchor.setRow2(cell.getRowIndex() + 1); + anchor.setAnchorType(ClientAnchor.AnchorType.DONT_MOVE_AND_RESIZE); + drawing.createPicture(anchor, index); + } +} diff --git a/src/main/java/com/alibaba/excel/write/executor/ExcelWriteAddExecutor.java b/src/main/java/com/alibaba/excel/write/executor/ExcelWriteAddExecutor.java new file mode 100644 index 00000000..8843e2cc --- /dev/null +++ b/src/main/java/com/alibaba/excel/write/executor/ExcelWriteAddExecutor.java @@ -0,0 +1,179 @@ +package com.alibaba.excel.write.executor; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Row; + +import com.alibaba.excel.context.WriteContext; +import com.alibaba.excel.enums.HeadKindEnum; +import com.alibaba.excel.metadata.BaseRowModel; +import com.alibaba.excel.metadata.CellData; +import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.util.CollectionUtils; +import com.alibaba.excel.util.WorkBookUtil; +import com.alibaba.excel.util.WriteHandlerUtils; +import com.alibaba.excel.write.metadata.holder.WriteHolder; +import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; + +import net.sf.cglib.beans.BeanMap; + +/** + * Add the data into excel + * + * @author Jiaju Zhuang + */ +public class ExcelWriteAddExecutor extends AbstractExcelWriteExecutor { + + public ExcelWriteAddExecutor(WriteContext writeContext) { + super(writeContext); + } + + public void add(List data) { + if (CollectionUtils.isEmpty(data)) { + return; + } + WriteSheetHolder writeSheetHolder = writeContext.writeSheetHolder(); + int newRowIndex = writeSheetHolder.getNewRowIndexAndStartDoWrite(); + if (writeSheetHolder.isNew() && !writeSheetHolder.getExcelWriteHeadProperty().hasHead()) { + newRowIndex += writeContext.currentWriteHolder().relativeHeadRowIndex(); + } + // BeanMap is out of order,so use fieldList + List fieldList = new ArrayList(); + for (int relativeRowIndex = 0; relativeRowIndex < data.size(); relativeRowIndex++) { + int n = relativeRowIndex + newRowIndex; + addOneRowOfDataToExcel(data.get(relativeRowIndex), n, relativeRowIndex, fieldList); + } + } + + private void addOneRowOfDataToExcel(Object oneRowData, int n, int relativeRowIndex, List fieldList) { + if (oneRowData == null) { + return; + } + WriteHandlerUtils.beforeRowCreate(writeContext, n, relativeRowIndex, Boolean.FALSE); + Row row = WorkBookUtil.createRow(writeContext.writeSheetHolder().getSheet(), n); + WriteHandlerUtils.afterRowCreate(writeContext, row, relativeRowIndex, Boolean.FALSE); + if (oneRowData instanceof List) { + addBasicTypeToExcel((List)oneRowData, row, relativeRowIndex); + } else { + addJavaObjectToExcel(oneRowData, row, relativeRowIndex, fieldList); + } + WriteHandlerUtils.afterRowDispose(writeContext, row, relativeRowIndex, Boolean.FALSE); + } + + private void addBasicTypeToExcel(List oneRowData, Row row, int relativeRowIndex) { + if (CollectionUtils.isEmpty(oneRowData)) { + return; + } + Map headMap = writeContext.currentWriteHolder().excelWriteHeadProperty().getHeadMap(); + int dataIndex = 0; + int cellIndex = 0; + for (Map.Entry entry : headMap.entrySet()) { + if (dataIndex >= oneRowData.size()) { + return; + } + cellIndex = entry.getKey(); + Head head = entry.getValue(); + doAddBasicTypeToExcel(oneRowData, head, row, relativeRowIndex, dataIndex++, cellIndex); + } + // Finish + if (dataIndex >= oneRowData.size()) { + return; + } + if (cellIndex != 0) { + cellIndex++; + } + int size = oneRowData.size() - dataIndex; + for (int i = 0; i < size; i++) { + doAddBasicTypeToExcel(oneRowData, null, row, relativeRowIndex, dataIndex++, cellIndex++); + } + } + + private void doAddBasicTypeToExcel(List oneRowData, Head head, Row row, int relativeRowIndex, int dataIndex, + int cellIndex) { + WriteHandlerUtils.beforeCellCreate(writeContext, row, head, cellIndex, relativeRowIndex, Boolean.FALSE); + Cell cell = WorkBookUtil.createCell(row, cellIndex); + WriteHandlerUtils.afterCellCreate(writeContext, cell, head, relativeRowIndex, Boolean.FALSE); + Object value = oneRowData.get(dataIndex); + CellData cellData = converterAndSet(writeContext.currentWriteHolder(), value == null ? null : value.getClass(), + cell, value, null); + WriteHandlerUtils.afterCellDispose(writeContext, cellData, cell, head, relativeRowIndex, Boolean.FALSE); + } + + private void addJavaObjectToExcel(Object oneRowData, Row row, int relativeRowIndex, List fieldList) { + WriteHolder currentWriteHolder = writeContext.currentWriteHolder(); + BeanMap beanMap = BeanMap.create(oneRowData); + Set beanMapHandledSet = new HashSet(); + int cellIndex = 0; + // If it's a class it needs to be cast by type + if (HeadKindEnum.CLASS.equals(writeContext.currentWriteHolder().excelWriteHeadProperty().getHeadKind())) { + Map headMap = writeContext.currentWriteHolder().excelWriteHeadProperty().getHeadMap(); + Map contentPropertyMap = + writeContext.currentWriteHolder().excelWriteHeadProperty().getContentPropertyMap(); + for (Map.Entry entry : contentPropertyMap.entrySet()) { + cellIndex = entry.getKey(); + ExcelContentProperty excelContentProperty = entry.getValue(); + String name = excelContentProperty.getField().getName(); + if (!beanMap.containsKey(name)) { + continue; + } + Head head = headMap.get(cellIndex); + WriteHandlerUtils.beforeCellCreate(writeContext, row, head, cellIndex, relativeRowIndex, Boolean.FALSE); + Cell cell = WorkBookUtil.createCell(row, cellIndex); + WriteHandlerUtils.afterCellCreate(writeContext, cell, head, relativeRowIndex, Boolean.FALSE); + Object value = beanMap.get(name); + CellData cellData = converterAndSet(currentWriteHolder, excelContentProperty.getField().getType(), cell, + value, excelContentProperty); + WriteHandlerUtils.afterCellDispose(writeContext, cellData, cell, head, relativeRowIndex, Boolean.FALSE); + beanMapHandledSet.add(name); + } + } + // Finish + if (beanMapHandledSet.size() == beanMap.size()) { + return; + } + if (cellIndex != 0) { + cellIndex++; + } + Map ignoreMap = writeContext.currentWriteHolder().excelWriteHeadProperty().getIgnoreMap(); + initFieldList(oneRowData.getClass(), fieldList); + for (Field field : fieldList) { + String filedName = field.getName(); + boolean uselessData = !beanMap.containsKey(filedName) || beanMapHandledSet.contains(filedName) + || ignoreMap.containsKey(filedName); + if (uselessData) { + continue; + } + Object value = beanMap.get(filedName); + if (value == null) { + continue; + } + WriteHandlerUtils.beforeCellCreate(writeContext, row, null, cellIndex, relativeRowIndex, Boolean.FALSE); + Cell cell = WorkBookUtil.createCell(row, cellIndex++); + WriteHandlerUtils.afterCellCreate(writeContext, cell, null, relativeRowIndex, Boolean.FALSE); + CellData cellData = converterAndSet(currentWriteHolder, value.getClass(), cell, value, null); + WriteHandlerUtils.afterCellDispose(writeContext, cellData, cell, null, relativeRowIndex, Boolean.FALSE); + } + } + + private void initFieldList(Class clazz, List fieldList) { + if (!fieldList.isEmpty()) { + return; + } + Class tempClass = clazz; + while (tempClass != null) { + if (tempClass != BaseRowModel.class) { + Collections.addAll(fieldList, tempClass.getDeclaredFields()); + } + tempClass = tempClass.getSuperclass(); + } + } + +} diff --git a/src/main/java/com/alibaba/excel/write/executor/ExcelWriteExecutor.java b/src/main/java/com/alibaba/excel/write/executor/ExcelWriteExecutor.java new file mode 100644 index 00000000..b7cb607c --- /dev/null +++ b/src/main/java/com/alibaba/excel/write/executor/ExcelWriteExecutor.java @@ -0,0 +1,9 @@ +package com.alibaba.excel.write.executor; + +/** + * Excel write Executor + * + * @author Jiaju Zhuang + */ +public interface ExcelWriteExecutor { +} diff --git a/src/main/java/com/alibaba/excel/write/executor/ExcelWriteFillExecutor.java b/src/main/java/com/alibaba/excel/write/executor/ExcelWriteFillExecutor.java new file mode 100644 index 00000000..217f21e5 --- /dev/null +++ b/src/main/java/com/alibaba/excel/write/executor/ExcelWriteFillExecutor.java @@ -0,0 +1,377 @@ +package com.alibaba.excel.write.executor; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; + +import com.alibaba.excel.context.WriteContext; +import com.alibaba.excel.enums.WriteDirectionEnum; +import com.alibaba.excel.enums.WriteTemplateAnalysisCellTypeEnum; +import com.alibaba.excel.exception.ExcelGenerateException; +import com.alibaba.excel.metadata.CellData; +import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.util.CollectionUtils; +import com.alibaba.excel.util.StringUtils; +import com.alibaba.excel.util.WriteHandlerUtils; +import com.alibaba.excel.write.metadata.fill.AnalysisCell; +import com.alibaba.excel.write.metadata.fill.FillConfig; +import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; + +import net.sf.cglib.beans.BeanMap; + +/** + * Fill the data into excel + * + * @author Jiaju Zhuang + */ +public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { + + private static final String ESCAPE_FILL_PREFIX = "\\\\\\{"; + private static final String ESCAPE_FILL_SUFFIX = "\\\\\\}"; + private static final String FILL_PREFIX = "{"; + private static final String FILL_SUFFIX = "}"; + private static final char IGNORE_CHAR = '\\'; + private static final String COLLECTION_PREFIX = "."; + /** + * Fields to replace in the template + */ + private Map> templateAnalysisCache = new HashMap>(8); + /** + * Collection fields to replace in the template + */ + private Map> templateCollectionAnalysisCache = + new HashMap>(8); + /** + * Style cache for collection fields + */ + private Map> collectionFieldStyleCache = + new HashMap>(8); + /** + * Last index cache for collection fields + */ + private Map> collectionLastIndexCache = + new HashMap>(8); + + public ExcelWriteFillExecutor(WriteContext writeContext) { + super(writeContext); + } + + public void fill(Object data, FillConfig fillConfig) { + if (fillConfig == null) { + fillConfig = FillConfig.builder().build(true); + } + fillConfig.init(); + if (data instanceof Collection) { + List analysisCellList = readTemplateData(templateCollectionAnalysisCache); + Collection collectionData = (Collection)data; + if (CollectionUtils.isEmpty(collectionData)) { + return; + } + Iterator iterator = collectionData.iterator(); + if (WriteDirectionEnum.VERTICAL.equals(fillConfig.getDirection()) && fillConfig.getForceNewRow()) { + shiftRows(collectionData.size(), analysisCellList); + } + while (iterator.hasNext()) { + doFill(analysisCellList, iterator.next(), fillConfig); + } + } else { + doFill(readTemplateData(templateAnalysisCache), data, fillConfig); + } + } + + private void shiftRows(int size, List analysisCellList) { + if (CollectionUtils.isEmpty(analysisCellList)) { + return; + } + int maxRowIndex = 0; + Integer sheetNo = writeContext.writeSheetHolder().getSheetNo(); + Map collectionLastIndexMap = collectionLastIndexCache.get(sheetNo); + for (AnalysisCell analysisCell : analysisCellList) { + if (collectionLastIndexMap != null) { + Integer lastRowIndex = collectionLastIndexMap.get(analysisCell); + if (lastRowIndex != null) { + if (lastRowIndex > maxRowIndex) { + maxRowIndex = lastRowIndex; + } + continue; + } + } + if (analysisCell.getRowIndex() > maxRowIndex) { + maxRowIndex = analysisCell.getRowIndex(); + } + } + Sheet cachedSheet = writeContext.writeSheetHolder().getCachedSheet(); + int lastRowIndex = cachedSheet.getLastRowNum(); + if (maxRowIndex >= lastRowIndex) { + return; + } + Sheet sheet = writeContext.writeSheetHolder().getCachedSheet(); + int number = size; + if (collectionLastIndexMap == null) { + number--; + } + sheet.shiftRows(maxRowIndex + 1, lastRowIndex, number); + for (AnalysisCell analysisCell : templateAnalysisCache.get(writeContext.writeSheetHolder().getSheetNo())) { + if (analysisCell.getRowIndex() > maxRowIndex) { + analysisCell.setRowIndex(analysisCell.getRowIndex() + number); + } + } + } + + private void doFill(List analysisCellList, Object oneRowData, FillConfig fillConfig) { + Map dataMap; + if (oneRowData instanceof Map) { + dataMap = (Map)oneRowData; + } else { + dataMap = BeanMap.create(oneRowData); + } + WriteSheetHolder writeSheetHolder = writeContext.writeSheetHolder(); + Map fieldNameContentPropertyMap = + writeContext.currentWriteHolder().excelWriteHeadProperty().getFieldNameContentPropertyMap(); + for (AnalysisCell analysisCell : analysisCellList) { + Cell cell = getOneCell(analysisCell, fillConfig); + if (analysisCell.getOnlyOneVariable()) { + String variable = analysisCell.getVariableList().get(0); + if (!dataMap.containsKey(variable)) { + continue; + } + Object value = dataMap.get(variable); + CellData cellData = converterAndSet(writeSheetHolder, value == null ? null : value.getClass(), cell, + value, fieldNameContentPropertyMap.get(variable)); + WriteHandlerUtils.afterCellDispose(writeContext, cellData, cell, null, null, Boolean.FALSE); + } else { + StringBuilder cellValueBuild = new StringBuilder(); + int index = 0; + List cellDataList = new ArrayList(); + for (String variable : analysisCell.getVariableList()) { + cellValueBuild.append(analysisCell.getPrepareDataList().get(index++)); + if (!dataMap.containsKey(variable)) { + continue; + } + Object value = dataMap.get(variable); + CellData cellData = convert(writeSheetHolder, value == null ? null : value.getClass(), cell, value, + fieldNameContentPropertyMap.get(variable)); + cellDataList.add(cellData); + switch (cellData.getType()) { + case STRING: + cellValueBuild.append(cellData.getStringValue()); + break; + case BOOLEAN: + cellValueBuild.append(cellData.getBooleanValue()); + break; + case NUMBER: + cellValueBuild.append(cellData.getNumberValue()); + break; + default: + break; + } + } + cellValueBuild.append(analysisCell.getPrepareDataList().get(index)); + cell.setCellValue(cellValueBuild.toString()); + WriteHandlerUtils.afterCellDispose(writeContext, cellDataList, cell, null, null, Boolean.FALSE); + } + } + } + + private Cell getOneCell(AnalysisCell analysisCell, FillConfig fillConfig) { + Sheet cachedSheet = writeContext.writeSheetHolder().getCachedSheet(); + if (WriteTemplateAnalysisCellTypeEnum.COMMON.equals(analysisCell.getCellType())) { + return cachedSheet.getRow(analysisCell.getRowIndex()).getCell(analysisCell.getColumnIndex()); + } + Integer sheetNo = writeContext.writeSheetHolder().getSheetNo(); + Sheet sheet = writeContext.writeSheetHolder().getSheet(); + + Map collectionLastIndexMap = collectionLastIndexCache.get(sheetNo); + if (collectionLastIndexMap == null) { + collectionLastIndexMap = new HashMap(16); + collectionLastIndexCache.put(sheetNo, collectionLastIndexMap); + } + boolean isOriginalCell = false; + Integer lastRowIndex; + Integer lastColumnIndex; + switch (fillConfig.getDirection()) { + case VERTICAL: + lastRowIndex = collectionLastIndexMap.get(analysisCell); + if (lastRowIndex == null) { + lastRowIndex = analysisCell.getRowIndex(); + collectionLastIndexMap.put(analysisCell, lastRowIndex); + isOriginalCell = true; + } else { + collectionLastIndexMap.put(analysisCell, ++lastRowIndex); + } + lastColumnIndex = analysisCell.getColumnIndex(); + break; + case HORIZONTAL: + lastRowIndex = analysisCell.getRowIndex(); + lastColumnIndex = collectionLastIndexMap.get(analysisCell); + if (lastColumnIndex == null) { + lastColumnIndex = analysisCell.getColumnIndex(); + collectionLastIndexMap.put(analysisCell, lastColumnIndex); + isOriginalCell = true; + } else { + collectionLastIndexMap.put(analysisCell, ++lastColumnIndex); + } + break; + default: + throw new ExcelGenerateException("The wrong direction."); + } + Row row = sheet.getRow(lastRowIndex); + if (row == null) { + row = cachedSheet.getRow(lastRowIndex); + if (row == null) { + WriteHandlerUtils.beforeRowCreate(writeContext, lastRowIndex, null, Boolean.FALSE); + if (fillConfig.getForceNewRow()) { + row = cachedSheet.createRow(lastRowIndex); + } else { + row = sheet.createRow(lastRowIndex); + } + WriteHandlerUtils.afterRowCreate(writeContext, row, null, Boolean.FALSE); + } + } + Cell cell = row.getCell(lastColumnIndex); + if (cell == null) { + WriteHandlerUtils.beforeCellCreate(writeContext, row, null, lastColumnIndex, null, Boolean.FALSE); + cell = row.createCell(lastColumnIndex); + WriteHandlerUtils.afterCellCreate(writeContext, cell, null, null, Boolean.FALSE); + } + + Map collectionFieldStyleMap = collectionFieldStyleCache.get(sheetNo); + if (collectionFieldStyleMap == null) { + collectionFieldStyleMap = new HashMap(16); + collectionFieldStyleCache.put(sheetNo, collectionFieldStyleMap); + } + if (isOriginalCell) { + collectionFieldStyleMap.put(analysisCell, cell.getCellStyle()); + } else { + CellStyle cellStyle = collectionFieldStyleMap.get(analysisCell); + if (cellStyle != null) { + cell.setCellStyle(cellStyle); + } + } + return cell; + } + + private List readTemplateData(Map> analysisCache) { + Integer sheetNo = writeContext.writeSheetHolder().getSheetNo(); + List analysisCellList = analysisCache.get(sheetNo); + if (analysisCellList != null) { + return analysisCellList; + } + Sheet sheet = writeContext.writeSheetHolder().getCachedSheet(); + analysisCellList = new ArrayList(); + List collectionAnalysisCellList = new ArrayList(); + for (int i = 0; i <= sheet.getLastRowNum(); i++) { + Row row = sheet.getRow(i); + if (row == null) { + continue; + } + for (int j = 0; j < row.getLastCellNum(); j++) { + Cell cell = row.getCell(j); + if (cell == null) { + continue; + } + prepareData(cell.getStringCellValue(), analysisCellList, collectionAnalysisCellList, i, j); + } + } + templateAnalysisCache.put(sheetNo, analysisCellList); + templateCollectionAnalysisCache.put(sheetNo, collectionAnalysisCellList); + return analysisCache.get(sheetNo); + } + + private void prepareData(String value, List analysisCellList, + List collectionAnalysisCellList, int rowIndex, int columnIndex) { + if (StringUtils.isEmpty(value)) { + return; + } + AnalysisCell analysisCell = null; + int startIndex = 0; + int length = value.length(); + int lastPrepareDataIndex = 0; + out: + while (startIndex < length) { + int prefixIndex = value.indexOf(FILL_PREFIX, startIndex); + if (prefixIndex < 0) { + break out; + } + if (prefixIndex != 0) { + char prefixPrefixChar = value.charAt(prefixIndex - 1); + if (prefixPrefixChar == IGNORE_CHAR) { + startIndex = prefixIndex + 1; + continue; + } + } + int suffixIndex = -1; + while (suffixIndex == -1 && startIndex < length) { + suffixIndex = value.indexOf(FILL_SUFFIX, startIndex + 1); + if (suffixIndex < 0) { + break out; + } + startIndex = suffixIndex + 1; + char prefixSuffixChar = value.charAt(suffixIndex - 1); + if (prefixSuffixChar == IGNORE_CHAR) { + suffixIndex = -1; + } + } + if (analysisCell == null) { + analysisCell = new AnalysisCell(); + analysisCell.setRowIndex(rowIndex); + analysisCell.setColumnIndex(columnIndex); + analysisCell.setOnlyOneVariable(Boolean.TRUE); + List variableList = new ArrayList(); + analysisCell.setVariableList(variableList); + List prepareDataList = new ArrayList(); + analysisCell.setPrepareDataList(prepareDataList); + analysisCell.setCellType(WriteTemplateAnalysisCellTypeEnum.COMMON); + } + String variable = value.substring(prefixIndex + 1, suffixIndex); + if (StringUtils.isEmpty(variable)) { + continue; + } + if (variable.startsWith(COLLECTION_PREFIX)) { + variable = variable.substring(1); + if (StringUtils.isEmpty(variable)) { + continue; + } + analysisCell.setCellType(WriteTemplateAnalysisCellTypeEnum.COLLECTION); + } + analysisCell.getVariableList().add(variable); + if (lastPrepareDataIndex == prefixIndex) { + analysisCell.getPrepareDataList().add(StringUtils.EMPTY); + } else { + analysisCell.getPrepareDataList() + .add(convertPrepareData(value.substring(lastPrepareDataIndex, prefixIndex))); + analysisCell.setOnlyOneVariable(Boolean.FALSE); + } + lastPrepareDataIndex = suffixIndex + 1; + } + if (analysisCell != null) { + if (lastPrepareDataIndex == length) { + analysisCell.getPrepareDataList().add(StringUtils.EMPTY); + } else { + analysisCell.getPrepareDataList().add(convertPrepareData(value.substring(lastPrepareDataIndex))); + analysisCell.setOnlyOneVariable(Boolean.FALSE); + } + if (WriteTemplateAnalysisCellTypeEnum.COMMON.equals(analysisCell.getCellType())) { + analysisCellList.add(analysisCell); + } else { + collectionAnalysisCellList.add(analysisCell); + } + } + } + + private String convertPrepareData(String prepareData) { + prepareData = prepareData.replaceAll(ESCAPE_FILL_PREFIX, FILL_PREFIX); + prepareData = prepareData.replaceAll(ESCAPE_FILL_SUFFIX, FILL_SUFFIX); + return prepareData; + } + +} diff --git a/src/main/java/com/alibaba/excel/write/handler/CellWriteHandler.java b/src/main/java/com/alibaba/excel/write/handler/CellWriteHandler.java index 9e8af22b..4d981380 100644 --- a/src/main/java/com/alibaba/excel/write/handler/CellWriteHandler.java +++ b/src/main/java/com/alibaba/excel/write/handler/CellWriteHandler.java @@ -1,5 +1,7 @@ package com.alibaba.excel.write.handler; +import java.util.List; + import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; @@ -16,32 +18,56 @@ import com.alibaba.excel.write.metadata.holder.WriteTableHolder; public interface CellWriteHandler extends WriteHandler { /** - * called before create the cell + * Called before create the cell * * @param writeSheetHolder * @param writeTableHolder - * Nullable + * Nullable.It is null without using table writes. * @param row * @param head + * Nullable.It is null in the case of fill data and without head. + * @param columnIndex * @param relativeRowIndex + * Nullable.It is null in the case of fill data. * @param isHead + * It will always be false when fill data. */ void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, - int relativeRowIndex, boolean isHead); + Integer columnIndex, Integer relativeRowIndex, Boolean isHead); + + /** + * Called after the cell is created + * + * @param writeSheetHolder + * @param writeTableHolder + * Nullable.It is null without using table writes. + * @param cell + * @param head + * Nullable.It is null in the case of fill data and without head. + * @param relativeRowIndex + * Nullable.It is null in the case of fill data. + * @param isHead + * It will always be false when fill data. + */ + void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, + Integer relativeRowIndex, Boolean isHead); /** - * called after the cell is created + * Called after all operations on the cell have been completed * * @param writeSheetHolder * @param writeTableHolder - * Nullable + * Nullable.It is null without using table writes. * @param cell * @param head - * @param cellData - * Nullable. + * Nullable.It is null in the case of fill data and without head. + * @param cellDataList + * Nullable.It is null in the case of add header.There may be several when fill the data. * @param relativeRowIndex + * Nullable.It is null in the case of fill data. * @param isHead + * It will always be false when fill data. */ - void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, - Cell cell, Head head, int relativeRowIndex, boolean isHead); + void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, + List cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead); } diff --git a/src/main/java/com/alibaba/excel/write/handler/RowWriteHandler.java b/src/main/java/com/alibaba/excel/write/handler/RowWriteHandler.java index 1630bd31..29ac10b0 100644 --- a/src/main/java/com/alibaba/excel/write/handler/RowWriteHandler.java +++ b/src/main/java/com/alibaba/excel/write/handler/RowWriteHandler.java @@ -13,28 +13,47 @@ import com.alibaba.excel.write.metadata.holder.WriteTableHolder; public interface RowWriteHandler extends WriteHandler { /** - * called before create the row + * Called before create the row * * @param writeSheetHolder * @param writeTableHolder - * Nullable + * Nullable.It is null without using table writes. * @param rowIndex * @param relativeRowIndex + * Nullable.It is null in the case of fill data. * @param isHead + * Nullable.It is null in the case of fill data. */ - void beforeRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, int rowIndex, - int relativeRowIndex, boolean isHead); + void beforeRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Integer rowIndex, + Integer relativeRowIndex, Boolean isHead); /** - * called after the row is created + * Called after the row is created * * @param writeSheetHolder * @param writeTableHolder - * Nullable + * Nullable.It is null without using table writes. * @param row * @param relativeRowIndex + * Nullable.It is null in the case of fill data. * @param isHead + * Nullable.It is null in the case of fill data. */ void afterRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, - int relativeRowIndex, boolean isHead); + Integer relativeRowIndex, Boolean isHead); + + /** + * Called after all operations on the row have been completed.This method is not called when fill the data. + * + * @param writeSheetHolder + * @param writeTableHolder + * Nullable.It is null without using table writes. + * @param row + * @param relativeRowIndex + * Nullable.It is null in the case of fill data. + * @param isHead + * Nullable.It is null in the case of fill data. + */ + void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, + Integer relativeRowIndex, Boolean isHead); } diff --git a/src/main/java/com/alibaba/excel/write/handler/SheetWriteHandler.java b/src/main/java/com/alibaba/excel/write/handler/SheetWriteHandler.java index 080abbf2..b97e1d15 100644 --- a/src/main/java/com/alibaba/excel/write/handler/SheetWriteHandler.java +++ b/src/main/java/com/alibaba/excel/write/handler/SheetWriteHandler.java @@ -11,7 +11,7 @@ import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder; public interface SheetWriteHandler extends WriteHandler { /** - * called before create the sheet + * Called before create the sheet * * @param writeWorkbookHolder * @param writeSheetHolder @@ -19,7 +19,7 @@ public interface SheetWriteHandler extends WriteHandler { void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder); /** - * called after the sheet is created + * Called after the sheet is created * * @param writeWorkbookHolder * @param writeSheetHolder diff --git a/src/main/java/com/alibaba/excel/write/handler/WorkbookWriteHandler.java b/src/main/java/com/alibaba/excel/write/handler/WorkbookWriteHandler.java index 508c9c57..8dba9646 100644 --- a/src/main/java/com/alibaba/excel/write/handler/WorkbookWriteHandler.java +++ b/src/main/java/com/alibaba/excel/write/handler/WorkbookWriteHandler.java @@ -10,14 +10,21 @@ import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder; public interface WorkbookWriteHandler extends WriteHandler { /** - * called before create the sheet + * Called before create the workbook */ void beforeWorkbookCreate(); /** - * called after the sheet is created + * Called after the workbook is created * * @param writeWorkbookHolder */ void afterWorkbookCreate(WriteWorkbookHolder writeWorkbookHolder); + + /** + * Called after all operations on the workbook have been completed + * + * @param writeWorkbookHolder + */ + void afterWorkbookDispose(WriteWorkbookHolder writeWorkbookHolder); } diff --git a/src/main/java/com/alibaba/excel/write/merge/AbstractMergeStrategy.java b/src/main/java/com/alibaba/excel/write/merge/AbstractMergeStrategy.java index 7b95f1c5..13bdac3f 100644 --- a/src/main/java/com/alibaba/excel/write/merge/AbstractMergeStrategy.java +++ b/src/main/java/com/alibaba/excel/write/merge/AbstractMergeStrategy.java @@ -1,5 +1,7 @@ package com.alibaba.excel.write.merge; +import java.util.List; + import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; @@ -18,13 +20,17 @@ import com.alibaba.excel.write.metadata.holder.WriteTableHolder; public abstract class AbstractMergeStrategy implements CellWriteHandler { @Override public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, - Head head, int relativeRowIndex, boolean isHead) { + Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) { } @Override - public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, - Cell cell, Head head, int relativeRowIndex, boolean isHead) { + public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, + Head head, Integer relativeRowIndex, Boolean isHead) {} + + @Override + public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, + List cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { if (isHead) { return; } diff --git a/src/main/java/com/alibaba/excel/write/metadata/fill/AnalysisCell.java b/src/main/java/com/alibaba/excel/write/metadata/fill/AnalysisCell.java index 4613891c..791e2643 100644 --- a/src/main/java/com/alibaba/excel/write/metadata/fill/AnalysisCell.java +++ b/src/main/java/com/alibaba/excel/write/metadata/fill/AnalysisCell.java @@ -2,6 +2,8 @@ package com.alibaba.excel.write.metadata.fill; import java.util.List; +import com.alibaba.excel.enums.WriteTemplateAnalysisCellTypeEnum; + /** * Read the cells of the template while populating the data. * @@ -11,7 +13,9 @@ public class AnalysisCell { private int columnIndex; private int rowIndex; private List variableList; - private String prepareData; + private List prepareDataList; + private Boolean onlyOneVariable; + private WriteTemplateAnalysisCellTypeEnum cellType; public int getColumnIndex() { return columnIndex; @@ -37,11 +41,49 @@ public class AnalysisCell { this.variableList = variableList; } - public String getPrepareData() { - return prepareData; + public List getPrepareDataList() { + return prepareDataList; + } + + public void setPrepareDataList(List prepareDataList) { + this.prepareDataList = prepareDataList; + } + + public Boolean getOnlyOneVariable() { + return onlyOneVariable; + } + + public void setOnlyOneVariable(Boolean onlyOneVariable) { + this.onlyOneVariable = onlyOneVariable; + } + + public WriteTemplateAnalysisCellTypeEnum getCellType() { + return cellType; + } + + public void setCellType(WriteTemplateAnalysisCellTypeEnum cellType) { + this.cellType = cellType; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + AnalysisCell that = (AnalysisCell)o; + if (columnIndex != that.columnIndex) { + return false; + } + return rowIndex == that.rowIndex; } - public void setPrepareData(String prepareData) { - this.prepareData = prepareData; + @Override + public int hashCode() { + int result = columnIndex; + result = 31 * result + rowIndex; + return result; } } diff --git a/src/main/java/com/alibaba/excel/write/metadata/fill/FillConfig.java b/src/main/java/com/alibaba/excel/write/metadata/fill/FillConfig.java new file mode 100644 index 00000000..a5bbe917 --- /dev/null +++ b/src/main/java/com/alibaba/excel/write/metadata/fill/FillConfig.java @@ -0,0 +1,83 @@ +package com.alibaba.excel.write.metadata.fill; + +import com.alibaba.excel.enums.WriteDirectionEnum; + +/** + * Fill config + * + * @author Jiaju Zhuang + **/ +public class FillConfig { + private WriteDirectionEnum direction; + /** + * Create a new row each time you use the list parameter.The default create if necessary. + *

+ * Warnning:If you use forceNewRow set true, will not be able to use asynchronous write file, simply + * say the whole file will be stored in memory. + */ + private Boolean forceNewRow; + private boolean hasInit; + + public WriteDirectionEnum getDirection() { + return direction; + } + + public void setDirection(WriteDirectionEnum direction) { + this.direction = direction; + } + + public Boolean getForceNewRow() { + return forceNewRow; + } + + public void setForceNewRow(Boolean forceNewRow) { + this.forceNewRow = forceNewRow; + } + + public void init() { + if (hasInit) { + return; + } + if (direction == null) { + direction = WriteDirectionEnum.VERTICAL; + } + if (forceNewRow == null) { + forceNewRow = Boolean.FALSE; + } + hasInit = true; + } + + public static FillConfigBuilder builder() { + return new FillConfigBuilder(); + } + + public static class FillConfigBuilder { + private FillConfig fillConfig; + + FillConfigBuilder() { + this.fillConfig = new FillConfig(); + } + + public FillConfigBuilder direction(WriteDirectionEnum direction) { + fillConfig.setDirection(direction); + return this; + } + + public FillConfigBuilder forceNewRow(Boolean forceNewRow) { + fillConfig.setForceNewRow(forceNewRow); + return this; + } + + public FillConfig build() { + return build(true); + } + + public FillConfig build(boolean autoInit) { + if (autoInit) { + fillConfig.init(); + } + return fillConfig; + } + + } +} diff --git a/src/main/java/com/alibaba/excel/write/metadata/holder/WriteSheetHolder.java b/src/main/java/com/alibaba/excel/write/metadata/holder/WriteSheetHolder.java index 208fb419..1fb19fb0 100644 --- a/src/main/java/com/alibaba/excel/write/metadata/holder/WriteSheetHolder.java +++ b/src/main/java/com/alibaba/excel/write/metadata/holder/WriteSheetHolder.java @@ -3,12 +3,13 @@ package com.alibaba.excel.write.metadata.holder; import java.util.HashMap; import java.util.Map; +import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.xssf.streaming.SXSSFSheet; import org.apache.poi.xssf.usermodel.XSSFSheet; import com.alibaba.excel.enums.HolderEnum; -import com.alibaba.excel.enums.WriteLastRowType; -import com.alibaba.excel.support.ExcelTypeEnum; +import com.alibaba.excel.enums.WriteLastRowTypeEnum; import com.alibaba.excel.write.metadata.WriteSheet; /** @@ -22,14 +23,22 @@ public class WriteSheetHolder extends AbstractWriteHolder { */ private WriteSheet writeSheet; /*** - * poi sheet + * Current poi Sheet.This is only for writing, and there may be no data in version 07 when template data needs to be + * read. + *

    + *
  • 03:{@link HSSFSheet}
  • + *
  • 07:{@link SXSSFSheet}
  • + *
*/ private Sheet sheet; - /** - * When reading version 07 with the template, the sheet cannot read template data, so need to use - * xssfSheet to get it. + /*** + * Current poi Sheet.Be sure to use and this method when reading template data. + *
    + *
  • 03:{@link HSSFSheet}
  • + *
  • 07:{@link XSSFSheet}
  • + *
*/ - private XSSFSheet xssfSheet; + private Sheet cachedSheet; /*** * sheetNo */ @@ -53,7 +62,7 @@ public class WriteSheetHolder extends AbstractWriteHolder { * @param writeSheet * @param writeWorkbookHolder */ - private WriteLastRowType writeLastRowType; + private WriteLastRowTypeEnum writeLastRowTypeEnum; public WriteSheetHolder(WriteSheet writeSheet, WriteWorkbookHolder writeWorkbookHolder) { super(writeSheet, writeWorkbookHolder, writeWorkbookHolder.getWriteWorkbook().getConvertAllFiled()); @@ -66,10 +75,10 @@ public class WriteSheetHolder extends AbstractWriteHolder { } this.parentWriteWorkbookHolder = writeWorkbookHolder; this.hasBeenInitializedTable = new HashMap(); - if (writeWorkbookHolder.getTemplateInputStream() == null && writeWorkbookHolder.getTemplateFile() == null) { - writeLastRowType = WriteLastRowType.COMMON_EMPTY; + if (writeWorkbookHolder.getTempTemplateInputStream() != null) { + writeLastRowTypeEnum = WriteLastRowTypeEnum.TEMPLATE_EMPTY; } else { - writeLastRowType = WriteLastRowType.TEMPLATE_EMPTY; + writeLastRowTypeEnum = WriteLastRowTypeEnum.COMMON_EMPTY; } } @@ -93,12 +102,12 @@ public class WriteSheetHolder extends AbstractWriteHolder { return sheetNo; } - public XSSFSheet getXssfSheet() { - return xssfSheet; + public Sheet getCachedSheet() { + return cachedSheet; } - public void setXssfSheet(XSSFSheet xssfSheet) { - this.xssfSheet = xssfSheet; + public void setCachedSheet(Sheet cachedSheet) { + this.cachedSheet = cachedSheet; } public void setSheetNo(Integer sheetNo) { @@ -129,12 +138,12 @@ public class WriteSheetHolder extends AbstractWriteHolder { this.hasBeenInitializedTable = hasBeenInitializedTable; } - public WriteLastRowType getWriteLastRowType() { - return writeLastRowType; + public WriteLastRowTypeEnum getWriteLastRowTypeEnum() { + return writeLastRowTypeEnum; } - public void setWriteLastRowType(WriteLastRowType writeLastRowType) { - this.writeLastRowType = writeLastRowType; + public void setWriteLastRowTypeEnum(WriteLastRowTypeEnum writeLastRowTypeEnum) { + this.writeLastRowTypeEnum = writeLastRowTypeEnum; } /** @@ -145,25 +154,16 @@ public class WriteSheetHolder extends AbstractWriteHolder { public int getNewRowIndexAndStartDoWrite() { // 'getLastRowNum' doesn't matter if it has one or zero,is's zero int newRowIndex = 0; - switch (writeLastRowType) { + switch (writeLastRowTypeEnum) { case TEMPLATE_EMPTY: - if (parentWriteWorkbookHolder.getExcelType() == ExcelTypeEnum.XLSX) { - if (parentWriteWorkbookHolder.getTemplateLastRowMap().containsKey(sheetNo)) { - newRowIndex = parentWriteWorkbookHolder.getTemplateLastRowMap().get(sheetNo); - } - } else { - newRowIndex = sheet.getLastRowNum(); - } - newRowIndex++; - break; case HAS_DATA: - newRowIndex = sheet.getLastRowNum(); + newRowIndex = Math.max(sheet.getLastRowNum(), cachedSheet.getLastRowNum()); newRowIndex++; break; default: break; } - writeLastRowType = WriteLastRowType.HAS_DATA; + writeLastRowTypeEnum = WriteLastRowTypeEnum.HAS_DATA; return newRowIndex; } diff --git a/src/main/java/com/alibaba/excel/write/metadata/holder/WriteWorkbookHolder.java b/src/main/java/com/alibaba/excel/write/metadata/holder/WriteWorkbookHolder.java index 0120142e..ec364fb9 100644 --- a/src/main/java/com/alibaba/excel/write/metadata/holder/WriteWorkbookHolder.java +++ b/src/main/java/com/alibaba/excel/write/metadata/holder/WriteWorkbookHolder.java @@ -1,20 +1,28 @@ package com.alibaba.excel.write.metadata.holder; import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; +import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.xssf.streaming.SXSSFWorkbook; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import com.alibaba.excel.enums.HolderEnum; import com.alibaba.excel.exception.ExcelGenerateException; import com.alibaba.excel.support.ExcelTypeEnum; +import com.alibaba.excel.util.FileUtils; +import com.alibaba.excel.util.IoUtils; import com.alibaba.excel.write.metadata.WriteWorkbook; /** @@ -24,14 +32,22 @@ import com.alibaba.excel.write.metadata.WriteWorkbook; */ public class WriteWorkbookHolder extends AbstractWriteHolder { /*** - * poi Workbook + * Current poi Workbook.This is only for writing, and there may be no data in version 07 when template data needs to + * be read. + *
    + *
  • 03:{@link HSSFWorkbook}
  • + *
  • 07:{@link SXSSFWorkbook}
  • + *
*/ private Workbook workbook; - /** - * When reading version 07 with the template, the workbook cannot get the specific line number, so it - * needs to get the specific line number. + /*** + * Current poi Workbook.Be sure to use and this method when reading template data. + *
    + *
  • 03:{@link HSSFWorkbook}
  • + *
  • 07:{@link XSSFWorkbook}
  • + *
*/ - private XSSFWorkbook xssfWorkbook; + private Workbook cachedWorkbook; /** * current param */ @@ -80,11 +96,6 @@ public class WriteWorkbookHolder extends AbstractWriteHolder { * prevent duplicate creation of sheet objects */ private Map hasBeenInitializedSheet; - /** - * When using SXSSFWorkbook, you can't get the actual last line.But we need to read the last line when we are using - * the template, so we cache it - */ - private Map templateLastRowMap; public WriteWorkbookHolder(WriteWorkbook writeWorkbook) { super(writeWorkbook, null, writeWorkbook.getConvertAllFiled()); @@ -99,19 +110,16 @@ public class WriteWorkbookHolder extends AbstractWriteHolder { } else { this.outputStream = writeWorkbook.getOutputStream(); } - if (writeWorkbook.getTemplateInputStream() != null) { - if (writeWorkbook.getTemplateInputStream().markSupported()) { - this.templateInputStream = writeWorkbook.getTemplateInputStream(); - } else { - this.templateInputStream = new BufferedInputStream(writeWorkbook.getTemplateInputStream()); - } - } - this.templateFile = writeWorkbook.getTemplateFile(); if (writeWorkbook.getAutoCloseStream() == null) { this.autoCloseStream = Boolean.TRUE; } else { this.autoCloseStream = writeWorkbook.getAutoCloseStream(); } + try { + copyTemplate(); + } catch (IOException e) { + throw new ExcelGenerateException("Copy template failure.", e); + } if (writeWorkbook.getExcelType() == null) { if (file != null && file.getName().endsWith(ExcelTypeEnum.XLS.getValue())) { this.excelType = ExcelTypeEnum.XLS; @@ -127,7 +135,25 @@ public class WriteWorkbookHolder extends AbstractWriteHolder { this.mandatoryUseInputStream = writeWorkbook.getMandatoryUseInputStream(); } this.hasBeenInitializedSheet = new HashMap(); - this.templateLastRowMap = new HashMap(8); + } + + private void copyTemplate() throws IOException { + if (writeWorkbook.getTemplateFile() == null && writeWorkbook.getTemplateInputStream() == null) { + return; + } + byte[] templateFileByte = null; + if (writeWorkbook.getTemplateFile() != null) { + templateFileByte = FileUtils.readFileToByteArray(writeWorkbook.getTemplateFile()); + } else if (writeWorkbook.getTemplateInputStream() == null) { + try { + templateFileByte = IoUtils.toByteArray(writeWorkbook.getTemplateInputStream()); + } finally { + if (autoCloseStream) { + writeWorkbook.getTemplateInputStream().close(); + } + } + } + this.tempTemplateInputStream = new ByteArrayInputStream(templateFileByte); } public Workbook getWorkbook() { @@ -138,12 +164,12 @@ public class WriteWorkbookHolder extends AbstractWriteHolder { this.workbook = workbook; } - public XSSFWorkbook getXssfWorkbook() { - return xssfWorkbook; + public Workbook getCachedWorkbook() { + return cachedWorkbook; } - public void setXssfWorkbook(XSSFWorkbook xssfWorkbook) { - this.xssfWorkbook = xssfWorkbook; + public void setCachedWorkbook(Workbook cachedWorkbook) { + this.cachedWorkbook = cachedWorkbook; } public Map getHasBeenInitializedSheet() { @@ -226,14 +252,6 @@ public class WriteWorkbookHolder extends AbstractWriteHolder { this.mandatoryUseInputStream = mandatoryUseInputStream; } - public Map getTemplateLastRowMap() { - return templateLastRowMap; - } - - public void setTemplateLastRowMap(Map templateLastRowMap) { - this.templateLastRowMap = templateLastRowMap; - } - @Override public HolderEnum holderType() { return HolderEnum.WORKBOOK; diff --git a/src/main/java/com/alibaba/excel/write/style/AbstractCellStyleStrategy.java b/src/main/java/com/alibaba/excel/write/style/AbstractCellStyleStrategy.java index 51b03d13..7aafc448 100644 --- a/src/main/java/com/alibaba/excel/write/style/AbstractCellStyleStrategy.java +++ b/src/main/java/com/alibaba/excel/write/style/AbstractCellStyleStrategy.java @@ -1,5 +1,7 @@ package com.alibaba.excel.write.style; +import java.util.List; + import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Workbook; @@ -27,28 +29,23 @@ public abstract class AbstractCellStyleStrategy implements CellWriteHandler, She } @Override - public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) { + public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, + Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) { } @Override - public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) { - initCellStyle(writeWorkbookHolder.getWorkbook()); - hasInitialized = true; - } + public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, + Head head, Integer relativeRowIndex, Boolean isHead) { - @Override - public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, - Head head, int relativeRowIndex, boolean isHead) { - if (!hasInitialized) { - initCellStyle(writeSheetHolder.getParentWriteWorkbookHolder().getWorkbook()); - hasInitialized = true; - } } @Override - public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, - Cell cell, Head head, int relativeRowIndex, boolean isHead) { + public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, + List cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { + if (isHead == null || head == null) { + return; + } if (isHead) { setHeadCellStyle(cell, head, relativeRowIndex); } else { @@ -56,6 +53,17 @@ public abstract class AbstractCellStyleStrategy implements CellWriteHandler, She } } + @Override + public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) { + + } + + @Override + public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) { + initCellStyle(writeWorkbookHolder.getWorkbook()); + hasInitialized = true; + } + /** * Initialization cell style * @@ -70,7 +78,7 @@ public abstract class AbstractCellStyleStrategy implements CellWriteHandler, She * @param head * @param relativeRowIndex */ - protected abstract void setHeadCellStyle(Cell cell, Head head, int relativeRowIndex); + protected abstract void setHeadCellStyle(Cell cell, Head head, Integer relativeRowIndex); /** * Sets the cell style of content @@ -79,6 +87,6 @@ public abstract class AbstractCellStyleStrategy implements CellWriteHandler, She * @param head * @param relativeRowIndex */ - protected abstract void setContentCellStyle(Cell cell, Head head, int relativeRowIndex); + protected abstract void setContentCellStyle(Cell cell, Head head, Integer relativeRowIndex); } diff --git a/src/main/java/com/alibaba/excel/write/style/AbstractVerticalCellStyleStrategy.java b/src/main/java/com/alibaba/excel/write/style/AbstractVerticalCellStyleStrategy.java index 083d9cf7..09903a00 100644 --- a/src/main/java/com/alibaba/excel/write/style/AbstractVerticalCellStyleStrategy.java +++ b/src/main/java/com/alibaba/excel/write/style/AbstractVerticalCellStyleStrategy.java @@ -29,7 +29,7 @@ public abstract class AbstractVerticalCellStyleStrategy extends AbstractCellStyl } @Override - protected void setHeadCellStyle(Cell cell, Head head, int relativeRowIndex) { + protected void setHeadCellStyle(Cell cell, Head head, Integer relativeRowIndex) { int columnIndex = head.getColumnIndex(); if (headCellStyleCache.containsKey(columnIndex)) { CellStyle cellStyle = headCellStyleCache.get(columnIndex); @@ -49,7 +49,7 @@ public abstract class AbstractVerticalCellStyleStrategy extends AbstractCellStyl } @Override - protected void setContentCellStyle(Cell cell, Head head, int relativeRowIndex) { + protected void setContentCellStyle(Cell cell, Head head, Integer relativeRowIndex) { int columnIndex = head.getColumnIndex(); if (contentCellStyleCache.containsKey(columnIndex)) { CellStyle cellStyle = contentCellStyleCache.get(columnIndex); diff --git a/src/main/java/com/alibaba/excel/write/style/HorizontalCellStyleStrategy.java b/src/main/java/com/alibaba/excel/write/style/HorizontalCellStyleStrategy.java index da5e53bc..295cd556 100644 --- a/src/main/java/com/alibaba/excel/write/style/HorizontalCellStyleStrategy.java +++ b/src/main/java/com/alibaba/excel/write/style/HorizontalCellStyleStrategy.java @@ -51,7 +51,7 @@ public class HorizontalCellStyleStrategy extends AbstractCellStyleStrategy { } @Override - protected void setHeadCellStyle(Cell cell, Head head, int relativeRowIndex) { + protected void setHeadCellStyle(Cell cell, Head head, Integer relativeRowIndex) { if (headCellStyle == null) { return; } @@ -59,7 +59,7 @@ public class HorizontalCellStyleStrategy extends AbstractCellStyleStrategy { } @Override - protected void setContentCellStyle(Cell cell, Head head, int relativeRowIndex) { + protected void setContentCellStyle(Cell cell, Head head, Integer relativeRowIndex) { if (contentCellStyleList == null || contentCellStyleList.isEmpty()) { return; } diff --git a/src/main/java/com/alibaba/excel/write/style/column/AbstractColumnWidthStyleStrategy.java b/src/main/java/com/alibaba/excel/write/style/column/AbstractColumnWidthStyleStrategy.java index 67e9733a..07afcfc5 100644 --- a/src/main/java/com/alibaba/excel/write/style/column/AbstractColumnWidthStyleStrategy.java +++ b/src/main/java/com/alibaba/excel/write/style/column/AbstractColumnWidthStyleStrategy.java @@ -1,5 +1,7 @@ package com.alibaba.excel.write.style.column; +import java.util.List; + import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; @@ -24,27 +26,28 @@ public abstract class AbstractColumnWidthStyleStrategy implements CellWriteHandl @Override public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, - Head head, int relativeRowIndex, boolean isHead) { + Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) {} - } + @Override + public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, + Head head, Integer relativeRowIndex, Boolean isHead) {} @Override - public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, - Cell cell, Head head, int relativeRowIndex, boolean isHead) { - setColumnWidth(writeSheetHolder, cellData, cell, head, relativeRowIndex, isHead); + public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { + setColumnWidth(writeSheetHolder, cellDataList, cell, head, relativeRowIndex, isHead); } /** * Sets the column width when head create * * @param writeSheetHolder - * @param cellData + * @param cellDataList * @param cell * @param head * @param relativeRowIndex * @param isHead */ - protected abstract void setColumnWidth(WriteSheetHolder writeSheetHolder, CellData cellData, Cell cell, Head head, - int relativeRowIndex, boolean isHead); + protected abstract void setColumnWidth(WriteSheetHolder writeSheetHolder, List cellDataList, Cell cell, Head head, + Integer relativeRowIndex, Boolean isHead); } diff --git a/src/main/java/com/alibaba/excel/write/style/column/AbstractHeadColumnWidthStyleStrategy.java b/src/main/java/com/alibaba/excel/write/style/column/AbstractHeadColumnWidthStyleStrategy.java index da9b7dac..9908144f 100644 --- a/src/main/java/com/alibaba/excel/write/style/column/AbstractHeadColumnWidthStyleStrategy.java +++ b/src/main/java/com/alibaba/excel/write/style/column/AbstractHeadColumnWidthStyleStrategy.java @@ -1,5 +1,7 @@ package com.alibaba.excel.write.style.column; +import java.util.List; + import org.apache.poi.ss.usermodel.Cell; import com.alibaba.excel.metadata.CellData; @@ -12,10 +14,12 @@ import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; * @author Jiaju Zhuang */ public abstract class AbstractHeadColumnWidthStyleStrategy extends AbstractColumnWidthStyleStrategy { + @Override - protected void setColumnWidth(WriteSheetHolder writeSheetHolder, CellData cellData, Cell cell, Head head, - int relativeRowIndex, boolean isHead) { - if (!isHead && relativeRowIndex != 0) { + protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List cellDataList, Cell cell, Head head, + Integer relativeRowIndex, Boolean isHead) { + boolean needSetWidth = relativeRowIndex != null && (isHead || relativeRowIndex == 0); + if (!needSetWidth) { return; } Integer width = columnWidth(head); @@ -36,4 +40,5 @@ public abstract class AbstractHeadColumnWidthStyleStrategy extends AbstractColum * @return */ protected abstract Integer columnWidth(Head head); + } diff --git a/src/main/java/com/alibaba/excel/write/style/column/LongestMatchColumnWidthStyleStrategy.java b/src/main/java/com/alibaba/excel/write/style/column/LongestMatchColumnWidthStyleStrategy.java index fb807f39..006927f9 100644 --- a/src/main/java/com/alibaba/excel/write/style/column/LongestMatchColumnWidthStyleStrategy.java +++ b/src/main/java/com/alibaba/excel/write/style/column/LongestMatchColumnWidthStyleStrategy.java @@ -1,12 +1,14 @@ package com.alibaba.excel.write.style.column; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.apache.poi.ss.usermodel.Cell; import com.alibaba.excel.metadata.CellData; import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.util.CollectionUtils; import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; /** @@ -24,9 +26,10 @@ public class LongestMatchColumnWidthStyleStrategy extends AbstractColumnWidthSty private static final Map> CACHE = new HashMap>(8); @Override - protected void setColumnWidth(WriteSheetHolder writeSheetHolder, CellData cellData, Cell cell, Head head, - int relativeRowIndex, boolean isHead) { - if (!isHead && cellData == null) { + protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List cellDataList, Cell cell, Head head, + Integer relativeRowIndex, Boolean isHead) { + boolean needSetWidth = isHead || !CollectionUtils.isEmpty(cellDataList); + if (!needSetWidth) { return; } Map maxColumnWidthMap = CACHE.get(writeSheetHolder.getSheetNo()); @@ -34,24 +37,25 @@ public class LongestMatchColumnWidthStyleStrategy extends AbstractColumnWidthSty maxColumnWidthMap = new HashMap(16); CACHE.put(writeSheetHolder.getSheetNo(), maxColumnWidthMap); } - Integer columnWidth = dataLength(cellData, cell, isHead); + Integer columnWidth = dataLength(cellDataList, cell, isHead); if (columnWidth < 0) { return; } if (columnWidth > MAX_COLUMN_WIDTH) { columnWidth = MAX_COLUMN_WIDTH; } - Integer maxColumnWidth = maxColumnWidthMap.get(head.getColumnIndex()); + Integer maxColumnWidth = maxColumnWidthMap.get(cell.getColumnIndex()); if (maxColumnWidth == null || columnWidth > maxColumnWidth) { - maxColumnWidthMap.put(head.getColumnIndex(), columnWidth); - writeSheetHolder.getSheet().setColumnWidth(head.getColumnIndex(), columnWidth * 256); + maxColumnWidthMap.put(cell.getColumnIndex(), columnWidth); + writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), columnWidth * 256); } } - private Integer dataLength(CellData cellData, Cell cell, boolean isHead) { + private Integer dataLength(List cellDataList, Cell cell, Boolean isHead) { if (isHead) { return cell.getStringCellValue().getBytes().length; } + CellData cellData = cellDataList.get(0); switch (cellData.getType()) { case STRING: return cellData.getStringValue().getBytes().length; diff --git a/src/main/java/com/alibaba/excel/write/style/row/AbstractRowHeightStyleStrategy.java b/src/main/java/com/alibaba/excel/write/style/row/AbstractRowHeightStyleStrategy.java index 3a6a3431..303c8180 100644 --- a/src/main/java/com/alibaba/excel/write/style/row/AbstractRowHeightStyleStrategy.java +++ b/src/main/java/com/alibaba/excel/write/style/row/AbstractRowHeightStyleStrategy.java @@ -20,14 +20,23 @@ public abstract class AbstractRowHeightStyleStrategy implements RowWriteHandler, } @Override - public void beforeRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, int rowIndex, - int relativeRowIndex, boolean isHead) { + public void beforeRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Integer rowIndex, + Integer relativeRowIndex, Boolean isHead) { } @Override public void afterRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, - int relativeRowIndex, boolean isHead) { + Integer relativeRowIndex, Boolean isHead) { + + } + + @Override + public void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, + Integer relativeRowIndex, Boolean isHead) { + if (isHead == null) { + return; + } if (isHead) { setHeadColumnHeight(row, relativeRowIndex); } else { diff --git a/src/test/java/com/alibaba/easyexcel/test/core/fill/FillData.java b/src/test/java/com/alibaba/easyexcel/test/core/fill/FillData.java index 92e18a8c..13dfa17a 100644 --- a/src/test/java/com/alibaba/easyexcel/test/core/fill/FillData.java +++ b/src/test/java/com/alibaba/easyexcel/test/core/fill/FillData.java @@ -1,6 +1,8 @@ package com.alibaba.easyexcel.test.core.fill; +import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.annotation.format.NumberFormat; +import com.alibaba.excel.converters.doubleconverter.DoubleStringConverter; import lombok.Data; @@ -10,6 +12,7 @@ import lombok.Data; @Data public class FillData { private String name; - @NumberFormat("0#") + @NumberFormat("#") + @ExcelProperty(converter = DoubleStringConverter.class) private double number; } diff --git a/src/test/java/com/alibaba/easyexcel/test/core/fill/FillDataTest.java b/src/test/java/com/alibaba/easyexcel/test/core/fill/FillDataTest.java index 06236ca5..93a5457a 100644 --- a/src/test/java/com/alibaba/easyexcel/test/core/fill/FillDataTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/core/fill/FillDataTest.java @@ -2,7 +2,9 @@ package com.alibaba.easyexcel.test.core.fill; import java.io.File; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import org.apache.poi.ss.usermodel.BorderStyle; import org.apache.poi.ss.usermodel.FillPatternType; @@ -10,6 +12,7 @@ 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.BeforeClass; import org.junit.FixMethodOrder; import org.junit.Test; @@ -19,9 +22,13 @@ 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.excel.EasyExcel; +import com.alibaba.excel.ExcelWriter; +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.OnceAbsoluteMergeStrategy; +import com.alibaba.excel.write.metadata.WriteSheet; +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; @@ -39,52 +46,112 @@ public class FillDataTest { private static File file07; private static File file03; private static File simpleTemplate07; + private static File simpleTemplate03; + private static File fileComplex07; + private static File complexFillTemplate07; + private static File fileComplex03; + private static File complexFillTemplate03; + private static File fileHorizontal07; + private static File horizontalFillTemplate07; + private static File fileHorizontal03; + private static File horizontalFillTemplate03; @BeforeClass public static void init() { file07 = TestFileUtil.createNewFile("fill07.xlsx"); file03 = TestFileUtil.createNewFile("fill03.xls"); simpleTemplate07 = TestFileUtil.readFile("fill" + File.separator + "simple.xlsx"); + simpleTemplate03 = TestFileUtil.readFile("fill" + File.separator + "simple.xls"); + fileComplex07 = TestFileUtil.createNewFile("fillComplex07.xlsx"); + complexFillTemplate07 = TestFileUtil.readFile("fill" + File.separator + "complex.xlsx"); + fileComplex03 = TestFileUtil.createNewFile("fillComplex03.xls"); + complexFillTemplate03 = TestFileUtil.readFile("fill" + File.separator + "complex.xls"); + fileHorizontal07 = TestFileUtil.createNewFile("fillHorizontal07.xlsx"); + horizontalFillTemplate07 = TestFileUtil.readFile("fill" + File.separator + "horizontal.xlsx"); + fileHorizontal03 = TestFileUtil.createNewFile("fillHorizontal03.xls"); + horizontalFillTemplate03 = TestFileUtil.readFile("fill" + File.separator + "horizontal.xls"); } @Test public void t01Fill07() { - fill(file07); + fill(file07, simpleTemplate07); } @Test public void t02Fill03() { - fill(file03); + fill(file03, simpleTemplate03); } - private void fill(File file) { + @Test + public void t03ComplexFill07() { + complexFill(fileComplex07, complexFillTemplate07); + } + + @Test + public void t04ComplexFill03() { + complexFill(fileComplex03, complexFillTemplate03); + } + + @Test + public void t05HorizontalFill07() { + horizontalFill(fileHorizontal07, horizontalFillTemplate07); + } + + @Test + public void t06HorizontalFill03() { + horizontalFill(fileHorizontal03, horizontalFillTemplate03); + } + + private void horizontalFill(File file, File template) { + ExcelWriter excelWriter = EasyExcel.write(file).withTemplate(template).build(); + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + FillConfig fillConfig = FillConfig.builder().direction(WriteDirectionEnum.HORIZONTAL).build(); + excelWriter.fill(data(), fillConfig, writeSheet); + excelWriter.fill(data(), fillConfig, writeSheet); + Map map = new HashMap(); + map.put("date", "2019年10月9日13:28:28"); + excelWriter.fill(map, writeSheet); + excelWriter.finish(); + + List list = EasyExcel.read(file).sheet().headRowNumber(0).doReadSync(); + Assert.assertEquals(list.size(), 5L); + Map map0 = (Map)list.get(0); + Assert.assertEquals("张三", map0.get(2)); + } + + private void complexFill(File file, File template) { + ExcelWriter excelWriter = EasyExcel.write(file).withTemplate(template).build(); + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); + excelWriter.fill(data(), fillConfig, writeSheet); + excelWriter.fill(data(), fillConfig, writeSheet); + Map map = new HashMap(); + map.put("date", "2019年10月9日13:28:28"); + map.put("total", 1000); + excelWriter.fill(map, writeSheet); + excelWriter.finish(); + List list = EasyExcel.read(file).sheet().headRowNumber(3).doReadSync(); + Assert.assertEquals(list.size(), 21L); + Map map19 = (Map)list.get(19); + Assert.assertEquals("张三", map19.get(0)); + } + + private void fill(File file, File template) { FillData fillData = new FillData(); fillData.setName("张三"); fillData.setNumber(5.2); - EasyExcel.write(file).withTemplate(simpleTemplate07).sheet().doFill(fillData); + EasyExcel.write(file, FillData.class).withTemplate(template).sheet().doFill(fillData); } - private List data() { - List list = new ArrayList(); - StyleData data = new StyleData(); - data.setString("字符串0"); - data.setString1("字符串01"); - StyleData data1 = new StyleData(); - data1.setString("字符串1"); - data1.setString1("字符串11"); - list.add(data); - list.add(data1); - return list; - } - - private List data10() { - List list = new ArrayList(); + private List data() { + List list = new ArrayList(); for (int i = 0; i < 10; i++) { - StyleData data = new StyleData(); - data.setString("字符串0"); - data.setString1("字符串01"); - list.add(data); + FillData fillData = new FillData(); + list.add(fillData); + fillData.setName("张三"); + fillData.setNumber(5.2); } return list; } + } diff --git a/src/test/java/com/alibaba/easyexcel/test/core/large/LargeDataTest.java b/src/test/java/com/alibaba/easyexcel/test/core/large/LargeDataTest.java index b7ba56b3..fa32433a 100644 --- a/src/test/java/com/alibaba/easyexcel/test/core/large/LargeDataTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/core/large/LargeDataTest.java @@ -1,13 +1,19 @@ package com.alibaba.easyexcel.test.core.large; import java.io.File; +import java.util.ArrayList; +import java.util.List; +import org.junit.BeforeClass; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.alibaba.easyexcel.test.demo.write.DemoData; import com.alibaba.easyexcel.test.util.TestFileUtil; import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.ExcelWriter; +import com.alibaba.excel.write.metadata.WriteSheet; /** * @@ -15,12 +21,67 @@ import com.alibaba.excel.EasyExcel; */ public class LargeDataTest { private static final Logger LOGGER = LoggerFactory.getLogger(LargeDataTest.class); + private static File fileFill07; + private static File template07; + private int i = 0; + + @BeforeClass + public static void init() { + fileFill07 = TestFileUtil.createNewFile("largefill07.xlsx"); + template07 = TestFileUtil.readFile("large" + File.separator + "fill.xlsx"); + } @Test - public void read() { + public void t01Read() { long start = System.currentTimeMillis(); EasyExcel.read(TestFileUtil.getPath() + "large" + File.separator + "large07.xlsx", LargeData.class, new LargeDataListener()).headRowNumber(2).sheet().doRead(); LOGGER.info("Large data total time spent:{}", System.currentTimeMillis() - start); } + + @Test + public void t02Fill() { + ExcelWriter excelWriter = EasyExcel.write(fileFill07).withTemplate(template07).build(); + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + for (int j = 0; j < 100; j++) { + excelWriter.fill(data(), writeSheet); + LOGGER.info("{} fill success.", j); + } + excelWriter.finish(); + } + + private List data() { + List list = new ArrayList(); + int size = i + 5000; + for (; i < size; i++) { + LargeData largeData = new LargeData(); + list.add(largeData); + largeData.setStr1("str1-" + i); + largeData.setStr2("str2-" + i); + largeData.setStr3("str3-" + i); + largeData.setStr4("str4-" + i); + largeData.setStr5("str5-" + i); + largeData.setStr6("str6-" + i); + largeData.setStr7("str7-" + i); + largeData.setStr8("str8-" + i); + largeData.setStr9("str9-" + i); + largeData.setStr10("str10-" + i); + largeData.setStr11("str11-" + i); + largeData.setStr12("str12-" + i); + largeData.setStr13("str13-" + i); + largeData.setStr14("str14-" + i); + largeData.setStr15("str15-" + i); + largeData.setStr16("str16-" + i); + largeData.setStr17("str17-" + i); + largeData.setStr18("str18-" + i); + largeData.setStr19("str19-" + i); + largeData.setStr20("str20-" + i); + largeData.setStr21("str21-" + i); + largeData.setStr22("str22-" + i); + largeData.setStr23("str23-" + i); + largeData.setStr24("str24-" + i); + largeData.setStr25("str25-" + i); + } + return list; + } } diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/fill/FillData.java b/src/test/java/com/alibaba/easyexcel/test/demo/fill/FillData.java new file mode 100644 index 00000000..8890b82f --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/demo/fill/FillData.java @@ -0,0 +1,12 @@ +package com.alibaba.easyexcel.test.demo.fill; + +import lombok.Data; + +/** + * @author Jiaju Zhuang + */ +@Data +public class FillData { + private String name; + private double number; +} diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/fill/FillTest.java b/src/test/java/com/alibaba/easyexcel/test/demo/fill/FillTest.java new file mode 100644 index 00000000..0159cbc7 --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/demo/fill/FillTest.java @@ -0,0 +1,181 @@ +package com.alibaba.easyexcel.test.demo.fill; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.junit.Ignore; +import org.junit.Test; + +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.ExcelWriter; +import com.alibaba.excel.enums.WriteDirectionEnum; +import com.alibaba.excel.write.metadata.WriteSheet; +import com.alibaba.excel.write.metadata.fill.FillConfig; + +/** + * 写的填充写法 + * + * @author Jiaju Zhuang + */ +@Ignore +public class FillTest { + /** + * 最简单的填充 + */ + @Test + public void simpleFill() { + // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 + String templateFileName = + TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "simple.xlsx"; + + // 方案1 根据对象填充 + String fileName = TestFileUtil.getPath() + "simpleFill" + System.currentTimeMillis() + ".xlsx"; + // 这里 会填充到第一个sheet, 然后文件流会自动关闭 + FillData fillData = new FillData(); + fillData.setName("张三"); + fillData.setNumber(5.2); + EasyExcel.write(fileName).withTemplate(templateFileName).sheet().doFill(fillData); + + // 方案2 根据Map填充 + fileName = TestFileUtil.getPath() + "simpleFill" + System.currentTimeMillis() + ".xlsx"; + // 这里 会填充到第一个sheet, 然后文件流会自动关闭 + Map map = new HashMap(); + map.put("name", "张三"); + map.put("number", 5.2); + EasyExcel.write(fileName).withTemplate(templateFileName).sheet().doFill(map); + } + + /** + * 填充列表 + */ + @Test + public void listFill() { + // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 + // 填充list 的时候还要注意 模板中{.} 多了个点 表示list + String templateFileName = + TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "list.xlsx"; + + // 方案1 一下子全部放到内存里面 并填充 + String fileName = TestFileUtil.getPath() + "listFill" + System.currentTimeMillis() + ".xlsx"; + // 这里 会填充到第一个sheet, 然后文件流会自动关闭 + EasyExcel.write(fileName).withTemplate(templateFileName).sheet().doFill(data()); + + // 方案2 分多次 填充 会使用文件缓存(省内存) + fileName = TestFileUtil.getPath() + "listFill" + System.currentTimeMillis() + ".xlsx"; + ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build(); + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + excelWriter.fill(data(), writeSheet); + excelWriter.fill(data(), writeSheet); + // 千万别忘记关闭流 + excelWriter.finish(); + } + + /** + * 复杂的填充 + */ + @Test + public void complexFill() { + // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 + // {} 代表普通变量 {.} 代表是list的变量 + String templateFileName = + TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "complex.xlsx"; + + String fileName = TestFileUtil.getPath() + "complexFill" + System.currentTimeMillis() + ".xlsx"; + ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build(); + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + // 这里注意 入参用了forceNewRow 代表在写入list的时候不管list下面有没有空行 都会创建一行,然后下面的数据往后移动。默认 是false,会直接使用下一行,如果没有则创建。 + // forceNewRow 如果设置了true,有个缺点 就是他会把所有的数据都放到内存了,所以慎用 + // 简单的说 如果你的模板有list,且list不是最后一行,下面还有数据需要填充 就必须设置 forceNewRow=true 但是这个就会把所有数据放到内存 会很耗内存 + // 如果数据量大 list不是最后一行 参照下一个 + FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); + excelWriter.fill(data(), fillConfig, writeSheet); + excelWriter.fill(data(), fillConfig, writeSheet); + Map map = new HashMap(); + map.put("date", "2019年10月9日13:28:28"); + map.put("total", 1000); + excelWriter.fill(map, writeSheet); + excelWriter.finish(); + } + + /** + * 数据量大的复杂填充 + *

+ * 这里的解决方案是 确保模板list为最后一行,然后再拼接table.还有03版没救,只能刚正面加内存。 + */ + @Test + public void complexFillWithTable() { + // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 + // {} 代表普通变量 {.} 代表是list的变量 + // 这里模板 删除了list以后的数据,也就是统计的这一行 + String templateFileName = + TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "complexFillWithTable.xlsx"; + + String fileName = TestFileUtil.getPath() + "complexFillWithTable" + System.currentTimeMillis() + ".xlsx"; + ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build(); + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + // 直接写入数据 + excelWriter.fill(data(), writeSheet); + excelWriter.fill(data(), writeSheet); + + // 写入list之前的数据 + Map map = new HashMap(); + map.put("date", "2019年10月9日13:28:28"); + excelWriter.fill(map, writeSheet); + + // list 后面还有个统计 想办法手动写入 + // 这里偷懒直接用list 也可以用对象 + List> totalListList = new ArrayList>(); + List totalList = new ArrayList(); + totalListList.add(totalList); + totalList.add(null); + totalList.add(null); + totalList.add(null); + // 第四列 + totalList.add("统计:1000"); + // 这里是write 别和fill 搞错了 + excelWriter.write(totalListList, writeSheet); + excelWriter.finish(); + // 总体上写法比较复杂 但是也没有想到好的版本 异步的去写入excel 不支持行的删除和移动,也不支持备注这种的写入,所以也排除了可以 + // 新建一个 然后一点点复制过来的方案,最后导致list需要新增行的时候,后面的列的数据没法后移,后续会继续想想解决方案 + } + + /** + * 横向的填充 + */ + @Test + public void horizontalFill() { + // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 + // {} 代表普通变量 {.} 代表是list的变量 + String templateFileName = + TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "horizontal.xlsx"; + + String fileName = TestFileUtil.getPath() + "horizontalFill" + System.currentTimeMillis() + ".xlsx"; + ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build(); + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + FillConfig fillConfig = FillConfig.builder().direction(WriteDirectionEnum.HORIZONTAL).build(); + excelWriter.fill(data(), fillConfig, writeSheet); + excelWriter.fill(data(), fillConfig, writeSheet); + + Map map = new HashMap(); + map.put("date", "2019年10月9日13:28:28"); + excelWriter.fill(map, writeSheet); + + // 别忘记关闭流 + excelWriter.finish(); + } + + private List data() { + List list = new ArrayList(); + for (int i = 0; i < 10; i++) { + FillData fillData = new FillData(); + list.add(fillData); + fillData.setName("张三"); + fillData.setNumber(5.2); + } + return list; + } +} diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/write/CustomCellWriteHandler.java b/src/test/java/com/alibaba/easyexcel/test/demo/write/CustomCellWriteHandler.java index bff544a1..b92d44a2 100644 --- a/src/test/java/com/alibaba/easyexcel/test/demo/write/CustomCellWriteHandler.java +++ b/src/test/java/com/alibaba/easyexcel/test/demo/write/CustomCellWriteHandler.java @@ -1,5 +1,7 @@ package com.alibaba.easyexcel.test.demo.write; +import java.util.List; + import org.apache.poi.common.usermodel.HyperlinkType; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CreationHelper; @@ -25,13 +27,19 @@ public class CustomCellWriteHandler implements CellWriteHandler { @Override public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, - Head head, int relativeRowIndex, boolean isHead) { + Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) { + + } + + @Override + public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, + Head head, Integer relativeRowIndex, Boolean isHead) { } @Override - public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, - Cell cell, Head head, int relativeRowIndex, boolean isHead) { + public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, + List cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { // 这里可以对cell进行任何操作 LOGGER.info("第{}行,第{}列写入完成。", cell.getRowIndex(), cell.getColumnIndex()); if (isHead && cell.getColumnIndex() == 0) { diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/large/LargeData.java b/src/test/java/com/alibaba/easyexcel/test/temp/large/LargeData.java index 1424c426..43726ad3 100644 --- a/src/test/java/com/alibaba/easyexcel/test/temp/large/LargeData.java +++ b/src/test/java/com/alibaba/easyexcel/test/temp/large/LargeData.java @@ -1,11 +1,13 @@ package com.alibaba.easyexcel.test.temp.large; +import lombok.Builder; import lombok.Data; /** * @author Jiaju Zhuang */ @Data +@Builder public class LargeData { private String str1; diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiTest.java b/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiTest.java index 7828bd08..0a19c355 100644 --- a/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiTest.java @@ -4,7 +4,10 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellCopyPolicy; +import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.xssf.streaming.SXSSFRow; @@ -72,29 +75,101 @@ public class PoiTest { xssfWorkbook.close(); } + @Test + public void lastRowNum255() throws IOException, InvalidFormatException { + String file = TestFileUtil.getPath() + "fill" + File.separator + "complex.xlsx"; + XSSFWorkbook xssfWorkbook = new XSSFWorkbook(new File(file)); + SXSSFWorkbook sxssfWorkbook = new SXSSFWorkbook(xssfWorkbook); + Sheet xssfSheet = xssfWorkbook.getSheetAt(0); + xssfSheet.shiftRows(2, 4, 10); + + FileOutputStream fileout = new FileOutputStream("d://test/r2" + System.currentTimeMillis() + ".xlsx"); + sxssfWorkbook.write(fileout); + sxssfWorkbook.dispose(); + sxssfWorkbook.close(); + + xssfWorkbook.close(); + } + + @Test + public void cp() throws IOException, InvalidFormatException { + String file = "d://test/tt.xlsx"; + XSSFWorkbook xssfWorkbook = new XSSFWorkbook(new File(file)); + XSSFSheet xssfSheet = xssfWorkbook.getSheetAt(0); + XSSFRow row = xssfSheet.getRow(2); + xssfSheet.removeRow(row); +// Row r2= xssfSheet.createRow(2); +// r2.createCell(1); + SXSSFWorkbook sxssfWorkbook = new SXSSFWorkbook(xssfWorkbook); + + + SXSSFSheet sxssfSheet = sxssfWorkbook.getSheetAt(0); + sxssfSheet.createRow(2); + + + FileOutputStream fileout = new FileOutputStream("d://test/r2" + System.currentTimeMillis() + ".xlsx"); + sxssfWorkbook.write(fileout); + sxssfWorkbook.dispose(); + sxssfWorkbook.close(); + + xssfWorkbook.close(); + } + + @Test + public void lastRowNum233443() throws IOException, InvalidFormatException { + String file = "d://test/tt.xlsx"; + XSSFWorkbook xssfWorkbook = new XSSFWorkbook(new File(file)); + SXSSFWorkbook sxssfWorkbook = new SXSSFWorkbook(xssfWorkbook); + XSSFSheet xssfSheet = xssfWorkbook.getSheetAt(0); + XSSFRow row = xssfSheet.getRow(2); + xssfSheet.removeRow(row); + new CellCopyPolicy().createBuilder().build(); + + FileOutputStream fileout = new FileOutputStream("d://test/r2" + System.currentTimeMillis() + ".xlsx"); + sxssfWorkbook.write(fileout); + sxssfWorkbook.dispose(); + sxssfWorkbook.close(); + + xssfWorkbook.close(); + } + + @Test + public void lastRowNum2333() throws IOException, InvalidFormatException { + String file = TestFileUtil.getPath() + "fill" + File.separator + "simple.xlsx"; + XSSFWorkbook xssfWorkbook = new XSSFWorkbook(new File(file)); + SXSSFWorkbook sxssfWorkbook = new SXSSFWorkbook(xssfWorkbook); + Sheet xssfSheet = xssfWorkbook.getSheetAt(0); + Cell cell = xssfSheet.getRow(0).createCell(9); + cell.setCellValue("testssdf是士大夫否t"); + + FileOutputStream fileout = new FileOutputStream("d://test/r2" + System.currentTimeMillis() + ".xlsx"); + sxssfWorkbook.write(fileout); + sxssfWorkbook.dispose(); + sxssfWorkbook.close(); + + xssfWorkbook.close(); + } + @Test public void testread() throws IOException { String file = TestFileUtil.getPath() + "fill" + File.separator + "simple.xlsx"; SXSSFWorkbook xssfWorkbook = new SXSSFWorkbook(new XSSFWorkbook(file)); Sheet xssfSheet = xssfWorkbook.getXSSFWorkbook().getSheetAt(0); -// -// Cell cell = xssfSheet.getRow(0).createCell(9); - + // + // Cell cell = xssfSheet.getRow(0).createCell(9); String file1 = TestFileUtil.getPath() + "fill" + File.separator + "simple.xlsx"; SXSSFWorkbook xssfWorkbook1 = new SXSSFWorkbook(new XSSFWorkbook(file1)); Sheet xssfSheet1 = xssfWorkbook1.getXSSFWorkbook().getSheetAt(0); -// Cell cell1 = xssfSheet1.getRow(0).createCell(9); - + // Cell cell1 = xssfSheet1.getRow(0).createCell(9); xssfWorkbook.close(); xssfWorkbook1.close(); } - @Test public void testreadRead() throws IOException { String file = TestFileUtil.getPath() + "fill" + File.separator + "simple.xlsx"; diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiWriteTest.java b/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiWriteTest.java index 02e38c09..5c1620e2 100644 --- a/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiWriteTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiWriteTest.java @@ -5,6 +5,7 @@ import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; +import java.util.regex.Pattern; import org.apache.poi.ss.formula.functions.T; import org.apache.poi.xssf.streaming.SXSSFCell; @@ -44,6 +45,31 @@ public class PoiWriteTest { sxxsFWorkbook.write(fileOutputStream); } + private static final Pattern FILL_PATTERN = Pattern.compile("^.*?\\$\\{[^}]+}.*?$"); + + @Test + public void part() throws IOException { + LOGGER.info("test:{}", FILL_PATTERN.matcher("${name今年${number}岁了").matches()); + LOGGER.info("test:{}", FILL_PATTERN.matcher("${name}今年${number}岁了").matches()); + LOGGER.info("test:{}", FILL_PATTERN.matcher("${name}").matches()); + LOGGER.info("test:{}", FILL_PATTERN.matcher("${number}").matches()); + LOGGER.info("test:{}", FILL_PATTERN.matcher("${name}今年").matches()); + LOGGER.info("test:{}", FILL_PATTERN.matcher("今年${number}岁了").matches()); + LOGGER.info("test:{}", FILL_PATTERN.matcher("今年${number岁了").matches()); + LOGGER.info("test:{}", FILL_PATTERN.matcher("${}").matches()); + LOGGER.info("test:{}", FILL_PATTERN.matcher("胜多负少").matches()); + } + + private static final Pattern FILL_PATTERN2 = Pattern.compile("测试"); + + @Test + public void part2() throws IOException { + LOGGER.info("test:{}", FILL_PATTERN.matcher("我是测试呀").find()); + LOGGER.info("test:{}", FILL_PATTERN.matcher("测试u").matches()); + LOGGER.info("test:{}", FILL_PATTERN.matcher("我是测试").matches()); + + } + @Test public void test() throws Exception { Class clazz = TestCell.class; diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/simple/HgTest.java b/src/test/java/com/alibaba/easyexcel/test/temp/simple/HgTest.java index ad07b70d..2eb4d1ab 100644 --- a/src/test/java/com/alibaba/easyexcel/test/temp/simple/HgTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/temp/simple/HgTest.java @@ -27,7 +27,7 @@ public class HgTest { @Test public void hh() throws IOException { List list = - EasyExcel.read(new FileInputStream("D:\\test\\MRP生产视图(1).xlsx")).headRowNumber(0).sheet().doReadSync(); + EasyExcel.read(new FileInputStream("D:\\test\\201909301017rule.xlsx")).headRowNumber(2).sheet().doReadSync(); for (Object data : list) { LOGGER.info("返回数据:{}", JSON.toJSONString(data)); } diff --git a/src/test/resources/demo/fill/complex.xlsx b/src/test/resources/demo/fill/complex.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..537671369ce0724b61b2654322897087d55879ac GIT binary patch literal 10297 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 literal 0 HcmV?d00001 diff --git a/src/test/resources/demo/fill/complexFillWithTable.xlsx b/src/test/resources/demo/fill/complexFillWithTable.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..4de1a1e791e0835dd6fb91a3723f441f479b3665 GIT binary patch literal 10290 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 literal 0 HcmV?d00001 diff --git a/src/test/resources/demo/fill/horizontal.xlsx b/src/test/resources/demo/fill/horizontal.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..c8b4564eeab4fc95ffca5f3c78cfb9a86254f28e GIT binary patch literal 10307 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 literal 0 HcmV?d00001 diff --git a/src/test/resources/demo/fill/simple.xlsx b/src/test/resources/demo/fill/simple.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..7514d1dabf0766b7bfad8accca1878c176536c2d GIT binary patch literal 10135 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 AegFUf literal 0 HcmV?d00001 diff --git a/src/test/resources/fill/complex.xls b/src/test/resources/fill/complex.xls new file mode 100644 index 0000000000000000000000000000000000000000..d5758951c6e0838446ddd6f1ba70428b9a9c3e0a GIT binary patch literal 19456 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 AcmMzZ literal 0 HcmV?d00001 diff --git a/src/test/resources/fill/complex.xlsx b/src/test/resources/fill/complex.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..537671369ce0724b61b2654322897087d55879ac GIT binary patch literal 10297 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 literal 0 HcmV?d00001 diff --git a/src/test/resources/fill/horizontal.xls b/src/test/resources/fill/horizontal.xls new file mode 100644 index 0000000000000000000000000000000000000000..570f9012d725554874cbadae3fc45996d97f41b6 GIT binary patch literal 19968 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 K;2pC4o%esF<}~sE delta 4411 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 diff --git a/src/test/resources/large/fill.xlsx b/src/test/resources/large/fill.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..c3c376d31a6ba4920046118683ed0593934ca5de GIT binary patch literal 11475 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 literal 0 HcmV?d00001