From 05b7f623a55c2833d034657d51fb226f09742932 Mon Sep 17 00:00:00 2001 From: Bengbengbalabalabeng Date: Sun, 19 Apr 2026 11:41:23 +0800 Subject: [PATCH 1/2] 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/2] 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); } }