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]

Reply via email to