From df7807552605681e9fc4852117146e31b5c9773d Mon Sep 17 00:00:00 2001 From: BigDataDZ <76271875+BigDataDZ@users.noreply.github.com> Date: Wed, 7 Oct 2026 22:44:11 +0800 Subject: [PATCH] fix: treat numRows(0) as no row limit when reading The default ReadListener#hasNext applied any non-null limit, so numRows(0) stopped the read before the first row. The documentation defines zero as "no limit on the number of rows, i.e. read all rows". Only apply the limit when it is positive, and add a parameterized regression test over all supported read formats covering the zero sentinel, the omitted setting and a positive limit that still stops early. Closes #1149 --- .../sheet/read/listener/ReadListener.java | 3 +- .../read/listener/NumRowsReadLimitTest.java | 104 ++++++++++++++++++ 2 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 fesod-sheet/src/test/java/org/apache/fesod/sheet/read/listener/NumRowsReadLimitTest.java diff --git a/fesod-sheet/src/main/java/org/apache/fesod/sheet/read/listener/ReadListener.java b/fesod-sheet/src/main/java/org/apache/fesod/sheet/read/listener/ReadListener.java index 9c4282b9f..56cad041b 100644 --- a/fesod-sheet/src/main/java/org/apache/fesod/sheet/read/listener/ReadListener.java +++ b/fesod-sheet/src/main/java/org/apache/fesod/sheet/read/listener/ReadListener.java @@ -102,7 +102,8 @@ default boolean hasNext(AnalysisContext context) { if (limit == null) { limit = context.readWorkbookHolder().getReadWorkbook().getNumRows(); } - if (limit != null && index >= limit) { + // Zero is documented as "no limit", so only a positive limit stops the read. + if (limit != null && limit > 0 && index >= limit) { return false; } return true; diff --git a/fesod-sheet/src/test/java/org/apache/fesod/sheet/read/listener/NumRowsReadLimitTest.java b/fesod-sheet/src/test/java/org/apache/fesod/sheet/read/listener/NumRowsReadLimitTest.java new file mode 100644 index 000000000..f28246812 --- /dev/null +++ b/fesod-sheet/src/test/java/org/apache/fesod/sheet/read/listener/NumRowsReadLimitTest.java @@ -0,0 +1,104 @@ +/* + * 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.read.listener; + +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import lombok.Getter; +import lombok.Setter; +import org.apache.fesod.sheet.FesodSheet; +import org.apache.fesod.sheet.annotation.ExcelProperty; +import org.apache.fesod.sheet.testkit.Tags; +import org.apache.fesod.sheet.testkit.base.AbstractExcelTest; +import org.apache.fesod.sheet.testkit.enums.ExcelFormat; +import org.apache.fesod.sheet.testkit.listeners.CollectingReadListener; +import org.apache.fesod.sheet.testkit.params.ExcelFormatSource; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.params.ParameterizedTest; + +/** + * The documented {@code numRows} sentinel: zero means no row limit, so all rows are read, while a + * positive limit still stops the read early. + */ +@Tag(Tags.ROUND_TRIP) +public class NumRowsReadLimitTest extends AbstractExcelTest { + + private static final int TOTAL_ROWS = 3; + + @ParameterizedTest + @ExcelFormatSource + void numRowsZeroReadsAllRowsLikeNoLimit(ExcelFormat format) throws IOException { + File file = createTempFile(format); + writeHeadless(file); + + Assertions.assertEquals(TOTAL_ROWS, readRows(file, 0)); + } + + @ParameterizedTest + @ExcelFormatSource + void omittedNumRowsReadsAllRows(ExcelFormat format) throws IOException { + File file = createTempFile(format); + writeHeadless(file); + + Assertions.assertEquals(TOTAL_ROWS, readRows(file, null)); + } + + @ParameterizedTest + @ExcelFormatSource + void positiveNumRowsStillStopsEarly(ExcelFormat format) throws IOException { + File file = createTempFile(format); + writeHeadless(file); + + Assertions.assertEquals(1, readRows(file, 1)); + } + + /** + * Writes rows without a header row, so the numRows limit maps one-to-one onto data rows + * regardless of the head row numbering. + */ + private void writeHeadless(File file) { + List> rows = new java.util.ArrayList<>(); + for (int i = 1; i <= TOTAL_ROWS; i++) { + rows.add(Arrays.asList("row-" + i)); + } + FesodSheet.write(file).sheet().doWrite(rows); + } + + private int readRows(File file, Integer numRows) { + CollectingReadListener listener = new CollectingReadListener<>(); + FesodSheet.read(file, RowData.class, listener) + .sheet() + .headRowNumber(0) + .numRows(numRows) + .doRead(); + return listener.getRows().size(); + } + + @Getter + @Setter + public static class RowData { + + @ExcelProperty("name") + private String name; + } +}