This is an automated email from the ASF dual-hosted git repository.

fanningpj pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/poi.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 006e49765c Bugzilla 69303: Fix RangeCopier's copyMergedRanges when 
copying withing the same sheet (#958)
006e49765c is described below

commit 006e49765c641380dbf3bca12196358e1702bdd4
Author: vo0ff <[email protected]>
AuthorDate: Tue Dec 2 00:38:04 2025 +0300

    Bugzilla 69303: Fix RangeCopier's copyMergedRanges when copying withing the 
same sheet (#958)
    
    * Bugzilla 69303: added failing test for case with same sheet RangeCopier 
for merged regions
    
    * Bugzilla 69303: fixed same sheet issue when enabling copy of merged ranges
    
    * Bugzilla 69303: added @since 6.0.0 to new public methods
    
    ---------
    
    Co-authored-by: Viktor Ozerov <[email protected]>
---
 .../org/apache/poi/ss/usermodel/RangeCopier.java   | 15 ++++++++++----
 .../org/apache/poi/ss/util/CellRangeAddress.java   | 14 +++++++++++++
 .../apache/poi/ss/util/CellRangeAddressBase.java   | 15 ++++++++++++++
 .../poi/ss/usermodel/BaseTestRangeCopier.java      | 24 +++++++++++++++++++---
 4 files changed, 61 insertions(+), 7 deletions(-)

diff --git a/poi/src/main/java/org/apache/poi/ss/usermodel/RangeCopier.java 
b/poi/src/main/java/org/apache/poi/ss/usermodel/RangeCopier.java
index c6b82a6860..97aaa94e79 100644
--- a/poi/src/main/java/org/apache/poi/ss/usermodel/RangeCopier.java
+++ b/poi/src/main/java/org/apache/poi/ss/usermodel/RangeCopier.java
@@ -54,9 +54,11 @@ public abstract class RangeCopier {
     }
 
     /** Uses input pattern to tile destination region, overwriting existing 
content. Works in following manner :
-     * 1.Start from top-left of destination.
-     * 2.Paste source but only inside of destination borders.
-     * 3.If there is space left on right or bottom side of copy, process it as 
in step 2.
+     * <ol>
+     *     <li>Start from top-left of destination.</li>
+     *     <li>Paste source but only inside of destination borders.</li>
+     *     <li>If there is space left on right or bottom side of copy, process 
it as in step 2.</li>
+     * </ol>
      * @param tilePatternRange source range which should be copied in tiled 
manner
      * @param tileDestRange    destination range, which should be overridden
      * @param copyStyles       whether to copy the cell styles
@@ -90,7 +92,12 @@ public abstract class RangeCopier {
         } while (nextRowIndexToCopy <= tileDestRange.getLastRow());
 
         if (copyMergedRanges) {
-            sourceSheet.getMergedRegions().forEach((mergedRangeAddress) -> 
destSheet.addMergedRegion(mergedRangeAddress));
+            int rowOffset = tileDestRange.getFirstRow() - 
tilePatternRange.getFirstRow();
+            int columnOffset = tileDestRange.getFirstColumn() - 
tilePatternRange.getFirstColumn();
+            sourceSheet.getMergedRegions().stream()
+                    .filter(tilePatternRange::contains)
+                    .map(sourceMergedRegion -> 
sourceMergedRegion.shift(rowOffset, columnOffset))
+                    .forEach(destSheet::addMergedRegion);
         }
 
         int tempCopyIndex = 
sourceSheet.getWorkbook().getSheetIndex(sourceCopy);
diff --git a/poi/src/main/java/org/apache/poi/ss/util/CellRangeAddress.java 
b/poi/src/main/java/org/apache/poi/ss/util/CellRangeAddress.java
index 91a60273a4..9cd1612d8f 100644
--- a/poi/src/main/java/org/apache/poi/ss/util/CellRangeAddress.java
+++ b/poi/src/main/java/org/apache/poi/ss/util/CellRangeAddress.java
@@ -133,4 +133,18 @@ public class CellRangeAddress extends CellRangeAddressBase 
{
         }
         return new CellRangeAddress(a.getRow(), b.getRow(), a.getCol(), 
b.getCol());
     }
+
+    /**
+     * Shifts cell range by specified number of rows and columns.
+     *
+     * @param rows    rows to shift by.
+     * @param columns columns to shift by.
+     * @return copy of this {@link CellRangeAddress}, shifted by rows and 
columns.
+     * @since 6.0.0
+     */
+    public CellRangeAddress shift(int rows, int columns) {
+        return new CellRangeAddress(getFirstRow() + rows, getLastRow() + rows,
+                getFirstColumn() + columns, getLastColumn() + columns);
+    }
+
 }
diff --git a/poi/src/main/java/org/apache/poi/ss/util/CellRangeAddressBase.java 
b/poi/src/main/java/org/apache/poi/ss/util/CellRangeAddressBase.java
index 7153dff29a..3b2e1e373c 100644
--- a/poi/src/main/java/org/apache/poi/ss/util/CellRangeAddressBase.java
+++ b/poi/src/main/java/org/apache/poi/ss/util/CellRangeAddressBase.java
@@ -228,6 +228,21 @@ public abstract class CellRangeAddressBase implements 
Iterable<CellAddress>, Dup
                 other._firstCol <= this._lastCol;
     }
 
