From 716caad67d469f71213baf2209dc7ff2e7a73dba Mon Sep 17 00:00:00 2001 From: Luis Sergio Carneiro Date: Wed, 3 Dec 2025 16:10:43 -0300 Subject: [PATCH 1/2] Fixing row and column tracking when there are blank cells --- .../fastexcel/reader/RowSpliterator.java | 35 ++++++++++++------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/fastexcel-reader/src/main/java/org/dhatim/fastexcel/reader/RowSpliterator.java b/fastexcel-reader/src/main/java/org/dhatim/fastexcel/reader/RowSpliterator.java index 6be28ba..56dc752 100644 --- a/fastexcel-reader/src/main/java/org/dhatim/fastexcel/reader/RowSpliterator.java +++ b/fastexcel-reader/src/main/java/org/dhatim/fastexcel/reader/RowSpliterator.java @@ -15,14 +15,21 @@ */ package org.dhatim.fastexcel.reader; -import javax.xml.stream.XMLStreamException; +import static org.dhatim.fastexcel.reader.DefaultXMLInputFactory.factory; + import java.io.InputStream; import java.math.BigDecimal; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Optional; +import java.util.Spliterator; import java.util.function.Consumer; import java.util.function.Function; -import static org.dhatim.fastexcel.reader.DefaultXMLInputFactory.factory; +import javax.xml.stream.XMLStreamException; class RowSpliterator implements Spliterator { @@ -96,8 +103,10 @@ private Row next() throws XMLStreamException { break; } - Cell cell = parseCell(trackedColIndex++); + Cell cell = parseCell(trackedColIndex++, rowIndex); CellAddress addr = cell.getAddress(); + // we may have to adjust because we may have skipped blanks + trackedColIndex = addr.getColumn() + 1; ensureSize(cells, addr.getColumn() + 1); cells.set(addr.getColumn(), cell); @@ -112,15 +121,15 @@ private int getRowIndexWithFallback(int fallbackRowIndex) { return rowIndexOrNull != null ? rowIndexOrNull : fallbackRowIndex; } - private CellAddress getCellAddressWithFallback(int trackedColIndex) { + private CellAddress getCellAddressWithFallback(int trackedColIndex, int trackedRowIndex) { String cellRefOrNull = r.getAttribute("r"); return cellRefOrNull != null ? new CellAddress(cellRefOrNull) : new CellAddress(trackedRowIndex, trackedColIndex); } - private Cell parseCell(int trackedColIndex) throws XMLStreamException { - CellAddress addr = getCellAddressWithFallback(trackedColIndex); + private Cell parseCell(int trackedColIndex, int trackedRowIndex) throws XMLStreamException { + CellAddress addr = getCellAddressWithFallback(trackedColIndex, trackedRowIndex); String type = r.getOptionalAttribute("t").orElse("n"); String styleString = r.getAttribute("s"); String formatId = null; @@ -192,7 +201,7 @@ private Cell parseOther(CellAddress addr, String type, String dataFormatId, Stri if (formula == null && value == null && definedType == CellType.NUMBER) { return new Cell(workbook, CellType.EMPTY, null, addr, null, rawValue); } else { - CellType cellType = (formula != null) ? CellType.FORMULA : definedType; + CellType cellType = formula != null ? CellType.FORMULA : definedType; return new Cell(workbook, cellType, value, addr, formula, rawValue, dataFormatId, dataFormatString); } } @@ -209,7 +218,7 @@ private String parseSharedFormula(Integer si, CellAddress addr) { * @see here */ private String parseSharedFormula(Integer dCol, Integer dRow, String baseFormula) { - String res = ""; + StringBuilder res = new StringBuilder(); int start = 0; boolean stringLiteral = false; for (int end = 0; end < baseFormula.length(); end++) { @@ -222,7 +231,7 @@ private String parseSharedFormula(Integer dCol, Integer dRow, String baseFormula } if (c >= 'A' && c <= 'Z' || c == '$') { - res += baseFormula.substring(start, end); + res.append(baseFormula.substring(start, end)); start = end; end++; boolean foundNum = false; @@ -240,17 +249,17 @@ private String parseSharedFormula(Integer dCol, Integer dRow, String baseFormula } if (foundNum) { String cellID = baseFormula.substring(start, end); - res += shiftCell(cellID, dCol, dRow); + res.append(shiftCell(cellID, dCol, dRow)); start = end; } } } if (start < baseFormula.length()) { - res += baseFormula.substring(start); + res.append(baseFormula.substring(start)); } - return res; + return res.toString(); } /** From dc80b0759e2481643ec48f4870c6b7e85a854c1a Mon Sep 17 00:00:00 2001 From: Luis Sergio Carneiro Date: Thu, 18 Dec 2025 15:51:22 -0300 Subject: [PATCH 2/2] Adding test case --- .../fastexcel/reader/RowSpliterator.java | 7 +-- .../fastexcel/reader/RowSpliteratorTest.java | 28 +++++++++++ .../src/test/resources/xml/blank_cells.xml | 48 +++++++++++++++++++ 3 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 fastexcel-reader/src/test/java/org/dhatim/fastexcel/reader/RowSpliteratorTest.java create mode 100644 fastexcel-reader/src/test/resources/xml/blank_cells.xml diff --git a/fastexcel-reader/src/main/java/org/dhatim/fastexcel/reader/RowSpliterator.java b/fastexcel-reader/src/main/java/org/dhatim/fastexcel/reader/RowSpliterator.java index 56dc752..8f8485d 100644 --- a/fastexcel-reader/src/main/java/org/dhatim/fastexcel/reader/RowSpliterator.java +++ b/fastexcel-reader/src/main/java/org/dhatim/fastexcel/reader/RowSpliterator.java @@ -92,7 +92,7 @@ private Row next() throws XMLStreamException { } int trackedColIndex = 0; - int rowIndex = getRowIndexWithFallback(++trackedRowIndex); + int rowIndex = getRowIndexWithFallback(trackedRowIndex); boolean isHidden = "1".equals(r.getAttribute("hidden")); List cells = new ArrayList<>(rowCapacity); @@ -103,7 +103,7 @@ private Row next() throws XMLStreamException { break; } - Cell cell = parseCell(trackedColIndex++, rowIndex); + Cell cell = parseCell(trackedColIndex++); CellAddress addr = cell.getAddress(); // we may have to adjust because we may have skipped blanks trackedColIndex = addr.getColumn() + 1; @@ -112,6 +112,7 @@ private Row next() throws XMLStreamException { cells.set(addr.getColumn(), cell); physicalCellCount++; } + trackedRowIndex++; rowCapacity = Math.max(rowCapacity, cells.size()); return new Row(rowIndex, physicalCellCount, cells, isHidden); } @@ -128,7 +129,7 @@ private CellAddress getCellAddressWithFallback(int trackedColIndex, int trackedR new CellAddress(trackedRowIndex, trackedColIndex); } - private Cell parseCell(int trackedColIndex, int trackedRowIndex) throws XMLStreamException { + private Cell parseCell(int trackedColIndex) throws XMLStreamException { CellAddress addr = getCellAddressWithFallback(trackedColIndex, trackedRowIndex); String type = r.getOptionalAttribute("t").orElse("n"); String styleString = r.getAttribute("s"); diff --git a/fastexcel-reader/src/test/java/org/dhatim/fastexcel/reader/RowSpliteratorTest.java b/fastexcel-reader/src/test/java/org/dhatim/fastexcel/reader/RowSpliteratorTest.java new file mode 100644 index 0000000..dade7a0 --- /dev/null +++ b/fastexcel-reader/src/test/java/org/dhatim/fastexcel/reader/RowSpliteratorTest.java @@ -0,0 +1,28 @@ +package org.dhatim.fastexcel.reader; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.InputStream; + +import org.junit.jupiter.api.Test; + +public class RowSpliteratorTest { + + @Test + void testBlankCells() throws Exception { + InputStream is = Resources.open("/xml/blank_cells.xml"); + RowSpliterator it = new RowSpliterator(null, is); + it.tryAdvance(row -> { + assertEquals(8, row.getCellCount()); + Cell cell = row.getCell(7); + assertEquals("H1", cell.getAddress().toString()); + }); + it.tryAdvance(row -> { + assertEquals(8, row.getCellCount()); + Cell cell = row.getCell(7); + assertEquals("H2", cell.getAddress().toString()); + }); + } + +} + diff --git a/fastexcel-reader/src/test/resources/xml/blank_cells.xml b/fastexcel-reader/src/test/resources/xml/blank_cells.xml new file mode 100644 index 0000000..2148f5d --- /dev/null +++ b/fastexcel-reader/src/test/resources/xml/blank_cells.xml @@ -0,0 +1,48 @@ + + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + \ No newline at end of file