From b92a5c542765915434e9262cf9e7a5ca02192a96 Mon Sep 17 00:00:00 2001 From: Jon Morgan Date: Tue, 12 Sep 2023 16:22:55 +1000 Subject: [PATCH 1/7] fix rawdata parsing for formula cells --- .../poiji/bind/mapping/HSSFUnmarshaller.java | 8 ++- .../deserialize/RawValueFormulaTest.java | 46 ++++++++++++++++++ .../deserialize/model/RowModelFormula.java | 31 ++++++++++++ src/test/resources/raw_value_formula.xls | Bin 0 -> 5632 bytes src/test/resources/raw_value_formula.xlsx | Bin 0 -> 4910 bytes 5 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 src/test/java/com/poiji/deserialize/RawValueFormulaTest.java create mode 100644 src/test/java/com/poiji/deserialize/model/RowModelFormula.java create mode 100644 src/test/resources/raw_value_formula.xls create mode 100644 src/test/resources/raw_value_formula.xlsx diff --git a/src/main/java/com/poiji/bind/mapping/HSSFUnmarshaller.java b/src/main/java/com/poiji/bind/mapping/HSSFUnmarshaller.java index 7db729d..9405dc4 100644 --- a/src/main/java/com/poiji/bind/mapping/HSSFUnmarshaller.java +++ b/src/main/java/com/poiji/bind/mapping/HSSFUnmarshaller.java @@ -271,7 +271,7 @@ private void constructTypeValue(Row currentRow, T instance, Field field, cell.setCellStyle(null); } String value; - if (options.isRawData() && cell.getCellType() == CellType.NUMERIC) { + if (options.isRawData() && isCellNumeric(cell)) { value = NumberToTextConverter.toText(cell.getNumericCellValue()); } else { value = dataFormatter.formatCellValue(cell, baseFormulaEvaluator); @@ -285,6 +285,12 @@ private void constructTypeValue(Row currentRow, T instance, Field field, } } + private boolean isCellNumeric(Cell cell) { + return (cell.getCellType() == CellType.NUMERIC || + (cell.getCellType() == CellType.FORMULA && + cell.getCachedFormulaResultType() == CellType.NUMERIC)); + } + private void setFieldData(T instance, Field field, Object data) { try { field.setAccessible(true); diff --git a/src/test/java/com/poiji/deserialize/RawValueFormulaTest.java b/src/test/java/com/poiji/deserialize/RawValueFormulaTest.java new file mode 100644 index 0000000..6355e58 --- /dev/null +++ b/src/test/java/com/poiji/deserialize/RawValueFormulaTest.java @@ -0,0 +1,46 @@ +package com.poiji.deserialize; + +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; + +import com.poiji.bind.Poiji; +import com.poiji.deserialize.model.RowModelFormula; +import com.poiji.option.PoijiOptions; +import java.io.File; +import java.util.Arrays; +import java.util.List; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/** + * Created by jmorgan on 12.09.2023 + */ +@RunWith(Parameterized.class) +public class RawValueFormulaTest { + + private String path; + + public RawValueFormulaTest(String path) { + this.path = path; + } + + @Parameterized.Parameters(name = "{index}: ({0})={1}") + public static Iterable queries() { + return Arrays.asList(new Object[][]{ + {"src/test/resources/raw_value_formula.xls"}, + {"src/test/resources/raw_value_formula.xlsx"}, + }); + } + + @Test + public void shouldMapCalculations() { + PoijiOptions options = PoijiOptions.PoijiOptionsBuilder.settings().headerCount(0).rawData(true).build(); + List models = Poiji.fromExcel(new File(path), RowModelFormula.class, options); + + for (RowModelFormula model : models) { + assertThat(model.getCurrencyValue(), is(123.45D)); + assertThat(model.getFormulaValue(), is(246.90D)); + } + } +} diff --git a/src/test/java/com/poiji/deserialize/model/RowModelFormula.java b/src/test/java/com/poiji/deserialize/model/RowModelFormula.java new file mode 100644 index 0000000..51f8a6e --- /dev/null +++ b/src/test/java/com/poiji/deserialize/model/RowModelFormula.java @@ -0,0 +1,31 @@ +package com.poiji.deserialize.model; + +import com.poiji.annotation.ExcelCell; + +/** + * Created by jmorgan on 12.09.2023 + */ +public class RowModelFormula { + + @ExcelCell(0) + private double currencyValue; + + @ExcelCell(1) + private double formulaValue; + + public double getCurrencyValue() { + return currencyValue; + } + + public void setCurrencyValue(double currencyValue) { + this.currencyValue = currencyValue; + } + + public double getFormulaValue() { + return formulaValue; + } + + public void setFormulaValue(double formulaValue) { + this.formulaValue = formulaValue; + } +} diff --git a/src/test/resources/raw_value_formula.xls b/src/test/resources/raw_value_formula.xls new file mode 100644 index 0000000000000000000000000000000000000000..8e6ca520cced8889213f6aa27a742355a76e8a98 GIT binary patch literal 5632 zcmeHLUu=_A6hB|rc3;QXy75OGQa?A9!8)=@q9cH-`{Uq&Y^kV;j7V3yF(|8C=;8>WHMP8#U;9j zEO65qE7#DCMf8BV=H*~wJ}1A2rnEHF5-bpw1b$;;NwFWy?3i42#q`Ye z7sbdgB(tP{)&dLJitV}fr~jrOF9YYFfBJf!{|fLja3y#-xC&eiMhK7^a4mQRcqMoh zcs2Mw@EY*_VCq}zx0|VeU_sXj0LFu@|bPZ{!uyg+ZC4y!6}nqHmmfZKyxEtfaTvNWT?H#k+M95oO<%oI+e zm>~&VW4u88FcC*g#0T?lgpjDYN}{(O6?Xi9n79~pZ0F>knRIy@DYW7$X-I{?5yX&OYo z25VGHS-J)FYH_C<75AlWm8j&9ODePodJp(Ne%q9$^S%zM{KFg5*tQxLmsnV#;)AA2rr^@V$FjP0Aj zA+U5zdU>zYGV7oaI63V;f2QXLo=X_`%#q_iU(v0>YN=Fc{v3zv@}om?sb8oas?G05bO>`*Mzja5OK`B3WQD-+#J(d1rXOVRBEV9QW`%UZ10K2sbXi zLl4a-ep{>-yM_CO=P2uAZrj0W98*8WJ=Y1nhjGL0#TX72({+vv^>aSdO`6kKpi&2X zr_*LYZ(L#pBo~G&px@SPrwjKGc>qETdnAuMBd=iohjH`e+kYqi1sK#dAu@(rZ~{nS zYIO87J`_9ub&$Q_eA0uEm5`{8A*TO8ls2BW|1kfDpqsvRv8Yu0=f1xeV+?nq|6guI BB~}0c literal 0 HcmV?d00001 diff --git a/src/test/resources/raw_value_formula.xlsx b/src/test/resources/raw_value_formula.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..38408bc25fcd989a75ea35b836405e8fb6103098 GIT binary patch literal 4910 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 CAO^4i literal 0 HcmV?d00001 From 4c4688e30b8f631ab63e48b1f9edece6a767872a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hakan=20=C3=96zler?= Date: Sun, 24 Sep 2023 21:11:29 +0300 Subject: [PATCH 2/7] fix #294 --- .../poiji/bind/mapping/HSSFUnmarshaller.java | 33 +++- .../com/poiji/bind/mapping/PoijiHandler.java | 14 +- .../UnknownCellsIdenticalHeadersTest.java | 179 ++++++++++-------- 3 files changed, 137 insertions(+), 89 deletions(-) diff --git a/src/main/java/com/poiji/bind/mapping/HSSFUnmarshaller.java b/src/main/java/com/poiji/bind/mapping/HSSFUnmarshaller.java index 7db729d..3b5df34 100644 --- a/src/main/java/com/poiji/bind/mapping/HSSFUnmarshaller.java +++ b/src/main/java/com/poiji/bind/mapping/HSSFUnmarshaller.java @@ -202,16 +202,29 @@ private T tailSetFieldValue(Row currentRow, Class type, T instanc throw new PoijiMultiRowException("Problem(s) occurred while reading data", errors); } - Map excelUnknownCellsMap = StreamSupport - .stream(Spliterators.spliteratorUnknownSize(currentRow.cellIterator(), Spliterator.ORDERED), false) - .filter(cell -> indexToTitle.size() != 0) - .filter(cell -> !mappedColumnIndices.contains(cell.getColumnIndex())) - .filter(cell -> !cell.toString().isEmpty()) - .collect(Collectors.toMap( - cell -> indexToTitle.get(cell.getColumnIndex()), - Object::toString)); - - unknownCells.forEach(field -> setFieldData(instance, field, excelUnknownCellsMap)); + if (unknownCells.isEmpty()) { + return instance; + } + + if (!indexToTitle.isEmpty()) { + Map excelUnknownCellsMap = StreamSupport + .stream(Spliterators.spliteratorUnknownSize(currentRow.cellIterator(), Spliterator.ORDERED), false) + .filter(cell -> !mappedColumnIndices.contains(cell.getColumnIndex())) + .filter(cell -> !cell.toString().isEmpty()) + .collect(Collectors.toMap( + cell -> indexToTitle.get(cell.getColumnIndex()), + Object::toString)); + unknownCells.forEach(field -> setFieldData(instance, field, excelUnknownCellsMap)); + } else { + Map excelUnknownCellsMap = StreamSupport + .stream(Spliterators.spliteratorUnknownSize(currentRow.cellIterator(), Spliterator.ORDERED), false) + .filter(cell -> !mappedColumnIndices.contains(cell.getColumnIndex())) + .filter(cell -> !cell.toString().isEmpty()) + .collect(Collectors.toMap( + cell -> String.valueOf(cell.getColumnIndex()), + Object::toString)); + unknownCells.forEach(field -> setFieldData(instance, field, excelUnknownCellsMap)); + } return instance; } diff --git a/src/main/java/com/poiji/bind/mapping/PoijiHandler.java b/src/main/java/com/poiji/bind/mapping/PoijiHandler.java index 719623e..1d36d57 100644 --- a/src/main/java/com/poiji/bind/mapping/PoijiHandler.java +++ b/src/main/java/com/poiji/bind/mapping/PoijiHandler.java @@ -127,9 +127,15 @@ private boolean setValue(String content, Class type, int column) { } else { excelUnknownCellsMap = (Map) field.get(instance); } - excelUnknownCellsMap.put(indexToTitle.get(column), content); + String index = indexToTitle.get(column); + if (index == null) { + excelUnknownCellsMap.put(String.valueOf(column), content); + } else { + excelUnknownCellsMap.put(indexToTitle.get(column), content); + } } catch (IllegalAccessException e) { - throw new IllegalCastException("Could not read content of field " + field.getName() + " on Object {" + instance + "}"); + throw new IllegalCastException("Could not read content of field " + field.getName() + + " on Object {" + instance + "}"); } } }); @@ -167,7 +173,7 @@ private boolean setValue(Field field, int column, String content, Object ins) { excelCellNameAnnotations.add(excelCellName); final String titleName = formatting.transform(options, excelCellName.value()); final Integer titleColumn = titleToIndex.get(titleName); - //Fix both columns mapped to name passing this condition below + // Fix both columns mapped to name passing this condition below if (titleColumn != null && titleColumn == column) { Object o = casting.castValue(field, content, internalRow, column, options); ReflectUtil.setFieldData(field, o, ins); @@ -234,7 +240,7 @@ private String getTitleNameForMap(String cellContent, int columnIndex) { @Override public void headerFooter(String text, boolean isHeader, String tagName) { - //no-op + // no-op } @Override diff --git a/src/test/java/com/poiji/deserialize/UnknownCellsIdenticalHeadersTest.java b/src/test/java/com/poiji/deserialize/UnknownCellsIdenticalHeadersTest.java index de3a6e5..92f9779 100644 --- a/src/test/java/com/poiji/deserialize/UnknownCellsIdenticalHeadersTest.java +++ b/src/test/java/com/poiji/deserialize/UnknownCellsIdenticalHeadersTest.java @@ -19,79 +19,108 @@ @RunWith(Parameterized.class) public class UnknownCellsIdenticalHeadersTest { - private String path; - - public UnknownCellsIdenticalHeadersTest(String path) { - this.path = path; - } - - @Parameterized.Parameters - public static List excel() { - return Arrays.asList( - "src/test/resources/unknown-cells-identical-headers.xlsx", - "src/test/resources/unknown-cells-identical-headers.xls" - ); - } - - @Test - public void byName() { - List organisations = Poiji.fromExcel( - new File(path), - OrgWithUnknownCellsByName.class, - PoijiOptions.PoijiOptionsBuilder.settings() - .sheetName("Organisation") - .build() - ); - - assertThat(organisations, notNullValue()); - assertThat(organisations.size(), is(2)); - - OrgWithUnknownCellsByName firstRow = organisations.stream() - .filter(org -> org.getId().equals("CrEaTe")) - .findFirst() - .get(); - assertThat(firstRow.getUnknownCells().size(), is(2)); - assertThat(firstRow.getUnknownCells().get("Tag"), is("testTag")); - assertThat(firstRow.getUnknownCells().get("Tag@5"), is("rndTag")); - - - OrgWithUnknownCellsByName secondRow = organisations.stream() - .filter(org -> org.getId().equals("8d9e6430-8626-4556-8004-079085d2df2d")) - .findFirst() - .get(); - assertThat(secondRow.getUnknownCells().size(), is(2)); - assertThat(secondRow.getUnknownCells().get("Tag"), is("testTag2")); - assertThat(secondRow.getUnknownCells().get("Tag@5"), is("rndTag2")); - } - - @Test - public void byIndex() { - List organisations = Poiji.fromExcel( - new File(path), - OrgWithUnknownCells.class, - PoijiOptions.PoijiOptionsBuilder.settings() - .sheetName("Organisation") - .build() - ); - - assertThat(organisations, notNullValue()); - assertThat(organisations.size(), is(2)); - - OrgWithUnknownCells firstRow = organisations.stream() - .filter(org -> org.getId().equals("CrEaTe")) - .findFirst() - .get(); - assertThat(firstRow.getUnknownCells().size(), is(2)); - assertThat(firstRow.getUnknownCells().get("Tag"), is("testTag")); - assertThat(firstRow.getUnknownCells().get("Tag@5"), is("rndTag")); - - - OrgWithUnknownCells secondRow = organisations.stream() - .filter(org -> org.getId().equals("8d9e6430-8626-4556-8004-079085d2df2d")) - .findFirst() - .get(); - assertThat(secondRow.getUnknownCells().size(), is(2)); - assertThat(secondRow.getUnknownCells().get("Tag"), is("testTag2")); - assertThat(secondRow.getUnknownCells().get("Tag@5"), is("rndTag2")); - } + private String path; + + public UnknownCellsIdenticalHeadersTest(String path) { + this.path = path; + } + + @Parameterized.Parameters + public static List excel() { + return Arrays.asList("src/test/resources/unknown-cells-identical-headers.xlsx", + "src/test/resources/unknown-cells-identical-headers.xls"); + } + + @Test + public void byName() { + List organisations = Poiji.fromExcel( + new File(path), + OrgWithUnknownCellsByName.class, + PoijiOptions.PoijiOptionsBuilder.settings() + .sheetName("Organisation") + .build()); + + assertThat(organisations, notNullValue()); + assertThat(organisations.size(), is(2)); + + OrgWithUnknownCellsByName firstRow = organisations.stream() + .filter(org -> org.getId().equals("CrEaTe")) + .findFirst() + .get(); + assertThat(firstRow.getUnknownCells().size(), is(2)); + assertThat(firstRow.getUnknownCells().get("Tag"), is("testTag")); + assertThat(firstRow.getUnknownCells().get("Tag@5"), is("rndTag")); + + OrgWithUnknownCellsByName secondRow = organisations.stream() + .filter(org -> org.getId().equals("8d9e6430-8626-4556-8004-079085d2df2d")) + .findFirst() + .get(); + assertThat(secondRow.getUnknownCells().size(), is(2)); + assertThat(secondRow.getUnknownCells().get("Tag"), is("testTag2")); + assertThat(secondRow.getUnknownCells().get("Tag@5"), is("rndTag2")); + } + + @Test + public void byIndex() { + List organisations = Poiji.fromExcel( + new File(path), + OrgWithUnknownCells.class, + PoijiOptions.PoijiOptionsBuilder.settings() + .sheetName("Organisation") + .build()); + + assertThat(organisations, notNullValue()); + assertThat(organisations.size(), is(2)); + + OrgWithUnknownCells firstRow = organisations.stream() + .filter(org -> org.getId().equals("CrEaTe")) + .findFirst() + .get(); + assertThat(firstRow.getUnknownCells().size(), is(2)); + assertThat(firstRow.getUnknownCells().get("Tag"), is("testTag")); + assertThat(firstRow.getUnknownCells().get("Tag@5"), is("rndTag")); + + OrgWithUnknownCells secondRow = organisations.stream() + .filter(org -> org.getId().equals("8d9e6430-8626-4556-8004-079085d2df2d")) + .findFirst() + .get(); + assertThat(secondRow.getUnknownCells().size(), is(2)); + assertThat(secondRow.getUnknownCells().get("Tag"), is("testTag2")); + assertThat(secondRow.getUnknownCells().get("Tag@5"), is("rndTag2")); + } + + @Test + public void byIndexNoHeader() { + List organisations = Poiji.fromExcel( + new File(path), + OrgWithUnknownCells.class, + PoijiOptions.PoijiOptionsBuilder.settings() + .sheetName("Organisation") + .skip(1) + .headerCount(0) + .build()); + + assertThat(organisations, notNullValue()); + assertThat(organisations.size(), is(2)); + + OrgWithUnknownCells firstRow = organisations.stream() + .filter(org -> org.getId().equals("CrEaTe")) + .findFirst() + .get(); + assertThat(firstRow.getUnknownCells().size(), is(2)); + assertThat(firstRow.getUnknownCells().get("Tag") == null ? firstRow.getUnknownCells().get("4") + : firstRow.getUnknownCells().get("Tag"), is("testTag")); + assertThat(firstRow.getUnknownCells().get("Tag@5") == null ? firstRow.getUnknownCells().get("5") + : firstRow.getUnknownCells().get("Tag@5"), is("rndTag")); + + OrgWithUnknownCells secondRow = organisations.stream() + .filter(org -> org.getId().equals("8d9e6430-8626-4556-8004-079085d2df2d")) + .findFirst() + .get(); + assertThat(secondRow.getUnknownCells().size(), is(2)); + assertThat(secondRow.getUnknownCells().get("Tag") == null ? secondRow.getUnknownCells().get("4") + : secondRow.getUnknownCells().get("Tag"), is("testTag2")); + assertThat(secondRow.getUnknownCells().get("Tag@5") == null ? secondRow.getUnknownCells().get("5") + : secondRow.getUnknownCells().get("Tag@5"), is("rndTag2")); + } } From b823cbe829fae58f809a4d63ea975a34d4413ff6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hakan=20=C3=96zler?= Date: Sun, 24 Sep 2023 21:17:12 +0300 Subject: [PATCH 3/7] remove the partial statement --- src/main/java/com/poiji/bind/mapping/HSSFUnmarshaller.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/poiji/bind/mapping/HSSFUnmarshaller.java b/src/main/java/com/poiji/bind/mapping/HSSFUnmarshaller.java index 38d5db7..492e33f 100644 --- a/src/main/java/com/poiji/bind/mapping/HSSFUnmarshaller.java +++ b/src/main/java/com/poiji/bind/mapping/HSSFUnmarshaller.java @@ -210,7 +210,6 @@ private T tailSetFieldValue(Row currentRow, Class type, T instanc Map excelUnknownCellsMap = StreamSupport .stream(Spliterators.spliteratorUnknownSize(currentRow.cellIterator(), Spliterator.ORDERED), false) .filter(cell -> !mappedColumnIndices.contains(cell.getColumnIndex())) - .filter(cell -> !cell.toString().isEmpty()) .collect(Collectors.toMap( cell -> indexToTitle.get(cell.getColumnIndex()), Object::toString)); @@ -219,7 +218,6 @@ private T tailSetFieldValue(Row currentRow, Class type, T instanc Map excelUnknownCellsMap = StreamSupport .stream(Spliterators.spliteratorUnknownSize(currentRow.cellIterator(), Spliterator.ORDERED), false) .filter(cell -> !mappedColumnIndices.contains(cell.getColumnIndex())) - .filter(cell -> !cell.toString().isEmpty()) .collect(Collectors.toMap( cell -> String.valueOf(cell.getColumnIndex()), Object::toString)); @@ -300,8 +298,8 @@ private void constructTypeValue(Row currentRow, T instance, Field field, private boolean isCellNumeric(Cell cell) { return (cell.getCellType() == CellType.NUMERIC || - (cell.getCellType() == CellType.FORMULA && - cell.getCachedFormulaResultType() == CellType.NUMERIC)); + (cell.getCellType() == CellType.FORMULA && + cell.getCachedFormulaResultType() == CellType.NUMERIC)); } private void setFieldData(T instance, Field field, Object data) { From 08d7b069c0daab1008482b99a9b6ec222290f223 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hakan=20=C3=96zler?= Date: Sun, 24 Sep 2023 21:25:20 +0300 Subject: [PATCH 4/7] bump 4.1.2 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f2324ff..c263140 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.github.ozlerhakan poiji - 4.1.1 + 4.1.2 jar poiji From 679353090ff71bd86351b1d0df01a26850238ddf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hakan=20=C3=96zler?= Date: Sun, 24 Sep 2023 21:25:41 +0300 Subject: [PATCH 5/7] use the static import of valueOf --- src/main/java/com/poiji/bind/mapping/HSSFUnmarshaller.java | 2 +- src/main/java/com/poiji/bind/mapping/PoijiHandler.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/poiji/bind/mapping/HSSFUnmarshaller.java b/src/main/java/com/poiji/bind/mapping/HSSFUnmarshaller.java index 492e33f..eeece4d 100644 --- a/src/main/java/com/poiji/bind/mapping/HSSFUnmarshaller.java +++ b/src/main/java/com/poiji/bind/mapping/HSSFUnmarshaller.java @@ -219,7 +219,7 @@ private T tailSetFieldValue(Row currentRow, Class type, T instanc .stream(Spliterators.spliteratorUnknownSize(currentRow.cellIterator(), Spliterator.ORDERED), false) .filter(cell -> !mappedColumnIndices.contains(cell.getColumnIndex())) .collect(Collectors.toMap( - cell -> String.valueOf(cell.getColumnIndex()), + cell -> valueOf(cell.getColumnIndex()), Object::toString)); unknownCells.forEach(field -> setFieldData(instance, field, excelUnknownCellsMap)); } diff --git a/src/main/java/com/poiji/bind/mapping/PoijiHandler.java b/src/main/java/com/poiji/bind/mapping/PoijiHandler.java index 1d36d57..cf8e080 100644 --- a/src/main/java/com/poiji/bind/mapping/PoijiHandler.java +++ b/src/main/java/com/poiji/bind/mapping/PoijiHandler.java @@ -129,7 +129,7 @@ private boolean setValue(String content, Class type, int column) { } String index = indexToTitle.get(column); if (index == null) { - excelUnknownCellsMap.put(String.valueOf(column), content); + excelUnknownCellsMap.put(valueOf(column), content); } else { excelUnknownCellsMap.put(indexToTitle.get(column), content); } From 97b0ccf7a07bdfdbc9eff989cdcfe6ef23688284 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hakan=20=C3=96zler?= Date: Sun, 24 Sep 2023 21:34:15 +0300 Subject: [PATCH 6/7] add a test for dateTimeFormatter --- src/test/java/com/poiji/deserialize/PoijiOptionsTest.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/test/java/com/poiji/deserialize/PoijiOptionsTest.java b/src/test/java/com/poiji/deserialize/PoijiOptionsTest.java index ef9e799..3e045a9 100644 --- a/src/test/java/com/poiji/deserialize/PoijiOptionsTest.java +++ b/src/test/java/com/poiji/deserialize/PoijiOptionsTest.java @@ -44,6 +44,13 @@ public void shouldPrintDateTimeFormatter() { assertThat(options.dateFormatter().toString(), equalTo(formatter.toString())); } + @Test + public void shouldPrintDateFormatter() { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/M/yyyy HH:mm:ss"); + PoijiOptions options = PoijiOptions.PoijiOptionsBuilder.settings().dateTimeFormatter(formatter).build(); + assertThat(options.dateTimeFormatter().toString(), equalTo(formatter.toString())); + } + @Test public void shouldUseUsLocaleWhenNotSpecified() { PoijiOptions options = PoijiOptions.PoijiOptionsBuilder.settings().build(); From 188bc7fd314d0def35c615355d88ed590d84c9f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hakan=20=C3=96zler?= Date: Mon, 25 Sep 2023 21:57:43 +0300 Subject: [PATCH 7/7] update deprecated methods --- .../poiji/deserialize/FromExcelSheetTest.java | 12 +++++++++--- .../poiji/deserialize/RawValueFormulaTest.java | 8 ++++---- .../com/poiji/deserialize/RawValueTest.java | 8 ++++---- src/test/java/com/poiji/util/FilesTest.java | 8 ++++---- src/test/resources/dates-news.xlsx | Bin 0 -> 10738 bytes 5 files changed, 21 insertions(+), 15 deletions(-) create mode 100644 src/test/resources/dates-news.xlsx diff --git a/src/test/java/com/poiji/deserialize/FromExcelSheetTest.java b/src/test/java/com/poiji/deserialize/FromExcelSheetTest.java index 8541c33..77c2747 100644 --- a/src/test/java/com/poiji/deserialize/FromExcelSheetTest.java +++ b/src/test/java/com/poiji/deserialize/FromExcelSheetTest.java @@ -37,6 +37,7 @@ public void shouldMapXLSSheet() throws IOException { assertThat(result, notNullValue()); CellFormatModel cellFormatModel = result.get(1); assertThat(cellFormatModel.getAge(), is(40)); + workbook.close(); } @Test @@ -51,9 +52,9 @@ public void shouldMapXLSXSheet() throws IOException { assertThat(result, notNullValue()); CellFormatModel cellFormatModel = result.get(1); assertThat(cellFormatModel.getAge(), is(40)); + workbook.close(); } - @Test public void shouldMapXLSXSheetWithOptions() throws IOException { @@ -61,11 +62,13 @@ public void shouldMapXLSXSheetWithOptions() throws IOException { FileInputStream fileInputStream = new FileInputStream(file); Workbook workbook = new XSSFWorkbook(fileInputStream); Sheet sheet = workbook.getSheetAt(0); - List result = Poiji.fromExcel(sheet, CellFormatModel.class, PoijiOptions.PoijiOptionsBuilder.settings().build()); + List result = Poiji.fromExcel(sheet, CellFormatModel.class, + PoijiOptions.PoijiOptionsBuilder.settings().build()); assertThat(result, notNullValue()); CellFormatModel cellFormatModel = result.get(1); assertThat(cellFormatModel.getAge(), is(40)); + workbook.close(); } @Test @@ -76,7 +79,9 @@ public void shouldMapXLSXSheetWithOptionsConsumer() throws IOException { Workbook workbook = new XSSFWorkbook(fileInputStream); Sheet sheet = workbook.getSheetAt(0); - Poiji.fromExcel(sheet, CellFormatModel.class, PoijiOptions.PoijiOptionsBuilder.settings().build(), this::printout); + Poiji.fromExcel(sheet, CellFormatModel.class, PoijiOptions.PoijiOptionsBuilder.settings().build(), + this::printout); + workbook.close(); } private void printout(CellFormatModel instance) { @@ -91,5 +96,6 @@ public void shouldMapXLSXSheetError() throws IOException { Workbook workbook = new SXSSFWorkbook(xssfWorkbook); Sheet sheet = workbook.getSheetAt(0); Poiji.fromExcel(sheet, CellFormatModel.class); + workbook.close(); } } diff --git a/src/test/java/com/poiji/deserialize/RawValueFormulaTest.java b/src/test/java/com/poiji/deserialize/RawValueFormulaTest.java index 6355e58..9a1b2b6 100644 --- a/src/test/java/com/poiji/deserialize/RawValueFormulaTest.java +++ b/src/test/java/com/poiji/deserialize/RawValueFormulaTest.java @@ -1,7 +1,7 @@ package com.poiji.deserialize; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; import com.poiji.bind.Poiji; import com.poiji.deserialize.model.RowModelFormula; @@ -27,9 +27,9 @@ public RawValueFormulaTest(String path) { @Parameterized.Parameters(name = "{index}: ({0})={1}") public static Iterable queries() { - return Arrays.asList(new Object[][]{ - {"src/test/resources/raw_value_formula.xls"}, - {"src/test/resources/raw_value_formula.xlsx"}, + return Arrays.asList(new Object[][] { + { "src/test/resources/raw_value_formula.xls" }, + { "src/test/resources/raw_value_formula.xlsx" }, }); } diff --git a/src/test/java/com/poiji/deserialize/RawValueTest.java b/src/test/java/com/poiji/deserialize/RawValueTest.java index bc2baa0..aa91fa8 100644 --- a/src/test/java/com/poiji/deserialize/RawValueTest.java +++ b/src/test/java/com/poiji/deserialize/RawValueTest.java @@ -12,7 +12,7 @@ import java.util.List; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; /** * Created by hakan on 02/08/2018 @@ -28,9 +28,9 @@ public RawValueTest(String path) { @Parameterized.Parameters(name = "{index}: ({0})={1}") public static Iterable queries() { - return Arrays.asList(new Object[][]{ - {"src/test/resources/raw_value.xls"}, - {"src/test/resources/raw_value.xlsx"}, + return Arrays.asList(new Object[][] { + { "src/test/resources/raw_value.xls" }, + { "src/test/resources/raw_value.xlsx" }, }); } diff --git a/src/test/java/com/poiji/util/FilesTest.java b/src/test/java/com/poiji/util/FilesTest.java index 65f0843..9fd549e 100644 --- a/src/test/java/com/poiji/util/FilesTest.java +++ b/src/test/java/com/poiji/util/FilesTest.java @@ -5,7 +5,7 @@ import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; public class FilesTest { @@ -26,8 +26,8 @@ public void getInstance() throws Exception { @Test public void getExtension() throws Exception { - assertThat(files.getExtension("cars.xl"),is(".xl")); - assertThat(files.getExtension("cars.xls"),is(".xls")); - assertThat(files.getExtension("cars.xlsx"),is(".xlsx")); + assertThat(files.getExtension("cars.xl"), is(".xl")); + assertThat(files.getExtension("cars.xls"), is(".xls")); + assertThat(files.getExtension("cars.xlsx"), is(".xlsx")); } } \ No newline at end of file diff --git a/src/test/resources/dates-news.xlsx b/src/test/resources/dates-news.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..81287a096b9633ebbf458d45eedf9510ae835c1b GIT binary patch literal 10738 zcmeHtg*Z0ufAl=m|N#LyrqC0*a>InV1k zob&w!&$H*c_MScWUhA5@?v=l_)D+?1@d1bcWB>p_4UiNF0NcR;0PqL^03HAtR!`dA z&c)2m#X!r`!OZz3hli~VRX#i{Q!W4&`uzWn|6&PLY7N?b1Y);8+j%V0p&gzVT3(J! z)=H*Fw}tf9BjGt+;|4L)jSr)_I;a-8n_k@ptjBO7N>nw+pd%%*X2Wr%qe^FpRuWz1 zbR>2wm-8f`uu?m^E-sQUmk8tF0CBPbNvV0xn8S+Zo`(JrUp*?%x9($=ZK;Z|$I|re zESu%9mtS|q20!Y+Dl)qg84G#@YOs?iEI#}2L7kgNo9d6sVz4pI!_1+b}bkFG1aR#h@7;L)}dx@)SfvHZ^K<#qSman z>PAePGp7OVAGw39UFlRe&nqN;HAAziaQm2;Fk4vK{5VCU!wj~6x*t(DNgUq=Kb_?a`hqeIWA>nlrX3Q5s!Pv|>K&rbDVSKgl zDLTAA`ku6t#n-bavdfBjOh{I~B;|6-5hm#!)Xwhj;Q(rXGt&kTkoE^u_mrTkXizgX za5A%T=H&Q!{-1gN7u)1tUcE9wS+xg<9๯P7{1&PC!QgoM;YopfkeIvhs)ev1k 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 V5do@$0027l!vIy|Muwkv{|`bz!Xf|w literal 0 HcmV?d00001