+    /**
+     * Determines whether this CellRangeAddress fully contains 
CellRangeAddress.
+     *
+     * @param other a candidate cell range address to check if contained 
within this range
+     * @return returns true if this range contains other range.
+     * @see #isInRange(int, int) for checking if a single cell contains
+     * @since 6.0.0
+     */
+    public boolean contains(CellRangeAddressBase other) {
+        return this._firstRow <= other._firstRow &&
+                this._lastRow >= other._lastRow &&
+                this._firstCol <= other._firstCol &&
+                this._lastCol >= other._lastCol;
+    }
+
     /**
      * Useful for logic like table/range styling, where some elements apply 
based on relative position in a range.
      * @return set of {@link CellPosition}s occupied by the given coordinates. 
 Empty if the coordinates are not in the range, never null.
diff --git 
a/poi/src/test/java/org/apache/poi/ss/usermodel/BaseTestRangeCopier.java 
b/poi/src/test/java/org/apache/poi/ss/usermodel/BaseTestRangeCopier.java
index 6239eb22b5..5ba489385b 100644
--- a/poi/src/test/java/org/apache/poi/ss/usermodel/BaseTestRangeCopier.java
+++ b/poi/src/test/java/org/apache/poi/ss/usermodel/BaseTestRangeCopier.java
@@ -152,9 +152,27 @@ public abstract class BaseTestRangeCopier {
         transSheetRangeCopier.copyRange(tileRange, tileRange, false, true);
         assertEquals(cellContent, getCellContent(destSheet, "D6"));
         assertFalse(destSheet.getMergedRegions().isEmpty());
-        destSheet.getMergedRegions().forEach((mergedRegion) -> {
-            assertEquals(mergedRangeAddress, mergedRegion);
-        });
+        destSheet.getMergedRegions().forEach(mergedRegion ->
+                assertEquals(mergedRangeAddress, mergedRegion)
+        );
+    }
+
+    @Test
+    void testSameSheetMergedRanges() {
+        String cellContent = "D6 merged to E7";
+
+        // create cell merged from D6 to E7
+        CellRangeAddress mergedRangeAddress = new CellRangeAddress(5,6,3,4);
+        Cell cell = sheet1.createRow(5).createCell(3);
+        cell.setCellValue(cellContent);
+        sheet1.addMergedRegion(mergedRangeAddress);
+
+        CellRangeAddress tileRange = CellRangeAddress.valueOf("D6:E7");
+        CellRangeAddress targetRange = CellRangeAddress.valueOf("D8:E9");
+        rangeCopier.copyRange(tileRange, targetRange, false, true);
+        assertEquals(cellContent, getCellContent(sheet1, "D8"));
+        assertFalse(sheet1.getMergedRegions().isEmpty());
+        
assertTrue(sheet1.getMergedRegions().stream().anyMatch(targetRange::equals));
     }
 
    protected static String getCellContent(Sheet sheet, String coordinates) {


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to