This is an automated email from the ASF dual-hosted git repository.
delei pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fesod.git
The following commit(s) were added to refs/heads/main by this push:
new 70e445a0 fix: read XLS literal error cells as errors instead of
booleans (#1011)
70e445a0 is described below
commit 70e445a062408c766dd0094f3f2f57963145d327
Author: anchor <[email protected]>
AuthorDate: Mon Aug 17 09:04:01 2026 +0800
fix: read XLS literal error cells as errors instead of booleans (#1011)
A BIFF BOOLERR record stores either a boolean or an error code,
distinguished by its fError flag, but BoolErrRecordHandler called
getBooleanValue() unconditionally. POI returns `errorCode != 0` for
error records, so a literal #DIV/0! or #N/A read back as true and
#NULL! (code 0) read back as false, while the same content stored in
.xlsx read back as the error text.
Branch on isError() and emit a CellDataTypeEnum.ERROR cell holding the
error text, so the existing StringErrorConverter yields the same
user-visible value as the xlsx path. This also lines the handler up
with FormulaRecordHandler, which already maps XLS formula errors to
ERROR.
Assisted-by: Cursor (Fable 5)
Co-authored-by: codeAnqiang-ma
<[email protected]>
Co-authored-by: Cursor <[email protected]>
Co-authored-by: ian zhang <[email protected]>
Co-authored-by: DeleiGuo <[email protected]>
---
.../v03/handlers/BoolErrRecordHandler.java | 18 ++-
.../v03/handlers/BoolErrRecordHandlerTest.java | 121 +++++++++++++++++++++
2 files changed, 134 insertions(+), 5 deletions(-)
diff --git
a/fesod-sheet/src/main/java/org/apache/fesod/sheet/analysis/v03/handlers/BoolErrRecordHandler.java
b/fesod-sheet/src/main/java/org/apache/fesod/sheet/analysis/v03/handlers/BoolErrRecordHandler.java
index 4fe379ca..8a5d5895 100644
---
a/fesod-sheet/src/main/java/org/apache/fesod/sheet/analysis/v03/handlers/BoolErrRecordHandler.java
+++
b/fesod-sheet/src/main/java/org/apache/fesod/sheet/analysis/v03/handlers/BoolErrRecordHandler.java
@@ -28,10 +28,12 @@ package org.apache.fesod.sheet.analysis.v03.handlers;
import java.util.List;
import org.apache.fesod.sheet.analysis.v03.IgnorableXlsRecordHandler;
import org.apache.fesod.sheet.context.xls.XlsReadContext;
+import org.apache.fesod.sheet.enums.CellDataTypeEnum;
import org.apache.fesod.sheet.enums.RowTypeEnum;
import org.apache.fesod.sheet.metadata.data.ReadCellData;
import org.apache.poi.hssf.record.BoolErrRecord;
import org.apache.poi.hssf.record.Record;
+import org.apache.poi.ss.formula.eval.ErrorEval;
/**
* Record handler
@@ -52,11 +54,17 @@ public class BoolErrRecordHandler extends
AbstractXlsRecordHandler implements Ig
return;
}
}
- xlsReadContext
- .xlsReadSheetHolder()
- .getCellMap()
- .put(targetColumnIndex,
ReadCellData.newInstance(ber.getBooleanValue(), ber.getRow(), (int)
- targetColumnIndex));
+ ReadCellData<?> cellData;
+ if (ber.isError()) {
+ // A BOOLERR record stores either a boolean or an error code;
getBooleanValue() would
+ // report the error code as `code != 0`.
+ cellData = new ReadCellData<>(CellDataTypeEnum.ERROR,
ErrorEval.getText(ber.getErrorValue()));
+ cellData.setRowIndex(ber.getRow());
+ cellData.setColumnIndex(targetColumnIndex);
+ } else {
+ cellData = ReadCellData.newInstance(ber.getBooleanValue(),
ber.getRow(), targetColumnIndex);
+ }
+
xlsReadContext.xlsReadSheetHolder().getCellMap().put(targetColumnIndex,
cellData);
xlsReadContext.xlsReadSheetHolder().setTempRowType(RowTypeEnum.DATA);
}
}
diff --git
a/fesod-sheet/src/test/java/org/apache/fesod/sheet/analysis/v03/handlers/BoolErrRecordHandlerTest.java
b/fesod-sheet/src/test/java/org/apache/fesod/sheet/analysis/v03/handlers/BoolErrRecordHandlerTest.java
new file mode 100644
index 00000000..15dc6b5e
--- /dev/null
+++
b/fesod-sheet/src/test/java/org/apache/fesod/sheet/analysis/v03/handlers/BoolErrRecordHandlerTest.java
@@ -0,0 +1,121 @@
+/*
+ * 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.analysis.v03.handlers;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import org.apache.fesod.sheet.FesodSheet;
+import org.apache.fesod.sheet.enums.ReadDefaultReturnEnum;
+import org.apache.fesod.sheet.testkit.Tags;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.usermodel.FormulaError;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+/**
+ * A cell holding a literal error value (an error stored as the cell value
rather than as a formula
+ * result, e.g. after "Paste Special → Values") is written to a BIFF
{@code BOOLERR} record, which
+ * stores either a boolean or an error code. Reading such a cell must yield
the error text, exactly as
+ * the xlsx path already does, instead of a boolean derived from the error
code.
+ */
+@Tag(Tags.READ)
+class BoolErrRecordHandlerTest {
+
+ private static final String MARKER = "marker";
+
+ @Test
+ void read_literalErrorCells_returnErrorText_inStringMode(@TempDir Path
dir) throws IOException {
+ Map<Integer, Object> xls = readFirstRow(writeXls(dir),
ReadDefaultReturnEnum.STRING);
+ Map<Integer, Object> xlsx = readFirstRow(writeXlsx(dir),
ReadDefaultReturnEnum.STRING);
+
+ Map<Integer, Object> expected = new LinkedHashMap<>();
+ expected.put(0, "true");
+ expected.put(1, "#DIV/0!");
+ expected.put(2, "#N/A");
+ expected.put(3, "#NULL!");
+ expected.put(4, MARKER);
+
+ // Before the fix the .xls row read back as {0=true, 1=true, 2=true,
3=false, 4=marker}: the
+ // error code was taken as `code != 0`, so #NULL! (code 0) even came
back as false.
+ Assertions.assertEquals(expected, xls);
+ Assertions.assertEquals(xlsx, xls, "the same content must read the
same from .xls and .xlsx");
+ }
+
+ @Test
+ void read_literalErrorCells_returnErrorText_inActualDataMode(@TempDir Path
dir) throws IOException {
+ Map<Integer, Object> xls = readFirstRow(writeXls(dir),
ReadDefaultReturnEnum.ACTUAL_DATA);
+ Map<Integer, Object> xlsx = readFirstRow(writeXlsx(dir),
ReadDefaultReturnEnum.ACTUAL_DATA);
+
+ // The boolean cell keeps its Boolean type; only the error cells
change.
+ Assertions.assertEquals(Boolean.TRUE, xls.get(0));
+ Assertions.assertEquals("#DIV/0!", xls.get(1));
+ Assertions.assertEquals("#N/A", xls.get(2));
+ Assertions.assertEquals("#NULL!", xls.get(3));
+ Assertions.assertEquals(MARKER, xls.get(4));
+ Assertions.assertEquals(xlsx, xls, "the same content must read the
same from .xls and .xlsx");
+ }
+
+ /** One row: a literal boolean, three literal error values, and a plain
string. */
+ private static void fillRow(Workbook workbook) {
+ Row row = workbook.createSheet("sheet").createRow(0);
+ row.createCell(0).setCellValue(true);
+ row.createCell(1).setCellErrorValue(FormulaError.DIV0.getCode());
+ row.createCell(2).setCellErrorValue(FormulaError.NA.getCode());
+ row.createCell(3).setCellErrorValue(FormulaError.NULL.getCode());
+ row.createCell(4).setCellValue(MARKER);
+ }
+
+ private static File writeXls(Path dir) throws IOException {
+ return write(dir.resolve("literal-error.xls"), new HSSFWorkbook());
+ }
+
+ private static File writeXlsx(Path dir) throws IOException {
+ return write(dir.resolve("literal-error.xlsx"), new XSSFWorkbook());
+ }
+
+ private static File write(Path path, Workbook workbook) throws IOException
{
+ try (Workbook closeable = workbook;
+ OutputStream out = Files.newOutputStream(path)) {
+ fillRow(closeable);
+ closeable.write(out);
+ }
+ return path.toFile();
+ }
+
+ private static Map<Integer, Object> readFirstRow(File file,
ReadDefaultReturnEnum readDefaultReturn) {
+ List<Map<Integer, Object>> rows = FesodSheet.read(file)
+ .readDefaultReturn(readDefaultReturn)
+ .headRowNumber(0)
+ .sheet(0)
+ .doReadSync();
+ return rows.get(0);
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]