Copilot commented on code in PR #842: URL: https://github.com/apache/fesod/pull/842#discussion_r2835919693
########## fesod-sheet/src/test/java/org/apache/fesod/sheet/fill/LoopRowFillingMergeTest.java: ########## @@ -0,0 +1,594 @@ +/* + * 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.fill; + +import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Path; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import org.apache.fesod.sheet.ExcelWriter; +import org.apache.fesod.sheet.FesodSheet; +import org.apache.fesod.sheet.enums.FillMergeStrategy; +import org.apache.fesod.sheet.enums.WriteDirectionEnum; +import org.apache.fesod.sheet.write.executor.ExcelWriteFillExecutor; +import org.apache.fesod.sheet.write.metadata.WriteSheet; +import org.apache.fesod.sheet.write.metadata.fill.FillConfig; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.usermodel.CellType; +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.ss.usermodel.WorkbookFactory; +import org.apache.poi.ss.util.CellRangeAddress; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +/** + * Tests for multi-rows loop filling, cells merge handing and copy merged cell-styles. Review Comment: Class-level JavaDoc has typos (“merge handing”). Please correct to “merge handling” (and similarly in any other new/updated comments) so IDE-rendered docs stay professional. ```suggestion * Tests for multi-rows loop filling, cells merge handling and copy merged cell-styles. ``` ########## fesod-sheet/src/main/java/org/apache/fesod/sheet/write/executor/ExcelWriteFillExecutor.java: ########## @@ -137,18 +141,116 @@ public void fill(Object data, FillConfig fillConfig) { return; } Iterator<?> iterator = collectionData.iterator(); + int rowSpan = calculateRowSpan(analysisCellList); if (WriteDirectionEnum.VERTICAL.equals(fillConfig.getDirection()) && fillConfig.getForceNewRow()) { - shiftRows(collectionData.size(), analysisCellList); + shiftRows(collectionData.size(), rowSpan, analysisCellList); } while (iterator.hasNext()) { - doFill(analysisCellList, iterator.next(), fillConfig, getRelativeRowIndex()); + doFill(analysisCellList, iterator.next(), fillConfig, getRelativeRowIndex(), rowSpan); } } else { - doFill(readTemplateData(templateAnalysisCache), realData, fillConfig, null); + doFill(readTemplateData(templateAnalysisCache), realData, fillConfig, null, 0); } } - private void shiftRows(int size, List<AnalysisCell> analysisCellList) { + private void addMergedRegionIfNecessary(List<AnalysisCell> analysisCellList, FillConfig fillConfig) { + FillMergeStrategy mergeStrategy = fillConfig.getMergeStrategy(); + if (FillMergeStrategy.NONE.equals(mergeStrategy)) { + return; + } + + Set<CellRangeAddress> dataRowMergeRegions = determineMergedRegionsForRow(analysisCellList, fillConfig); + if (CollectionUtils.isEmpty(dataRowMergeRegions)) { + return; + } + + Sheet sheet = writeContext.writeSheetHolder().getSheet(); + + // Unify the style using anchor cells + if (FillMergeStrategy.MERGE_CELL_STYLE.equals(mergeStrategy)) { + Sheet cachedSheet = writeContext.writeSheetHolder().getCachedSheet(); + Map<CellCoordinate, Set<CellCoordinate>> cellCoordinateMap = indexMergedRegionsMap(dataRowMergeRegions); + + for (Map.Entry<CellCoordinate, Set<CellCoordinate>> entry : cellCoordinateMap.entrySet()) { + CellCoordinate anchor = entry.getKey(); + CellStyle anchorStyle = + anchor.getOrCreateCell(sheet, cachedSheet).getCellStyle(); + + for (CellCoordinate mergedCell : entry.getValue()) { + Cell cell = mergedCell.getOrCreateCell(sheet, cachedSheet); + cell.setCellStyle(anchorStyle); + } + } + } + + // Merge cells + for (CellRangeAddress range : dataRowMergeRegions) { + sheet.addMergedRegionUnsafe(range.copy()); + } + } + + private Set<CellRangeAddress> determineMergedRegionsForRow(List<AnalysisCell> cells, FillConfig fillConfig) { + if (CollectionUtils.isEmpty(cells) + || !WriteDirectionEnum.VERTICAL.equals(fillConfig.getDirection()) + || FillMergeStrategy.NONE.equals(fillConfig.getMergeStrategy())) { + return Collections.emptySet(); + } + + Sheet sheet = writeContext.writeSheetHolder().getSheet(); + if (sheet.getNumMergedRegions() == 0) { + return Collections.emptySet(); + } + + Set<CellRangeAddress> absoluteRegions = new HashSet<>(); + Map<AnalysisCell, Integer> collectionLastIndexMap = collectionLastIndexCache.get(currentUniqueDataFlag); + + for (AnalysisCell cell : cells) { + for (CellRangeAddress range : sheet.getMergedRegions()) { + if (range.isInRange(cell.getRowIndex(), cell.getColumnIndex())) { + int firstRow = collectionLastIndexMap.get(cell); + int lastRow = firstRow + (range.getLastRow() - range.getFirstRow()); Review Comment: `determineMergedRegionsForRow` calculates the replicated merged range using `firstRow = collectionLastIndexMap.get(cell)`. This only works if the template variable cell is on the merged region’s first row; if a placeholder exists in any non-anchor cell inside a merged region, the computed `firstRow/lastRow` will be wrong and can create overlapping/incorrect merges. Consider computing a row offset (`collectionLastIndexMap.get(cell) - cell.getRowIndex()`) and shifting the original `range` by that offset (and/or only using the merged region’s anchor cell) to generate the replicated `CellRangeAddress`. ```suggestion Integer mappedRowIndex = collectionLastIndexMap.get(cell); if (mappedRowIndex == null) { continue; } int rowOffset = mappedRowIndex - cell.getRowIndex(); for (CellRangeAddress range : sheet.getMergedRegions()) { if (range.isInRange(cell.getRowIndex(), cell.getColumnIndex())) { int firstRow = range.getFirstRow() + rowOffset; int lastRow = range.getLastRow() + rowOffset; ``` ########## fesod-sheet/src/main/java/org/apache/fesod/sheet/write/executor/ExcelWriteFillExecutor.java: ########## @@ -137,18 +141,116 @@ public void fill(Object data, FillConfig fillConfig) { return; } Iterator<?> iterator = collectionData.iterator(); + int rowSpan = calculateRowSpan(analysisCellList); if (WriteDirectionEnum.VERTICAL.equals(fillConfig.getDirection()) && fillConfig.getForceNewRow()) { - shiftRows(collectionData.size(), analysisCellList); + shiftRows(collectionData.size(), rowSpan, analysisCellList); } while (iterator.hasNext()) { - doFill(analysisCellList, iterator.next(), fillConfig, getRelativeRowIndex()); + doFill(analysisCellList, iterator.next(), fillConfig, getRelativeRowIndex(), rowSpan); } } else { - doFill(readTemplateData(templateAnalysisCache), realData, fillConfig, null); + doFill(readTemplateData(templateAnalysisCache), realData, fillConfig, null, 0); } } - private void shiftRows(int size, List<AnalysisCell> analysisCellList) { + private void addMergedRegionIfNecessary(List<AnalysisCell> analysisCellList, FillConfig fillConfig) { + FillMergeStrategy mergeStrategy = fillConfig.getMergeStrategy(); + if (FillMergeStrategy.NONE.equals(mergeStrategy)) { + return; + } + + Set<CellRangeAddress> dataRowMergeRegions = determineMergedRegionsForRow(analysisCellList, fillConfig); + if (CollectionUtils.isEmpty(dataRowMergeRegions)) { + return; + } + + Sheet sheet = writeContext.writeSheetHolder().getSheet(); + + // Unify the style using anchor cells + if (FillMergeStrategy.MERGE_CELL_STYLE.equals(mergeStrategy)) { + Sheet cachedSheet = writeContext.writeSheetHolder().getCachedSheet(); + Map<CellCoordinate, Set<CellCoordinate>> cellCoordinateMap = indexMergedRegionsMap(dataRowMergeRegions); + + for (Map.Entry<CellCoordinate, Set<CellCoordinate>> entry : cellCoordinateMap.entrySet()) { + CellCoordinate anchor = entry.getKey(); + CellStyle anchorStyle = + anchor.getOrCreateCell(sheet, cachedSheet).getCellStyle(); + + for (CellCoordinate mergedCell : entry.getValue()) { + Cell cell = mergedCell.getOrCreateCell(sheet, cachedSheet); + cell.setCellStyle(anchorStyle); + } + } + } + + // Merge cells + for (CellRangeAddress range : dataRowMergeRegions) { + sheet.addMergedRegionUnsafe(range.copy()); Review Comment: Current merge replication will also re-add the template’s existing merged regions for the first loop item (offset 0), which can create duplicate merged regions in the sheet. It would be safer to skip adding merges when the computed offset is 0 and the merged region already exists, or de-duplicate against existing merged regions before calling `addMergedRegionUnsafe`. ```suggestion // Merge cells // Avoid adding duplicate merged regions by tracking existing ones Set<String> existingMergedRegionKeys = new HashSet<>(); for (CellRangeAddress existingRange : sheet.getMergedRegions()) { String key = existingRange.getFirstRow() + ":" + existingRange.getLastRow() + ":" + existingRange.getFirstColumn() + ":" + existingRange.getLastColumn(); existingMergedRegionKeys.add(key); } for (CellRangeAddress range : dataRowMergeRegions) { String key = range.getFirstRow() + ":" + range.getLastRow() + ":" + range.getFirstColumn() + ":" + range.getLastColumn(); if (existingMergedRegionKeys.contains(key)) { continue; } sheet.addMergedRegionUnsafe(range.copy()); existingMergedRegionKeys.add(key); ``` ########## fesod-sheet/src/main/java/org/apache/fesod/sheet/write/executor/ExcelWriteFillExecutor.java: ########## @@ -137,18 +141,116 @@ public void fill(Object data, FillConfig fillConfig) { return; } Iterator<?> iterator = collectionData.iterator(); + int rowSpan = calculateRowSpan(analysisCellList); if (WriteDirectionEnum.VERTICAL.equals(fillConfig.getDirection()) && fillConfig.getForceNewRow()) { - shiftRows(collectionData.size(), analysisCellList); + shiftRows(collectionData.size(), rowSpan, analysisCellList); } while (iterator.hasNext()) { - doFill(analysisCellList, iterator.next(), fillConfig, getRelativeRowIndex()); + doFill(analysisCellList, iterator.next(), fillConfig, getRelativeRowIndex(), rowSpan); } } else { - doFill(readTemplateData(templateAnalysisCache), realData, fillConfig, null); + doFill(readTemplateData(templateAnalysisCache), realData, fillConfig, null, 0); } } - private void shiftRows(int size, List<AnalysisCell> analysisCellList) { + private void addMergedRegionIfNecessary(List<AnalysisCell> analysisCellList, FillConfig fillConfig) { + FillMergeStrategy mergeStrategy = fillConfig.getMergeStrategy(); + if (FillMergeStrategy.NONE.equals(mergeStrategy)) { + return; + } + + Set<CellRangeAddress> dataRowMergeRegions = determineMergedRegionsForRow(analysisCellList, fillConfig); + if (CollectionUtils.isEmpty(dataRowMergeRegions)) { + return; + } + + Sheet sheet = writeContext.writeSheetHolder().getSheet(); + + // Unify the style using anchor cells + if (FillMergeStrategy.MERGE_CELL_STYLE.equals(mergeStrategy)) { + Sheet cachedSheet = writeContext.writeSheetHolder().getCachedSheet(); + Map<CellCoordinate, Set<CellCoordinate>> cellCoordinateMap = indexMergedRegionsMap(dataRowMergeRegions); + + for (Map.Entry<CellCoordinate, Set<CellCoordinate>> entry : cellCoordinateMap.entrySet()) { + CellCoordinate anchor = entry.getKey(); + CellStyle anchorStyle = + anchor.getOrCreateCell(sheet, cachedSheet).getCellStyle(); + + for (CellCoordinate mergedCell : entry.getValue()) { + Cell cell = mergedCell.getOrCreateCell(sheet, cachedSheet); + cell.setCellStyle(anchorStyle); + } + } + } + + // Merge cells + for (CellRangeAddress range : dataRowMergeRegions) { + sheet.addMergedRegionUnsafe(range.copy()); + } + } + + private Set<CellRangeAddress> determineMergedRegionsForRow(List<AnalysisCell> cells, FillConfig fillConfig) { + if (CollectionUtils.isEmpty(cells) + || !WriteDirectionEnum.VERTICAL.equals(fillConfig.getDirection()) + || FillMergeStrategy.NONE.equals(fillConfig.getMergeStrategy())) { + return Collections.emptySet(); + } + + Sheet sheet = writeContext.writeSheetHolder().getSheet(); + if (sheet.getNumMergedRegions() == 0) { + return Collections.emptySet(); + } + + Set<CellRangeAddress> absoluteRegions = new HashSet<>(); + Map<AnalysisCell, Integer> collectionLastIndexMap = collectionLastIndexCache.get(currentUniqueDataFlag); + + for (AnalysisCell cell : cells) { + for (CellRangeAddress range : sheet.getMergedRegions()) { + if (range.isInRange(cell.getRowIndex(), cell.getColumnIndex())) { Review Comment: `determineMergedRegionsForRow` only considers merged regions that intersect cells containing template variables (`analysisCellList`). Any merged regions within the template row-span that do not cover a placeholder cell will never be replicated, which seems to conflict with the docs/PR description that say the template row’s merge structure is replicated. If the intent is to replicate *all* merges in the loop block, consider scanning merged regions by row-span bounds rather than by placeholder cells. ########## fesod-sheet/src/main/java/org/apache/fesod/sheet/enums/FillMergeStrategy.java: ########## @@ -0,0 +1,44 @@ +/* + * 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.enums; + +/** + * Strategy for handling merged regions during loop filling. + */ +public enum FillMergeStrategy { + + /** + * Merged regions in the fill template will NOT be handing. + */ + NONE, + + /** + * Automatically handling merged regions base on fill template. + */ + AUTO, + + /** + * Automatically handling merged regions and unify the style using anchor cells base on fill template. Review Comment: The JavaDoc/comment uses “handing” where “handling” is intended (e.g., “Merged regions … will NOT be handing”, “base on”). Please fix wording to avoid confusion in a public enum’s documentation. ```suggestion * Merged regions in the fill template will NOT be handled. */ NONE, /** * Automatically handling merged regions based on the fill template. */ AUTO, /** * Automatically handling merged regions and unifying the style using anchor cells based on the fill template. ``` ########## fesod-sheet/src/test/java/org/apache/fesod/sheet/fill/LoopRowFillingMergeTest.java: ########## @@ -0,0 +1,594 @@ +/* + * 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.fill; + +import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Path; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import org.apache.fesod.sheet.ExcelWriter; +import org.apache.fesod.sheet.FesodSheet; +import org.apache.fesod.sheet.enums.FillMergeStrategy; +import org.apache.fesod.sheet.enums.WriteDirectionEnum; +import org.apache.fesod.sheet.write.executor.ExcelWriteFillExecutor; +import org.apache.fesod.sheet.write.metadata.WriteSheet; +import org.apache.fesod.sheet.write.metadata.fill.FillConfig; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.usermodel.CellType; +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.ss.usermodel.WorkbookFactory; +import org.apache.poi.ss.util.CellRangeAddress; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +/** + * Tests for multi-rows loop filling, cells merge handing and copy merged cell-styles. + * + * @see ExcelWriteFillExecutor + */ +class LoopRowFillingMergeTest { + + // Case 1: base filling + private File caseA07; + private File caseATemplate07; + private File caseA03; + private File caseATemplate03; + + // Case 2: filling with auto merge + private File caseB07; + private File caseBTemplate07; + private File caseB03; + private File caseBTemplate03; + + // Case 3: filling with auto merge and copy merged cell-styles + private File caseC07; + private File caseCTemplate07; + private File caseC03; + private File caseCTemplate03; + + // Case 4: combined base filling of collection-based data (list rows) and common data + private File caseD07; + private File caseDTemplate07; + private File caseD03; + private File caseDTemplate03; + + // Case 5: combined filling of collection-based data (list rows) with auto merge and common data + private File caseE07; + private File caseETemplate07; + private File caseE03; + private File caseETemplate03; + + // Case 6: combined filling of collection-based data (list rows) with auto merge, copy merged cell-styles and + // common data + private File caseF07; + private File caseFTemplate07; + private File caseF03; + private File caseFTemplate03; + + private static final int MOCK_DATA_SIZE = 1000; Review Comment: `MOCK_DATA_SIZE` is set to 1000 and the merge validation does repeated linear scans over the merged-region list for each row/column. This can make the unit test noticeably slow/flaky in CI. Consider reducing the dataset size (while still covering multi-row behavior) and/or indexing merged regions (e.g., map from (row,col) to ranges) to keep the test runtime predictable. ```suggestion private static final int MOCK_DATA_SIZE = 50; ``` ########## fesod-sheet/src/main/java/org/apache/fesod/sheet/write/executor/ExcelWriteFillExecutor.java: ########## @@ -137,18 +141,116 @@ public void fill(Object data, FillConfig fillConfig) { return; } Iterator<?> iterator = collectionData.iterator(); + int rowSpan = calculateRowSpan(analysisCellList); if (WriteDirectionEnum.VERTICAL.equals(fillConfig.getDirection()) && fillConfig.getForceNewRow()) { - shiftRows(collectionData.size(), analysisCellList); + shiftRows(collectionData.size(), rowSpan, analysisCellList); } while (iterator.hasNext()) { - doFill(analysisCellList, iterator.next(), fillConfig, getRelativeRowIndex()); + doFill(analysisCellList, iterator.next(), fillConfig, getRelativeRowIndex(), rowSpan); } } else { - doFill(readTemplateData(templateAnalysisCache), realData, fillConfig, null); + doFill(readTemplateData(templateAnalysisCache), realData, fillConfig, null, 0); } } - private void shiftRows(int size, List<AnalysisCell> analysisCellList) { + private void addMergedRegionIfNecessary(List<AnalysisCell> analysisCellList, FillConfig fillConfig) { + FillMergeStrategy mergeStrategy = fillConfig.getMergeStrategy(); + if (FillMergeStrategy.NONE.equals(mergeStrategy)) { + return; + } + + Set<CellRangeAddress> dataRowMergeRegions = determineMergedRegionsForRow(analysisCellList, fillConfig); + if (CollectionUtils.isEmpty(dataRowMergeRegions)) { + return; + } + + Sheet sheet = writeContext.writeSheetHolder().getSheet(); + + // Unify the style using anchor cells + if (FillMergeStrategy.MERGE_CELL_STYLE.equals(mergeStrategy)) { + Sheet cachedSheet = writeContext.writeSheetHolder().getCachedSheet(); + Map<CellCoordinate, Set<CellCoordinate>> cellCoordinateMap = indexMergedRegionsMap(dataRowMergeRegions); + + for (Map.Entry<CellCoordinate, Set<CellCoordinate>> entry : cellCoordinateMap.entrySet()) { + CellCoordinate anchor = entry.getKey(); + CellStyle anchorStyle = + anchor.getOrCreateCell(sheet, cachedSheet).getCellStyle(); + + for (CellCoordinate mergedCell : entry.getValue()) { + Cell cell = mergedCell.getOrCreateCell(sheet, cachedSheet); + cell.setCellStyle(anchorStyle); + } + } + } + + // Merge cells + for (CellRangeAddress range : dataRowMergeRegions) { + sheet.addMergedRegionUnsafe(range.copy()); + } + } + + private Set<CellRangeAddress> determineMergedRegionsForRow(List<AnalysisCell> cells, FillConfig fillConfig) { + if (CollectionUtils.isEmpty(cells) + || !WriteDirectionEnum.VERTICAL.equals(fillConfig.getDirection()) + || FillMergeStrategy.NONE.equals(fillConfig.getMergeStrategy())) { + return Collections.emptySet(); + } + + Sheet sheet = writeContext.writeSheetHolder().getSheet(); + if (sheet.getNumMergedRegions() == 0) { + return Collections.emptySet(); + } + + Set<CellRangeAddress> absoluteRegions = new HashSet<>(); + Map<AnalysisCell, Integer> collectionLastIndexMap = collectionLastIndexCache.get(currentUniqueDataFlag); + + for (AnalysisCell cell : cells) { + for (CellRangeAddress range : sheet.getMergedRegions()) { + if (range.isInRange(cell.getRowIndex(), cell.getColumnIndex())) { + int firstRow = collectionLastIndexMap.get(cell); Review Comment: `determineMergedRegionsForRow` assumes `collectionLastIndexMap` is non-null, but `addMergedRegionIfNecessary` is invoked for non-collection fills as well (the `else` branch calls `doFill(..., rowSpan=0)`). If a caller sets `mergeStrategy != NONE` for a non-collection fill, `collectionLastIndexCache.get(currentUniqueDataFlag)` will be null and this will throw an NPE. Please guard by returning an empty set when `collectionLastIndexMap` is null (and/or only applying merge strategies when filling collection data / `analysisCell.getCellType() == COLLECTION`). ```suggestion if (collectionLastIndexMap == null || collectionLastIndexMap.isEmpty()) { return Collections.emptySet(); } for (AnalysisCell cell : cells) { Integer firstRow = collectionLastIndexMap.get(cell); if (firstRow == null) { // No recorded last index for this cell; skip to avoid NPE. continue; } for (CellRangeAddress range : sheet.getMergedRegions()) { if (range.isInRange(cell.getRowIndex(), cell.getColumnIndex())) { ``` ########## website/docs/sheet/fill/fill.md: ########## @@ -121,6 +137,100 @@ public void listFill() { --- +## Multi-Row Loop Filling Merge Strategy + +### Overview + +When filling list data, the template often contains complex merged regions spanning across rows or +columns. By default, Fesod fills the data but does not automatically replicate these merged regions. You can use +the `mergeStrategy` parameter to control the merging behavior of the generated rows. + Review Comment: Doc text has a few typos/grammar issues (e.g., “handing merge”, “handing merge across rows and columns”). Please update to “handling” for clarity and professionalism (also consider using ASCII ':' in “Warning:” for consistency). ########## website/i18n/zh-cn/docusaurus-plugin-content-docs/current/sheet/fill/fill.md: ########## @@ -104,6 +120,95 @@ public void listFill() { --- +## 列表填充合并策略 + +### 概述 + +在处理列表数据填充时,模板中可能定义了复杂的跨行或跨列合并结构。默认情况下,Fesod 不会自动合并跨行跨列单元格。但是,您可以使用 `mergeStrategy` 参数来控制合并行为。 + +### 合并策略 + +- **NONE**: 不进行任何自动合并(默认)。 +- **AUTO**: Fesod 会参照模板行的合并结构,自动对生成的每一行数据应用相同的合并区域。 +- **MERGE_CELL_STYLE**: 在 `AUTO` 的基础上,将 **锚定单元格(左上角单元格)** 的样式应用到整个合并区域内的所有单元格。 + - *注意:过多的单元格样式实例可能导致性能问题,并可能超出单元格样式数量限制(.xlsx 格式为 64000 个,.xls 格式为 4000 个),请在数据量较大时谨慎使用。* Review Comment: Doc text has a few typos/grammar issues (e.g., “handing merge”, “handing merge across rows and columns”). Please update to “处理/处理合并” (“handling”) in the Chinese doc to keep terminology consistent. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
