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 <vik.ozerov+git@gmail.com>
This commit is contained in:
vo0ff 2025-12-02 00:38:04 +03:00 committed by GitHub
parent c953a8ff22
commit 006e49765c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 61 additions and 7 deletions

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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.

View File

@ -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) {