From 05b7f623a55c2833d034657d51fb226f09742932 Mon Sep 17 00:00:00 2001 From: Bengbengbalabalabeng Date: Sun, 19 Apr 2026 11:41:23 +0800 Subject: [PATCH 1/3] fix: correct SheetWriteHandler.afterSheetDispose to avoid repeated execution --- .../fesod/sheet/write/ExcelBuilderImpl.java | 7 +- .../sheet/handler/CountingWriteHandler.java | 196 ++++++++++++++++++ .../fesod/sheet/handler/WriteHandlerTest.java | 135 +++++++++++- .../src/test/resources/fill/fillHandler03.xls | Bin 0 -> 19456 bytes .../test/resources/fill/fillHandler07.xlsx | Bin 0 -> 10292 bytes 5 files changed, 328 insertions(+), 10 deletions(-) create mode 100644 fesod-sheet/src/test/java/org/apache/fesod/sheet/handler/CountingWriteHandler.java create mode 100644 fesod-sheet/src/test/resources/fill/fillHandler03.xls create mode 100644 fesod-sheet/src/test/resources/fill/fillHandler07.xlsx diff --git a/fesod-sheet/src/main/java/org/apache/fesod/sheet/write/ExcelBuilderImpl.java b/fesod-sheet/src/main/java/org/apache/fesod/sheet/write/ExcelBuilderImpl.java index 97128f56c..1f5742a49 100644 --- a/fesod-sheet/src/main/java/org/apache/fesod/sheet/write/ExcelBuilderImpl.java +++ b/fesod-sheet/src/main/java/org/apache/fesod/sheet/write/ExcelBuilderImpl.java @@ -81,8 +81,6 @@ public void addContent(Collection data, WriteSheet writeSheet, WriteTable wri excelWriteAddExecutor = new ExcelWriteAddExecutor(context); } excelWriteAddExecutor.add(data); - // execute callback after the sheet is written - WriteHandlerUtils.afterSheetDispose(context); } catch (RuntimeException e) { finishOnException(); throw e; @@ -106,8 +104,6 @@ public void fill(Object data, FillConfig fillConfig, WriteSheet writeSheet) { excelWriteFillExecutor = new ExcelWriteFillExecutor(context); } excelWriteFillExecutor.fill(data, fillConfig); - // execute callback after the sheet is written - WriteHandlerUtils.afterSheetDispose(context); } catch (RuntimeException e) { finishOnException(); throw e; @@ -123,6 +119,9 @@ private void finishOnException() { @Override public void finish(boolean onException) { + // executes the callback after the current sheet has been fully written. + WriteHandlerUtils.afterSheetDispose(context); + if (context != null) { context.finish(onException); } diff --git a/fesod-sheet/src/test/java/org/apache/fesod/sheet/handler/CountingWriteHandler.java b/fesod-sheet/src/test/java/org/apache/fesod/sheet/handler/CountingWriteHandler.java new file mode 100644 index 000000000..2f2cefc91 --- /dev/null +++ b/fesod-sheet/src/test/java/org/apache/fesod/sheet/handler/CountingWriteHandler.java @@ -0,0 +1,196 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.fesod.sheet.handler; + +import java.util.List; +import org.apache.fesod.sheet.metadata.Head; +import org.apache.fesod.sheet.metadata.data.WriteCellData; +import org.apache.fesod.sheet.write.handler.CellWriteHandler; +import org.apache.fesod.sheet.write.handler.RowWriteHandler; +import org.apache.fesod.sheet.write.handler.SheetWriteHandler; +import org.apache.fesod.sheet.write.handler.WorkbookWriteHandler; +import org.apache.fesod.sheet.write.handler.context.SheetWriteHandlerContext; +import org.apache.fesod.sheet.write.metadata.holder.WriteSheetHolder; +import org.apache.fesod.sheet.write.metadata.holder.WriteTableHolder; +import org.apache.fesod.sheet.write.metadata.holder.WriteWorkbookHolder; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Row; +import org.junit.jupiter.api.Assertions; + +public class CountingWriteHandler + implements WorkbookWriteHandler, SheetWriteHandler, RowWriteHandler, CellWriteHandler { + + private final long headCount; + private final long dataCount; + + private long beforeCellCreate = 0L; + private long afterCellCreate = 0L; + private long afterCellDataConverted = 0L; + private long afterCellDispose = 0L; + private long beforeRowCreate = 0L; + private long afterRowCreate = 0L; + private long afterRowDispose = 0L; + private long beforeSheetCreate = 0L; + private long afterSheetCreate = 0L; + private long afterSheetDispose = 0L; + private long beforeWorkbookCreate = 0L; + private long afterWorkbookCreate = 0L; + private long afterWorkbookDispose = 0L; + + public CountingWriteHandler(long headCount, long dataCount) { + this.headCount = headCount; + this.dataCount = dataCount; + } + + @Override + public void beforeCellCreate( + WriteSheetHolder writeSheetHolder, + WriteTableHolder writeTableHolder, + Row row, + Head head, + Integer columnIndex, + Integer relativeRowIndex, + Boolean isHead) { + if (isHead) { + beforeCellCreate++; + } + } + + @Override + public void afterCellCreate( + WriteSheetHolder writeSheetHolder, + WriteTableHolder writeTableHolder, + Cell cell, + Head head, + Integer relativeRowIndex, + Boolean isHead) { + if (isHead) { + afterCellCreate++; + } + } + + @Override + public void afterCellDataConverted( + WriteSheetHolder writeSheetHolder, + WriteTableHolder writeTableHolder, + WriteCellData cellData, + Cell cell, + Head head, + Integer relativeRowIndex, + Boolean isHead) { + afterCellDataConverted++; + } + + @Override + public void afterCellDispose( + WriteSheetHolder writeSheetHolder, + WriteTableHolder writeTableHolder, + List> cellDataList, + Cell cell, + Head head, + Integer relativeRowIndex, + Boolean isHead) { + if (isHead) { + afterCellDispose++; + } + } + + @Override + public void beforeRowCreate( + WriteSheetHolder writeSheetHolder, + WriteTableHolder writeTableHolder, + Integer rowIndex, + Integer relativeRowIndex, + Boolean isHead) { + if (isHead) { + beforeRowCreate++; + } + } + + @Override + public void afterRowCreate( + WriteSheetHolder writeSheetHolder, + WriteTableHolder writeTableHolder, + Row row, + Integer relativeRowIndex, + Boolean isHead) { + if (isHead) { + afterRowCreate++; + } + } + + @Override + public void afterRowDispose( + WriteSheetHolder writeSheetHolder, + WriteTableHolder writeTableHolder, + Row row, + Integer relativeRowIndex, + Boolean isHead) { + if (isHead) { + afterRowDispose++; + } + } + + @Override + public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) { + beforeSheetCreate++; + } + + @Override + public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) { + afterSheetCreate++; + } + + @Override + public void beforeWorkbookCreate() { + beforeWorkbookCreate++; + } + + @Override + public void afterWorkbookCreate(WriteWorkbookHolder writeWorkbookHolder) { + afterWorkbookCreate++; + } + + @Override + public void afterWorkbookDispose(WriteWorkbookHolder writeWorkbookHolder) { + afterWorkbookDispose++; + } + + @Override + public void afterSheetDispose(SheetWriteHandlerContext context) { + afterSheetDispose++; + } + + public void afterAll() { + Assertions.assertEquals(headCount, beforeCellCreate); + Assertions.assertEquals(headCount, afterCellCreate); + Assertions.assertEquals(dataCount, afterCellDataConverted); + Assertions.assertEquals(headCount, afterCellDispose); + Assertions.assertEquals(headCount, beforeRowCreate); + Assertions.assertEquals(headCount, afterRowCreate); + Assertions.assertEquals(headCount, afterRowDispose); + Assertions.assertEquals(1L, beforeSheetCreate); + Assertions.assertEquals(1L, afterSheetCreate); + Assertions.assertEquals(1L, beforeWorkbookCreate); + Assertions.assertEquals(1L, afterWorkbookCreate); + Assertions.assertEquals(1L, afterWorkbookDispose); + Assertions.assertEquals(1L, afterSheetDispose); + } +} diff --git a/fesod-sheet/src/test/java/org/apache/fesod/sheet/handler/WriteHandlerTest.java b/fesod-sheet/src/test/java/org/apache/fesod/sheet/handler/WriteHandlerTest.java index 475c55669..554cd8a19 100644 --- a/fesod-sheet/src/test/java/org/apache/fesod/sheet/handler/WriteHandlerTest.java +++ b/fesod-sheet/src/test/java/org/apache/fesod/sheet/handler/WriteHandlerTest.java @@ -26,11 +26,19 @@ package org.apache.fesod.sheet.handler; import java.io.File; +import java.net.URISyntaxException; +import java.net.URL; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import org.apache.fesod.sheet.ExcelWriter; import org.apache.fesod.sheet.FesodSheet; import org.apache.fesod.sheet.util.TestFileUtil; -import org.junit.jupiter.api.BeforeAll; +import org.apache.fesod.sheet.write.metadata.WriteSheet; +import org.apache.fesod.sheet.write.metadata.WriteTable; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; @@ -41,15 +49,31 @@ @TestMethodOrder(MethodOrderer.MethodName.class) public class WriteHandlerTest { - private static File file07; - private static File file03; - private static File fileCsv; + private File file07; + private File file03; + private File fileCsv; - @BeforeAll - public static void init() { + private File fillTemplate07; + private File fillTemplate03; + private File fill07; + private File fill03; + + @BeforeEach + void init() throws Exception { file07 = TestFileUtil.createNewFile("writeHandler07.xlsx"); file03 = TestFileUtil.createNewFile("writeHandler03.xls"); fileCsv = TestFileUtil.createNewFile("writeHandlerCsv.csv"); + + fillTemplate07 = loadTemplate("fillHandler07.xlsx"); + fillTemplate03 = loadTemplate("fillHandler03.xls"); + fill07 = TestFileUtil.createNewFile("fill07.xlsx"); + fill03 = TestFileUtil.createNewFile("fill03.xls"); + } + + private File loadTemplate(String filename) throws URISyntaxException { + URL resource = getClass().getClassLoader().getResource("fill" + File.separator + filename); + Assertions.assertNotNull(resource); + return new File(resource.toURI()); } @Test @@ -97,6 +121,46 @@ public void t23TableWriteCsv() throws Exception { tableWrite(fileCsv); } + @Test + public void t31SheetWrite07() throws Exception { + writeSheetWithMultiWrites(file07); + } + + @Test + public void t32SheetWrite03() throws Exception { + writeSheetWithMultiWrites(file03); + } + + @Test + public void t33SheetWriteCsv() throws Exception { + writeSheetWithMultiWrites(fileCsv); + } + + @Test + public void t41TableWrite07() throws Exception { + writeTableWithMultiWrites(file07); + } + + @Test + public void t42TableWrite03() throws Exception { + writeTableWithMultiWrites(file03); + } + + @Test + public void t43TableWriteCsv() throws Exception { + writeTableWithMultiWrites(fileCsv); + } + + @Test + public void t51SheetFill07() throws Exception { + fillSheetWithMultiFills(fillTemplate07, fill07); + } + + @Test + public void t52SheetFill03() throws Exception { + fillSheetWithMultiFills(fillTemplate03, fill03); + } + private void workbookWrite(File file) { WriteHandler writeHandler = new WriteHandler(); FesodSheet.write(file) @@ -117,6 +181,24 @@ private void sheetWrite(File file) { writeHandler.afterAll(); } + private void writeSheetWithMultiWrites(File file) { + CountingWriteHandler writeHandler = new CountingWriteHandler(1L, 2L); + + try (ExcelWriter writer = + FesodSheet.write(file).head(WriteHandlerData.class).build()) { + + WriteSheet writeSheet = FesodSheet.writerSheet() + .needHead(Boolean.TRUE) + .registerWriteHandler(writeHandler) + .build(); + + writer.write(data(), writeSheet); + writer.write(data(), writeSheet); + } + + writeHandler.afterAll(); + } + private void tableWrite(File file) { WriteHandler writeHandler = new WriteHandler(); FesodSheet.write(file) @@ -128,6 +210,47 @@ private void tableWrite(File file) { writeHandler.afterAll(); } + private void writeTableWithMultiWrites(File file) { + CountingWriteHandler writeHandler = new CountingWriteHandler(2L, 2L); + + try (ExcelWriter writer = + FesodSheet.write(file).head(WriteHandlerData.class).build()) { + + WriteSheet writeSheet = FesodSheet.writerSheet() + .needHead(Boolean.FALSE) + .registerWriteHandler(writeHandler) + .build(); + WriteTable table1 = FesodSheet.writerTable(0).needHead(Boolean.TRUE).build(); + WriteTable table2 = FesodSheet.writerTable(1).needHead(Boolean.TRUE).build(); + + writer.write(data(), writeSheet, table1); + writer.write(data(), writeSheet, table2); + } + + writeHandler.afterAll(); + } + + private void fillSheetWithMultiFills(File template, File file) { + CountingWriteHandler writeHandler = new CountingWriteHandler(0L, 4L); + + try (ExcelWriter writer = FesodSheet.write(file).withTemplate(template).build()) { + + WriteSheet writeSheet = + FesodSheet.writerSheet().registerWriteHandler(writeHandler).build(); + + Map data1 = new HashMap<>(); + data1.put("name", "Tom"); + + Map data2 = new HashMap<>(); + data2.put("code", "Custom Code"); + + writer.fill(data1, writeSheet); + writer.fill(data2, writeSheet); + } + + writeHandler.afterAll(); + } + private List data() { List list = new ArrayList(); for (int i = 0; i < 1; i++) { diff --git a/fesod-sheet/src/test/resources/fill/fillHandler03.xls b/fesod-sheet/src/test/resources/fill/fillHandler03.xls new file mode 100644 index 0000000000000000000000000000000000000000..44305a77f00dc33dfd57b07845eca07516ce71a3 GIT binary patch literal 19456 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 F;2-R4TKfP1 literal 0 HcmV?d00001 diff --git a/fesod-sheet/src/test/resources/fill/fillHandler07.xlsx b/fesod-sheet/src/test/resources/fill/fillHandler07.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..9ed3b2d7dd0ea644622557e9dcf20a485f131a44 GIT binary patch literal 10292 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%0D7hM$YB-*dFPT9D+Pr>C5J74p zzKcdX1G$VwyCSlHCUj^6j}-5HaD^~2E3%#^KMS&+mySMVbY%AQx@P%Xs{v_!Hk*U> zI%e=>uRFKUW%)QswN}@MPS%2_NOxmv^kM*bB67(8_si(Un)8MHYK2qNfGpi~86c{G zuce_NY3m`Oh!K;nlG(PnIp+IgI(1Fc3R2L8&phCQjglofdgr^irm*GAyPd3fC$nV! zVo!cz3<$F_n08f4)b~M#ytac0=A$Fot`0qTQoM_ zetbWgRYWa}=S+b|o9*LyALHeJGB7EFPuAvlKNQ1Y(}6I8DyhG_r(F=sxkVQN=11USTs-3NtQ{@Vhs7yxoX%rk=sI>v70>;;=THp!ixyzUo}w;CafurT0*qPW zlx8I{lb98iBopph%$aNxSt|pgH^1R)PFk@U{Bf0jHH;GG`*Uh>ghZ^9IgL}q7a4R7 zb%*o0(T5m>+ZoAt%oGh>%<$meZcuS_pyydq)L>0BSv^$f*#y#NOwYVn$Rrqd79E|`E?30^*be#-Q>QI%dn(2&i8xs` zTYvv6X8h;8A2_cE@!An!^R!Ck#qZks0RC+iT&Y6-1BUexvI1NK=( zcV?sfu4L-tpQ89MMvEPmfy;jgQlT9H0(QJTJ}y&Zi521g%cN8spAa1)y6nOg%}bn$G>T)h-VA&NEHi+ zy`c)^1=*?C4W3H(W|OKY3HPiphIzHXIq(O!UIT%p`y*>2srk=GxtO*z;xb)qVsWDa zz4PbVG0vsn-~>h;T||eD+~h;cE6i9>rSyYS)FYO@>bE;Er}pV9lSRTmZzX$(^1POHJY^|!dyK#TS(g9Gq5hLNrQE|@Zoy)l0vA#U z!B6ll^f%aFFKoZ03rgs)>S9L;ISOnG9QSdEw*w>!f}$j<^e!N1RX4`E(UKc@fMN*} z>5>Dix%gdd>kkji>&y33^&3PYJR^*K^v^(-#?%#6=sjj}69 zU61Ch(BAiRi-fP4$ORZ5eo|92uAancBDLMs7;#Gr{89hi#bJtPkoz6q#7hzKmQ$Qi zwE(e1B8=;?(5(lMfhG@ZTk}TQ^C3P;>XWe$_%QujLfww+cTiOQizlOzy+j8!KaI$W zgbGuVcZ{-3_@9+$i zwWr)YsXYNbU@4uzw_6ghi9Xh|^_~OmoB?*uuQfdE0Zs;wH{ANfA-iAq_M{v1xKFqo zDkY_iP?4g^ENb828!v)hbfbHq1h*e`i_>7hPK+s0_hoq<>WjhU7gol@x-(Y{=&&8s z!bH`Ta!*G2C>jc0GIx+iVI}dz*5Hy8p;lGFB_u&BepoQ-jlfuf-l0bxgHqrBJf>&| z!V3uT9n6g7;(%Q_ay@FSqxDD3RI#pJUqf`{sFFNwEm*atBV{xl*z`0W9!#;XWGqD3 zN@R;#$am%qN@Uz5WR?%*GYvcm4D#G|`&@Jwop)0B)|A=X9XHL!9`JQeFEj(Cuul_h zuFk_yo;q?FJL-;5EMH+VfWm&RfOop8!Ey?JpOK5xwqY0NUDEOPi}jnA8(W(ED}ouIA&ci$%$ zbgk!n7ZN>7r7TC*`dB0vd`c}>&nx4a+v%6aWF9&Leoprt_}#9ZIFR{5t0+K1;rvRZ z{O+=OswJ?ZARr;g!SfEqKkDo^fq(vh9-Yze@z}5`a9F7&gl{7H&ehLIKMuo z|Hka`YyCYQnLUobG!p%t-_us2N8sQ5=>CfKug0RMB6`{u^CxyH*u4K>`tK&0r|3`n zI{rk*2RoBL(f{c3_-~sAWBhA8ZrTx?I0Ad3|Az2)DMge2#dSSh=u;o}$mq9}>i&xM zA0F>1ou?VQKk2Yy{d4~ADTSv=kv}P*;{K-aM~dWszjrzCG5AO6>nX>lHL^cB+W!^p z&x+Yo0#9>!e-c0hJ3lbrzl!a@E&8$j?f-%M zXLHKa+n?{2$Bz*9v|;ezgt!@2R{3+u^^2@K-9os?jP6u)iD!Hu#u$+;1e+ HBlrISND_>j literal 0 HcmV?d00001 From da6bc2b8347780836d7834b7dcde857ff3dc30d6 Mon Sep 17 00:00:00 2001 From: Bengbengbalabalabeng Date: Tue, 21 Apr 2026 21:50:23 +0800 Subject: [PATCH 2/3] fix: adjust afterSheetDispose execution position --- .../java/org/apache/fesod/sheet/write/ExcelBuilderImpl.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fesod-sheet/src/main/java/org/apache/fesod/sheet/write/ExcelBuilderImpl.java b/fesod-sheet/src/main/java/org/apache/fesod/sheet/write/ExcelBuilderImpl.java index 1f5742a49..882c29faf 100644 --- a/fesod-sheet/src/main/java/org/apache/fesod/sheet/write/ExcelBuilderImpl.java +++ b/fesod-sheet/src/main/java/org/apache/fesod/sheet/write/ExcelBuilderImpl.java @@ -119,10 +119,10 @@ private void finishOnException() { @Override public void finish(boolean onException) { - // executes the callback after the current sheet has been fully written. - WriteHandlerUtils.afterSheetDispose(context); - if (context != null) { + // executes the callback after the current sheet has been fully written. + WriteHandlerUtils.afterSheetDispose(context); + context.finish(onException); } } From 591af366ceb0e74497b97de21f358d2d5fb0b0b9 Mon Sep 17 00:00:00 2001 From: Bengbengbalabalabeng Date: Fri, 1 May 2026 20:52:10 +0800 Subject: [PATCH 3/3] fix: trigger afterSheetDispose for each sheet in multi-sheet write --- .../fesod/sheet/context/WriteContextImpl.java | 34 +++ .../fesod/sheet/util/WriteHandlerUtils.java | 3 +- .../fesod/sheet/write/ExcelBuilderImpl.java | 4 - .../sheet/handler/CountingWriteHandler.java | 174 +++++++++-- .../fesod/sheet/handler/WriteHandlerTest.java | 278 +++++++++++++++++- .../sheet/util/WriteHandlerUtilsTest.java | 8 +- .../src/test/resources/fill/fillHandler03.xls | Bin 19456 -> 19456 bytes .../test/resources/fill/fillHandler07.xlsx | Bin 10292 -> 10407 bytes 8 files changed, 471 insertions(+), 30 deletions(-) diff --git a/fesod-sheet/src/main/java/org/apache/fesod/sheet/context/WriteContextImpl.java b/fesod-sheet/src/main/java/org/apache/fesod/sheet/context/WriteContextImpl.java index 5a1768606..2e2f8b9c5 100644 --- a/fesod-sheet/src/main/java/org/apache/fesod/sheet/context/WriteContextImpl.java +++ b/fesod-sheet/src/main/java/org/apache/fesod/sheet/context/WriteContextImpl.java @@ -28,9 +28,13 @@ import java.io.File; import java.io.FileOutputStream; import java.io.OutputStream; +import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.UUID; +import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.MapUtils; import org.apache.fesod.common.util.ListUtils; import org.apache.fesod.common.util.StringUtils; import org.apache.fesod.sheet.enums.HeaderMergeStrategy; @@ -488,6 +492,36 @@ public void finish(boolean onException) { return; } finished = true; + + // executes the callback after all sheets has been fully written. + Map writeSheetHolderMap = writeWorkbookHolder.getHasBeenInitializedSheetIndexMap(); + if (MapUtils.isNotEmpty(writeSheetHolderMap)) { + if (MapUtils.size(writeSheetHolderMap) == 1) { + SheetWriteHandlerContext sheetWriteHandlerContext = + WriteHandlerUtils.createSheetWriteHandlerContext(this); + WriteHandlerUtils.afterSheetDispose(sheetWriteHandlerContext); + } else { + List sheetNos = writeSheetHolderMap.keySet().stream() + .filter(Objects::nonNull) + .sorted() + .collect(Collectors.toList()); + + for (Integer sheetNo : sheetNos) { + WriteSheetHolder holder = writeSheetHolderMap.get(sheetNo); + if (Objects.nonNull(holder)) { + // switch context + this.writeSheetHolder = holder; + this.writeTableHolder = null; + this.currentWriteHolder = holder; + + SheetWriteHandlerContext sheetWriteHandlerContext = + WriteHandlerUtils.createSheetWriteHandlerContext(this); + WriteHandlerUtils.afterSheetDispose(sheetWriteHandlerContext); + } + } + } + } + WriteHandlerUtils.afterWorkbookDispose(writeWorkbookHolder.getWorkbookWriteHandlerContext()); if (writeWorkbookHolder == null) { return; diff --git a/fesod-sheet/src/main/java/org/apache/fesod/sheet/util/WriteHandlerUtils.java b/fesod-sheet/src/main/java/org/apache/fesod/sheet/util/WriteHandlerUtils.java index c616d6e1a..e6b88be4c 100644 --- a/fesod-sheet/src/main/java/org/apache/fesod/sheet/util/WriteHandlerUtils.java +++ b/fesod-sheet/src/main/java/org/apache/fesod/sheet/util/WriteHandlerUtils.java @@ -230,8 +230,7 @@ public static void afterRowDispose(RowWriteHandlerContext context) { } } - public static void afterSheetDispose(WriteContext writeContext) { - SheetWriteHandlerContext context = WriteHandlerUtils.createSheetWriteHandlerContext(writeContext); + public static void afterSheetDispose(SheetWriteHandlerContext context) { SheetHandlerExecutionChain sheetHandlerExecutionChain = getSheetHandlerExecutionChain(context, false); if (sheetHandlerExecutionChain != null) { sheetHandlerExecutionChain.afterSheetDispose(context); diff --git a/fesod-sheet/src/main/java/org/apache/fesod/sheet/write/ExcelBuilderImpl.java b/fesod-sheet/src/main/java/org/apache/fesod/sheet/write/ExcelBuilderImpl.java index 882c29faf..e1b4d8f32 100644 --- a/fesod-sheet/src/main/java/org/apache/fesod/sheet/write/ExcelBuilderImpl.java +++ b/fesod-sheet/src/main/java/org/apache/fesod/sheet/write/ExcelBuilderImpl.java @@ -32,7 +32,6 @@ import org.apache.fesod.sheet.exception.ExcelGenerateException; import org.apache.fesod.sheet.support.ExcelTypeEnum; import org.apache.fesod.sheet.util.FileUtils; -import org.apache.fesod.sheet.util.WriteHandlerUtils; import org.apache.fesod.sheet.write.executor.ExcelWriteAddExecutor; import org.apache.fesod.sheet.write.executor.ExcelWriteFillExecutor; import org.apache.fesod.sheet.write.metadata.WriteSheet; @@ -120,9 +119,6 @@ private void finishOnException() { @Override public void finish(boolean onException) { if (context != null) { - // executes the callback after the current sheet has been fully written. - WriteHandlerUtils.afterSheetDispose(context); - context.finish(onException); } } diff --git a/fesod-sheet/src/test/java/org/apache/fesod/sheet/handler/CountingWriteHandler.java b/fesod-sheet/src/test/java/org/apache/fesod/sheet/handler/CountingWriteHandler.java index 2f2cefc91..ec82bdbba 100644 --- a/fesod-sheet/src/test/java/org/apache/fesod/sheet/handler/CountingWriteHandler.java +++ b/fesod-sheet/src/test/java/org/apache/fesod/sheet/handler/CountingWriteHandler.java @@ -19,7 +19,9 @@ package org.apache.fesod.sheet.handler; +import java.util.ArrayList; import java.util.List; +import org.apache.commons.collections4.CollectionUtils; import org.apache.fesod.sheet.metadata.Head; import org.apache.fesod.sheet.metadata.data.WriteCellData; import org.apache.fesod.sheet.write.handler.CellWriteHandler; @@ -37,9 +39,6 @@ public class CountingWriteHandler implements WorkbookWriteHandler, SheetWriteHandler, RowWriteHandler, CellWriteHandler { - private final long headCount; - private final long dataCount; - private long beforeCellCreate = 0L; private long afterCellCreate = 0L; private long afterCellDataConverted = 0L; @@ -50,13 +49,19 @@ public class CountingWriteHandler private long beforeSheetCreate = 0L; private long afterSheetCreate = 0L; private long afterSheetDispose = 0L; + private List afterSheetDisposeSheetNos = new ArrayList<>(); private long beforeWorkbookCreate = 0L; private long afterWorkbookCreate = 0L; private long afterWorkbookDispose = 0L; - public CountingWriteHandler(long headCount, long dataCount) { - this.headCount = headCount; - this.dataCount = dataCount; + private final HookInvocationCounter counter; + + private CountingWriteHandler(HookInvocationCounter counter) { + this.counter = counter; + } + + public static HookInvocationCounter builder() { + return new HookInvocationCounter(); } @Override @@ -176,21 +181,152 @@ public void afterWorkbookDispose(WriteWorkbookHolder writeWorkbookHolder) { @Override public void afterSheetDispose(SheetWriteHandlerContext context) { afterSheetDispose++; + + afterSheetDisposeSheetNos.add(context.getWriteSheetHolder().getSheetNo()); } public void afterAll() { - Assertions.assertEquals(headCount, beforeCellCreate); - Assertions.assertEquals(headCount, afterCellCreate); - Assertions.assertEquals(dataCount, afterCellDataConverted); - Assertions.assertEquals(headCount, afterCellDispose); - Assertions.assertEquals(headCount, beforeRowCreate); - Assertions.assertEquals(headCount, afterRowCreate); - Assertions.assertEquals(headCount, afterRowDispose); - Assertions.assertEquals(1L, beforeSheetCreate); - Assertions.assertEquals(1L, afterSheetCreate); - Assertions.assertEquals(1L, beforeWorkbookCreate); - Assertions.assertEquals(1L, afterWorkbookCreate); - Assertions.assertEquals(1L, afterWorkbookDispose); - Assertions.assertEquals(1L, afterSheetDispose); + Assertions.assertEquals( + counter.beforeCellCreate, beforeCellCreate, "beforeCellCreate executes an unexpected number of times"); + Assertions.assertEquals( + counter.afterCellCreate, afterCellCreate, "'afterCellCreate' executes an unexpected number of times"); + Assertions.assertEquals( + counter.afterCellDataConverted, + afterCellDataConverted, + "'afterCellDataConverted' executes an unexpected number of times"); + Assertions.assertEquals( + counter.afterCellDispose, + afterCellDispose, + "'afterCellDispose' executes an unexpected number of times"); + Assertions.assertEquals( + counter.beforeRowCreate, beforeRowCreate, "'beforeRowCreate' executes an unexpected number of times"); + Assertions.assertEquals( + counter.afterRowCreate, afterRowCreate, "'afterRowCreate' executes an unexpected number of times"); + Assertions.assertEquals( + counter.afterRowDispose, afterRowDispose, "'afterRowDispose' executes an unexpected number of times"); + Assertions.assertEquals( + counter.beforeSheetCreate, + beforeSheetCreate, + "'beforeSheetCreate' executes an unexpected number of times"); + Assertions.assertEquals( + counter.afterSheetCreate, + afterSheetCreate, + "'afterSheetCreate' executes an unexpected number of times"); + Assertions.assertEquals( + counter.beforeWorkbookCreate, + beforeWorkbookCreate, + "'beforeWorkbookCreate' executes an unexpected number of times"); + Assertions.assertEquals( + counter.afterWorkbookCreate, + afterWorkbookCreate, + "'afterWorkbookCreate' executes an unexpected number of times"); + Assertions.assertEquals( + counter.afterWorkbookDispose, + afterWorkbookDispose, + "'afterWorkbookDispose' executes an unexpected number of times"); + Assertions.assertEquals( + counter.afterSheetDispose, + afterSheetDispose, + "'afterSheetDispose' executes an unexpected number of times"); + + if (CollectionUtils.isNotEmpty(counter.afterSheetDisposeSheetNos)) { + Assertions.assertIterableEquals( + counter.afterSheetDisposeSheetNos, + afterSheetDisposeSheetNos, + "'afterSheetDisposeSheetNos' does not match"); + } + } + + public static class HookInvocationCounter { + private long beforeCellCreate = 0L; + private long afterCellCreate = 0L; + private long afterCellDataConverted = 0L; + private long afterCellDispose = 0L; + private long beforeRowCreate = 0L; + private long afterRowCreate = 0L; + private long afterRowDispose = 0L; + private long beforeSheetCreate = 0L; + private long afterSheetCreate = 0L; + private long afterSheetDispose = 0L; + private List afterSheetDisposeSheetNos; + private long beforeWorkbookCreate = 0L; + private long afterWorkbookCreate = 0L; + private long afterWorkbookDispose = 0L; + + private HookInvocationCounter() {} + + public HookInvocationCounter withBeforeCellCreate(long beforeCellCreate) { + this.beforeCellCreate = beforeCellCreate; + return this; + } + + public HookInvocationCounter withAfterCellCreate(long afterCellCreate) { + this.afterCellCreate = afterCellCreate; + return this; + } + + public HookInvocationCounter withAfterCellDataConverted(long afterCellDataConverted) { + this.afterCellDataConverted = afterCellDataConverted; + return this; + } + + public HookInvocationCounter withAfterCellDispose(long afterCellDispose) { + this.afterCellDispose = afterCellDispose; + return this; + } + + public HookInvocationCounter withBeforeRowCreate(long beforeRowCreate) { + this.beforeRowCreate = beforeRowCreate; + return this; + } + + public HookInvocationCounter withAfterRowCreate(long afterRowCreate) { + this.afterRowCreate = afterRowCreate; + return this; + } + + public HookInvocationCounter withAfterRowDispose(long afterRowDispose) { + this.afterRowDispose = afterRowDispose; + return this; + } + + public HookInvocationCounter withBeforeSheetCreate(long beforeSheetCreate) { + this.beforeSheetCreate = beforeSheetCreate; + return this; + } + + public HookInvocationCounter withAfterSheetCreate(long afterSheetCreate) { + this.afterSheetCreate = afterSheetCreate; + return this; + } + + public HookInvocationCounter withBeforeWorkbookCreate(long beforeWorkbookCreate) { + this.beforeWorkbookCreate = beforeWorkbookCreate; + return this; + } + + public HookInvocationCounter withAfterWorkbookCreate(long afterWorkbookCreate) { + this.afterWorkbookCreate = afterWorkbookCreate; + return this; + } + + public HookInvocationCounter withAfterWorkbookDispose(long afterWorkbookDispose) { + this.afterWorkbookDispose = afterWorkbookDispose; + return this; + } + + public HookInvocationCounter withAfterSheetDispose(long afterSheetDispose) { + this.afterSheetDispose = afterSheetDispose; + return this; + } + + public HookInvocationCounter withAfterSheetDisposeSheetNos(List afterSheetDisposeSheetNos) { + this.afterSheetDisposeSheetNos = afterSheetDisposeSheetNos; + return this; + } + + public CountingWriteHandler build() { + return new CountingWriteHandler(this); + } } } diff --git a/fesod-sheet/src/test/java/org/apache/fesod/sheet/handler/WriteHandlerTest.java b/fesod-sheet/src/test/java/org/apache/fesod/sheet/handler/WriteHandlerTest.java index 554cd8a19..5fae91626 100644 --- a/fesod-sheet/src/test/java/org/apache/fesod/sheet/handler/WriteHandlerTest.java +++ b/fesod-sheet/src/test/java/org/apache/fesod/sheet/handler/WriteHandlerTest.java @@ -29,6 +29,8 @@ import java.net.URISyntaxException; import java.net.URL; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -161,6 +163,46 @@ public void t52SheetFill03() throws Exception { fillSheetWithMultiFills(fillTemplate03, fill03); } + @Test + public void t61MultiSheetWrite07() throws Exception { + writeMultiSheet(file07); + } + + @Test + public void t62MultiSheetWrite03() throws Exception { + writeMultiSheet(file03); + } + + @Test + public void t71MultiSheetTableWrite07() throws Exception { + writeTableWithMultiSheetAndWrites(file07); + } + + @Test + public void t72MultiSheetTableWrite03() throws Exception { + writeTableWithMultiSheetAndWrites(file03); + } + + @Test + public void t81MultiSheetFill07() throws Exception { + fillMultiSheet(fillTemplate07, file07); + } + + @Test + public void t82MultiSheetFill03() throws Exception { + fillMultiSheet(fillTemplate03, file03); + } + + @Test + public void t91MultiSheetWithSheetLevelHandler07() throws Exception { + writeMultiSheetWithSheetLevelHandler(file07); + } + + @Test + public void t92MultiSheetWithSheetLevelHandler03() throws Exception { + writeMultiSheetWithSheetLevelHandler(file03); + } + private void workbookWrite(File file) { WriteHandler writeHandler = new WriteHandler(); FesodSheet.write(file) @@ -182,7 +224,22 @@ private void sheetWrite(File file) { } private void writeSheetWithMultiWrites(File file) { - CountingWriteHandler writeHandler = new CountingWriteHandler(1L, 2L); + CountingWriteHandler writeHandler = CountingWriteHandler.builder() + .withBeforeCellCreate(1L) + .withAfterCellCreate(1L) + .withAfterCellDataConverted(2L) + .withAfterCellDispose(1L) + .withBeforeRowCreate(1L) + .withAfterRowCreate(1L) + .withAfterRowDispose(1L) + .withBeforeSheetCreate(1L) + .withAfterSheetCreate(1L) + .withAfterSheetDispose(1L) + .withAfterSheetDisposeSheetNos(Collections.singletonList(0)) + .withBeforeWorkbookCreate(1L) + .withAfterWorkbookCreate(1L) + .withAfterWorkbookDispose(1L) + .build(); try (ExcelWriter writer = FesodSheet.write(file).head(WriteHandlerData.class).build()) { @@ -211,7 +268,22 @@ private void tableWrite(File file) { } private void writeTableWithMultiWrites(File file) { - CountingWriteHandler writeHandler = new CountingWriteHandler(2L, 2L); + CountingWriteHandler writeHandler = CountingWriteHandler.builder() + .withBeforeCellCreate(2L) + .withAfterCellCreate(2L) + .withAfterCellDataConverted(2L) + .withAfterCellDispose(2L) + .withBeforeRowCreate(2L) + .withAfterRowCreate(2L) + .withAfterRowDispose(2L) + .withBeforeSheetCreate(1L) + .withAfterSheetCreate(1L) + .withAfterSheetDispose(1L) + .withAfterSheetDisposeSheetNos(Collections.singletonList(0)) + .withBeforeWorkbookCreate(1L) + .withAfterWorkbookCreate(1L) + .withAfterWorkbookDispose(1L) + .build(); try (ExcelWriter writer = FesodSheet.write(file).head(WriteHandlerData.class).build()) { @@ -230,8 +302,74 @@ private void writeTableWithMultiWrites(File file) { writeHandler.afterAll(); } + private void writeTableWithMultiSheetAndWrites(File file) { + CountingWriteHandler writeHandler = CountingWriteHandler.builder() + .withBeforeCellCreate(4L) + .withAfterCellCreate(4L) + .withAfterCellDataConverted(4L) + .withAfterCellDispose(4L) + .withBeforeRowCreate(4L) + .withAfterRowCreate(4L) + .withAfterRowDispose(4L) + .withBeforeSheetCreate(2L) + .withAfterSheetCreate(2L) + .withAfterSheetDispose(2L) + .withAfterSheetDisposeSheetNos(Arrays.asList(0, 1)) + .withBeforeWorkbookCreate(1L) + .withAfterWorkbookCreate(1L) + .withAfterWorkbookDispose(1L) + .build(); + + try (ExcelWriter writer = FesodSheet.write(file) + .head(WriteHandlerData.class) + .registerWriteHandler(writeHandler) + .build()) { + + WriteSheet writeSheet1 = + FesodSheet.writerSheet(0).needHead(Boolean.FALSE).build(); + + writer.write( + data(), + writeSheet1, + FesodSheet.writerTable(0).needHead(Boolean.TRUE).build()); + writer.write( + data(), + writeSheet1, + FesodSheet.writerTable(1).needHead(Boolean.TRUE).build()); + + WriteSheet writeSheet2 = + FesodSheet.writerSheet(1).needHead(Boolean.FALSE).build(); + + writer.write( + data(), + writeSheet2, + FesodSheet.writerTable(0).needHead(Boolean.TRUE).build()); + writer.write( + data(), + writeSheet2, + FesodSheet.writerTable(1).needHead(Boolean.TRUE).build()); + } + + writeHandler.afterAll(); + } + private void fillSheetWithMultiFills(File template, File file) { - CountingWriteHandler writeHandler = new CountingWriteHandler(0L, 4L); + CountingWriteHandler writeHandler = CountingWriteHandler.builder() + .withBeforeCellCreate(0L) + .withAfterCellCreate(0L) + .withAfterCellDataConverted(4L) + .withAfterCellDispose(0L) + .withBeforeRowCreate(0L) + .withAfterRowCreate(0L) + .withAfterRowDispose(0L) + .withBeforeSheetCreate(1L) + .withAfterSheetCreate(1L) + .withAfterSheetDispose(1L) + .withAfterSheetDisposeSheetNos(Collections.singletonList(0)) + .withBeforeWorkbookCreate(1L) + .withAfterWorkbookCreate(1L) + .withAfterWorkbookDispose(1L) + .build(); try (ExcelWriter writer = FesodSheet.write(file).withTemplate(template).build()) { @@ -251,6 +389,140 @@ private void fillSheetWithMultiFills(File template, File file) { writeHandler.afterAll(); } + private void writeMultiSheet(File file) { + CountingWriteHandler writeHandler = CountingWriteHandler.builder() + .withBeforeCellCreate(2L) + .withAfterCellCreate(2L) + .withAfterCellDataConverted(2L) + .withAfterCellDispose(2L) + .withBeforeRowCreate(2L) + .withAfterRowCreate(2L) + .withAfterRowDispose(2L) + .withBeforeSheetCreate(2L) + .withAfterSheetCreate(2L) + .withAfterSheetDispose(2L) + .withAfterSheetDisposeSheetNos(Arrays.asList(0, 1)) + .withBeforeWorkbookCreate(1L) + .withAfterWorkbookCreate(1L) + .withAfterWorkbookDispose(1L) + .build(); + + try (ExcelWriter writer = FesodSheet.write(file) + .head(WriteHandlerData.class) + .registerWriteHandler(writeHandler) + .build()) { + + WriteSheet writeSheet1 = + FesodSheet.writerSheet(0).needHead(Boolean.TRUE).build(); + writer.write(data(), writeSheet1); + + WriteSheet writeSheet2 = + FesodSheet.writerSheet(1).needHead(Boolean.TRUE).build(); + writer.write(data(), writeSheet2); + } + + writeHandler.afterAll(); + } + + private void fillMultiSheet(File template, File file) { + CountingWriteHandler writeHandler = CountingWriteHandler.builder() + .withBeforeCellCreate(0L) + .withAfterCellCreate(0L) + .withAfterCellDataConverted(8L) + .withAfterCellDispose(0L) + .withBeforeRowCreate(0L) + .withAfterRowCreate(0L) + .withAfterRowDispose(0L) + .withBeforeSheetCreate(2L) + .withAfterSheetCreate(2L) + .withAfterSheetDispose(2L) + .withAfterSheetDisposeSheetNos(Arrays.asList(0, 1)) + .withBeforeWorkbookCreate(1L) + .withAfterWorkbookCreate(1L) + .withAfterWorkbookDispose(1L) + .build(); + + try (ExcelWriter writer = FesodSheet.write(file) + .withTemplate(template) + .registerWriteHandler(writeHandler) + .build()) { + + WriteSheet writeSheet0 = FesodSheet.writerSheet(0).build(); + Map data1 = new HashMap<>(); + data1.put("name", "Tom"); + Map data2 = new HashMap<>(); + data2.put("code", "Code1"); + writer.fill(data1, writeSheet0); + writer.fill(data2, writeSheet0); + + WriteSheet writeSheet1 = FesodSheet.writerSheet(1).build(); + Map data3 = new HashMap<>(); + data3.put("name", "Jerry"); + Map data4 = new HashMap<>(); + data4.put("code", "Code2"); + writer.fill(data3, writeSheet1); + writer.fill(data4, writeSheet1); + } + + writeHandler.afterAll(); + } + + private void writeMultiSheetWithSheetLevelHandler(File file) { + CountingWriteHandler writeHandler1 = CountingWriteHandler.builder() + .withBeforeCellCreate(1L) + .withAfterCellCreate(1L) + .withAfterCellDataConverted(1L) + .withAfterCellDispose(1L) + .withBeforeRowCreate(1L) + .withAfterRowCreate(1L) + .withAfterRowDispose(1L) + .withBeforeSheetCreate(1L) + .withAfterSheetCreate(1L) + .withAfterSheetDispose(1L) + .withAfterSheetDisposeSheetNos(Collections.singletonList(0)) + .withBeforeWorkbookCreate(1L) + .withAfterWorkbookCreate(1L) + // The data has not been fully written yet + .withAfterWorkbookDispose(0L) + .build(); + + CountingWriteHandler writeHandler2 = CountingWriteHandler.builder() + .withBeforeCellCreate(1L) + .withAfterCellCreate(1L) + .withAfterCellDataConverted(1L) + .withAfterCellDispose(1L) + .withBeforeRowCreate(1L) + .withAfterRowCreate(1L) + .withAfterRowDispose(1L) + .withBeforeSheetCreate(1L) + .withAfterSheetCreate(1L) + .withAfterSheetDispose(1L) + .withAfterSheetDisposeSheetNos(Collections.singletonList(1)) + .withBeforeWorkbookCreate(1L) + .withAfterWorkbookCreate(1L) + .withAfterWorkbookDispose(1L) + .build(); + + try (ExcelWriter writer = + FesodSheet.write(file).head(WriteHandlerData.class).build()) { + + WriteSheet writeSheet1 = FesodSheet.writerSheet(0) + .needHead(Boolean.TRUE) + .registerWriteHandler(writeHandler1) + .build(); + writer.write(data(), writeSheet1); + + WriteSheet writeSheet2 = FesodSheet.writerSheet(1) + .needHead(Boolean.TRUE) + .registerWriteHandler(writeHandler2) + .build(); + writer.write(data(), writeSheet2); + } + + writeHandler1.afterAll(); + writeHandler2.afterAll(); + } + private List data() { List list = new ArrayList(); for (int i = 0; i < 1; i++) { diff --git a/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/WriteHandlerUtilsTest.java b/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/WriteHandlerUtilsTest.java index 737c5f164..75819218f 100644 --- a/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/WriteHandlerUtilsTest.java +++ b/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/WriteHandlerUtilsTest.java @@ -248,17 +248,21 @@ void test_afterSheetCreate_chain_null() { @Test void test_afterSheetDispose_execution() { + SheetWriteHandlerContext context = Mockito.mock(SheetWriteHandlerContext.class); + Mockito.when(context.getWriteContext()).thenReturn(writeContext); Mockito.when(abstractWriteHolder.getSheetHandlerExecutionChain()).thenReturn(sheetChain); - Assertions.assertDoesNotThrow(() -> WriteHandlerUtils.afterSheetDispose(writeContext)); + Assertions.assertDoesNotThrow(() -> WriteHandlerUtils.afterSheetDispose(context)); Mockito.verify(sheetChain).afterSheetDispose(ArgumentMatchers.any(SheetWriteHandlerContext.class)); } @Test void test_afterSheetDispose_chain_null() { + SheetWriteHandlerContext context = Mockito.mock(SheetWriteHandlerContext.class); + Mockito.when(context.getWriteContext()).thenReturn(writeContext); Mockito.when(abstractWriteHolder.getSheetHandlerExecutionChain()).thenReturn(null); - Assertions.assertDoesNotThrow(() -> WriteHandlerUtils.afterSheetDispose(writeContext)); + Assertions.assertDoesNotThrow(() -> WriteHandlerUtils.afterSheetDispose(context)); } @Test diff --git a/fesod-sheet/src/test/resources/fill/fillHandler03.xls b/fesod-sheet/src/test/resources/fill/fillHandler03.xls index 44305a77f00dc33dfd57b07845eca07516ce71a3..176acb9d57fd9c1419aa3c84afec9f5757a36d88 100644 GIT binary patch delta 181 zcmZpe!Pqc^al;KJ_J)ECV+#hx&G(tQjG4?WCuf_vF>-9)WTwM3`J1%~3nK#q<77P> z)ydYDVv|M9g_urSOir}?rOw0v0w9uuNdU?Y04fFoMt-muh{H6w%0hkeMhgY@1Rzg@ zL1yv|3xmlDHe7IXH(OiwFm4X9v1Qz3C(10i=2mNN(N#tVpy~$oDVrDH+^pt!iIws2 J=AUlttN=ldHlbH?^BirQr7Mk3Q3?RS= zf|F<4XiT=Y6x-};UB(DyY-XB#!KPr7ohY+l&h~PxqN|J!K;;bzdy?Lq->l|%iIp*U J^G`Q+Rsf_{BWwTw diff --git a/fesod-sheet/src/test/resources/fill/fillHandler07.xlsx b/fesod-sheet/src/test/resources/fill/fillHandler07.xlsx index 9ed3b2d7dd0ea644622557e9dcf20a485f131a44..fb83f7599356194fa8960e4b37f12904dcee238a 100644 GIT binary patch delta 5187 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 delta 4581 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