From 11f44d890fd01718db0e3c5a032c6eb3d9231113 Mon Sep 17 00:00:00 2001 From: Igor Volkov Date: Sun, 24 Mar 2024 18:46:43 +0200 Subject: [PATCH] Improve blank mandatory cell handling for XLS files and Sheet instances --- README.adoc | 2 +- .../bind/mapping/HSSFUnmarshallerFile.java | 5 +- .../bind/mapping/HSSFUnmarshallerStream.java | 11 ++-- .../MandatoryCellsExceptionTest.java | 60 +++++++++++++++++- src/test/resources/blank-cell.xls | Bin 0 -> 5120 bytes src/test/resources/blank-cell.xlsx | Bin 0 -> 7165 bytes 6 files changed, 69 insertions(+), 9 deletions(-) create mode 100644 src/test/resources/blank-cell.xls create mode 100644 src/test/resources/blank-cell.xlsx diff --git a/README.adoc b/README.adoc index 38329f9..2f446df 100644 --- a/README.adoc +++ b/README.adoc @@ -710,7 +710,7 @@ The `mandatoryHeader` field is compatible with XLS and XLSX files. [NOTE] ==== -The `mandatoryCell` field works **only** with XLS files. +The `mandatoryCell` field works **only** with XLS files and `Sheet` instances. XLS workbooks are opened with `RETURN_BLANK_AS_NULL` missing cell policy. If passing a `Sheet` instance it is up for the caller to make sure the missing cell policy of the parent workbook is set accordingly. ==== === Debug Cells Formats diff --git a/src/main/java/com/poiji/bind/mapping/HSSFUnmarshallerFile.java b/src/main/java/com/poiji/bind/mapping/HSSFUnmarshallerFile.java index 077eb8e..a30777d 100644 --- a/src/main/java/com/poiji/bind/mapping/HSSFUnmarshallerFile.java +++ b/src/main/java/com/poiji/bind/mapping/HSSFUnmarshallerFile.java @@ -3,6 +3,7 @@ import com.poiji.bind.PoijiFile; import com.poiji.exception.PoijiException; import com.poiji.option.PoijiOptions; +import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.usermodel.WorkbookFactory; @@ -24,7 +25,9 @@ final class HSSFUnmarshallerFile extends HSSFUnmarshaller { @Override protected Workbook workbook() { try { - return WorkbookFactory.create(poijiFile.file(), options.getPassword(), true); + Workbook workbook = WorkbookFactory.create(poijiFile.file(), options.getPassword(), true); + workbook.setMissingCellPolicy(Row.MissingCellPolicy.RETURN_BLANK_AS_NULL); + return workbook; } catch (IOException e) { throw new PoijiException("Problem occurred while creating HSSFWorkbook", e); } diff --git a/src/main/java/com/poiji/bind/mapping/HSSFUnmarshallerStream.java b/src/main/java/com/poiji/bind/mapping/HSSFUnmarshallerStream.java index 9683d7e..3ed33c7 100644 --- a/src/main/java/com/poiji/bind/mapping/HSSFUnmarshallerStream.java +++ b/src/main/java/com/poiji/bind/mapping/HSSFUnmarshallerStream.java @@ -3,6 +3,7 @@ import com.poiji.bind.PoijiInputStream; import com.poiji.exception.PoijiException; import com.poiji.option.PoijiOptions; +import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.usermodel.WorkbookFactory; @@ -23,12 +24,14 @@ final class HSSFUnmarshallerStream extends HSSFUnmarshaller { @Override protected Workbook workbook() { try { - + Workbook workbook; if (options.getPassword() != null) { - return WorkbookFactory.create(poijiInputStream.stream(), options.getPassword()); + workbook = WorkbookFactory.create(poijiInputStream.stream(), options.getPassword()); + } else { + workbook = WorkbookFactory.create(poijiInputStream.stream()); } - - return WorkbookFactory.create(poijiInputStream.stream()); + workbook.setMissingCellPolicy(Row.MissingCellPolicy.RETURN_BLANK_AS_NULL); + return workbook; } catch (IOException e) { throw new PoijiException("Problem occurred while creating HSSFWorkbook", e); } diff --git a/src/test/java/com/poiji/deserialize/MandatoryCellsExceptionTest.java b/src/test/java/com/poiji/deserialize/MandatoryCellsExceptionTest.java index 0571b9f..14be6e1 100644 --- a/src/test/java/com/poiji/deserialize/MandatoryCellsExceptionTest.java +++ b/src/test/java/com/poiji/deserialize/MandatoryCellsExceptionTest.java @@ -2,18 +2,20 @@ import com.poiji.bind.Poiji; import com.poiji.deserialize.model.byname.MandatoryMissingCells; +import com.poiji.exception.PoijiExcelType; import com.poiji.exception.PoijiMultiRowException; import com.poiji.exception.PoijiMultiRowException.PoijiRowSpecificException; import com.poiji.option.PoijiOptions; - import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.Cell; 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.usermodel.XSSFSheet; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.junit.Test; -import java.io.IOException; +import java.io.*; import java.util.List; import static org.junit.Assert.assertEquals; @@ -22,7 +24,7 @@ public class MandatoryCellsExceptionTest { @Test - public void testExcelMandatoryColumn() { + public void shouldThrowExceptionForMissingCell() { try { Poiji.fromExcel(createDummyExcel(), MandatoryMissingCells.class, PoijiOptions.PoijiOptionsBuilder .settings() @@ -38,6 +40,58 @@ public void testExcelMandatoryColumn() { fail("Expected exception: " + PoijiMultiRowException.class.getName()); } + @Test + public void shouldThrowExceptionForBlankCellInSheet() throws IOException { + try (InputStream stream = new FileInputStream("src/test/resources/blank-cell.xlsx"); + XSSFWorkbook workbook = new XSSFWorkbook(stream)) { + workbook.setMissingCellPolicy(Row.MissingCellPolicy.RETURN_BLANK_AS_NULL); + XSSFSheet sheet = workbook.getSheetAt(0); + Poiji.fromExcel(sheet, MandatoryMissingCells.class, PoijiOptions.PoijiOptionsBuilder + .settings() + .build()); + } catch (PoijiMultiRowException e) { + List errors = e.getErrors(); + assertEquals(1, errors.size()); + assertEquals("Address", errors.get(0).getColumnName()); + assertEquals("address", errors.get(0).getFieldName()); + assertEquals((Integer) 1, errors.get(0).getRowNum()); + return; + } + fail("Expected exception: " + PoijiMultiRowException.class.getName()); + } + + @Test + public void shouldThrowExceptionForBlankCellInFile() { + try { + Poiji.fromExcel(new File("src/test/resources/blank-cell.xls"), MandatoryMissingCells.class, + PoijiOptions.PoijiOptionsBuilder.settings().build()); + } catch (PoijiMultiRowException e) { + List errors = e.getErrors(); + assertEquals(1, errors.size()); + assertEquals("Address", errors.get(0).getColumnName()); + assertEquals("address", errors.get(0).getFieldName()); + assertEquals((Integer) 1, errors.get(0).getRowNum()); + return; + } + fail("Expected exception: " + PoijiMultiRowException.class.getName()); + } + + @Test + public void shouldThrowExceptionForBlankCellInStream() throws IOException { + try (InputStream stream = new FileInputStream("src/test/resources/blank-cell.xls")) { + Poiji.fromExcel(stream, PoijiExcelType.XLS, MandatoryMissingCells.class, + PoijiOptions.PoijiOptionsBuilder.settings().build()); + } catch (PoijiMultiRowException e) { + List errors = e.getErrors(); + assertEquals(1, errors.size()); + assertEquals("Address", errors.get(0).getColumnName()); + assertEquals("address", errors.get(0).getFieldName()); + assertEquals((Integer) 1, errors.get(0).getRowNum()); + return; + } + fail("Expected exception: " + PoijiMultiRowException.class.getName()); + } + private Sheet createDummyExcel() { Workbook workbook = new HSSFWorkbook(); diff --git a/src/test/resources/blank-cell.xls b/src/test/resources/blank-cell.xls new file mode 100644 index 0000000000000000000000000000000000000000..b11c0b3c91d5f1c7a58681561e28f6e159169d73 GIT binary patch literal 5120 zcmeHKTWDNW6kYe`=H18S(N6kwnzYSK65GTPEG_NCKEQ(3(jXSB#7So+jm?Wm0%<|H zRSSMtuofZ%0WJ6=LRAX-<45{cl?fK5NQ)n-6!BN7V4>}G?S1df+{TQgB8nw@vd=kt zowFZjpV!TmZ)#@Ff4cex=Fl#b;GR>7vH)(dUGB>vXrEyB9LEv3g3RV3{f{zGSs@Eq zim~SD@~><*P2dJhd}3W<3H(Stf?-%dc4%lc@^8^@jUj1B3{%`>JJ~WYNpu-jX=Jrp z)@k{%j{8E(ov5V_aOLE!ZQHbE;IP@Lxy)s9$!+U0&mN*oSLibvp$*|wOr9Gfaf$AF=_uBjWgBILDThYQFx}zIgB7OuaL{*CIP@(bBMn={h z?d+!AHpI{@DnX5wNI{_`a^LtT(1yo?F5FlHUL=%RM~Oti)#GTz)_^Nt)A!~ns>6cb z43|^rHz%UCTTDd+m-SWrmf!W)xNO7u#uvlI>U`rLX52tTrst8L&sH}Nzt6>EMew#F zcuNtya$a1#X*_p)zi)c$-cbbq^Z0Y7g^*q1NL^!>X(TAl3r2o z@XB9=4pwFWL2T3cbgQyZ{awuMopeGC>@5}ws* z;9V`faudsuB$cfV-yv4QrmzJZNDid=+3{p5l}(R~5IC3|>vvGad+mLOu!N(k$nHxP zy6+{cHr*?L-5QP52l<(@RPX*JEK{6_c(2hiM4NX${N>wcdJZPK6=`7!UO!U4rijfk zK5gRG0M;3&*mKFM*NCygtzK81H?-VLoyqCU#cTaPC7J{6DBG5KsxuP$IS~uA&!Z8Q zjLmT*FDBow!)9FRyM$jGc#b_)cttZ{;A}Nt0g}ijw}3ChT&B3pz|BI1$v0oy>)fOp z%ORKuCTF9a#O`WdA+HO`l?+NUKqRT%RW{5rN(P^)?>UPj#3k>$%vyY=o1~MHYnIt! z#${y#P;W&Ty@jkY2tz)hu-9E#ckuk>J??d@%AH$qgdpbC>8q1l)`ZTzTY=WL+ZSjK zrE{<|#P*YA9VNmjxtiQgmMqCpUQX^Li_M!HDE&fyy`F8I-AOirhGdW!lIgxo76VzY z7DE~&Wl8XO|H%0Er%nu~v;BR8$4B-YOAhv?Q>rTdwY%$BGMi5A9XdMe+7E#v{vH*} zpUh|G^n1VFe)d@CqqEFk^Tlg2l>_A96e}qUUPsVMnOi=$M#^r$u`fw_!bIu6>T=Lr`$_c nDIUz;l>F$t}k}g3+1c86} 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 literal 0 HcmV?d00001