Patch from Lyle for bug 52903: HSSFSheet.shiftRows does not shift hyperlink references

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1717069 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Javen O'Neal 2015-11-29 14:45:24 +00:00
parent 5ee6c34217
commit 79e099f5fa
3 changed files with 175 additions and 109 deletions

View File

@ -1376,6 +1376,7 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
* @param endRow the row to end shifting
* @param n the number of rows to shift
*/
@Override
public void shiftRows(int startRow, int endRow, int n) {
shiftRows(startRow, endRow, n, false, false);
}
@ -1397,6 +1398,7 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
* @param copyRowHeight whether to copy the row height during the shift
* @param resetOriginalRowHeight whether to set the original row's height to the default
*/
@Override
public void shiftRows(int startRow, int endRow, int n, boolean copyRowHeight, boolean resetOriginalRowHeight) {
shiftRows(startRow, endRow, n, copyRowHeight, resetOriginalRowHeight, true);
}
@ -1422,6 +1424,9 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
public void shiftRows(int startRow, int endRow, int n,
boolean copyRowHeight, boolean resetOriginalRowHeight, boolean moveComments) {
int s, inc;
if (endRow < startRow) {
throw new IllegalArgumentException("startRow must be less than or equal to endRow. To shift rows up, use n<0.");
}
if (n < 0) {
s = startRow;
inc = 1;
@ -1433,13 +1438,30 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
return;
}
// Shift comments
if (moveComments) {
_sheet.getNoteRecords();
}
// Shift Merged Regions
shiftMerged(startRow, endRow, n, true);
// Shift Row Breaks
_sheet.getPageSettings().shiftRowBreaks(startRow, endRow, n);
// Delete overwritten hyperlinks
final int firstOverwrittenRow = startRow + n;
final int lastOverwrittenRow = endRow + n;
for (HSSFHyperlink link : getHyperlinkList()) {
// If hyperlink is fully contained in the rows that will be overwritten, delete the hyperlink
if (firstOverwrittenRow <= link.getFirstRow() &&
link.getFirstRow() <= lastOverwrittenRow &&
lastOverwrittenRow <= link.getLastRow() &&
link.getLastRow() <= lastOverwrittenRow) {
removeHyperlink(link);
}
}
for (int rowNum = s; rowNum >= startRow && rowNum <= endRow && rowNum >= 0 && rowNum < 65536; rowNum += inc) {
HSSFRow row = getRow(rowNum);
// notify all cells in this row that we are going to shift them,
@ -1453,7 +1475,7 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
// Remove all the old cells from the row we'll
// be writing too, before we start overwriting
// be writing to, before we start overwriting
// any cells. This avoids issues with cells
// changing type, and records not being correctly
// overwritten
@ -1475,13 +1497,13 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
// the destination row
for (Iterator<Cell> cells = row.cellIterator(); cells.hasNext(); ) {
HSSFCell cell = (HSSFCell) cells.next();
HSSFHyperlink link = cell.getHyperlink();
row.removeCell(cell);
CellValueRecordInterface cellRecord = cell.getCellValueRecord();
cellRecord.setRow(rowNum + n);
row2Replace.createCellFromRecord(cellRecord);
_sheet.addValueRecord(rowNum + n, cellRecord);
HSSFHyperlink link = cell.getHyperlink();
if (link != null) {
link.setFirstRow(link.getFirstRow() + n);
link.setLastRow(link.getLastRow() + n);
@ -2059,6 +2081,35 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
return hyperlinkList;
}
/**
* Remove the underlying HyperlinkRecord from this sheet.
* If multiple HSSFHyperlinks refer to the same HyperlinkRecord, all HSSFHyperlinks will be removed.
*
* @param link the HSSFHyperlink wrapper around the HyperlinkRecord to remove
*/
protected void removeHyperlink(HSSFHyperlink link) {
removeHyperlink(link.record);
}
/**
* Remove the underlying HyperlinkRecord from this sheet
*
* @param link the underlying HyperlinkRecord to remove from this sheet
*/
protected void removeHyperlink(HyperlinkRecord link) {
for (Iterator<RecordBase> it = _sheet.getRecords().iterator(); it.hasNext();) {
RecordBase rec = it.next();
if (rec instanceof HyperlinkRecord) {
HyperlinkRecord recLink = (HyperlinkRecord) rec;
if (link == recLink) {
it.remove();
// if multiple HSSFHyperlinks refer to the same record
return;
}
}
}
}
public HSSFSheetConditionalFormatting getSheetConditionalFormatting() {
return new HSSFSheetConditionalFormatting(this);
}

View File

@ -23,12 +23,7 @@ import java.io.IOException;
import org.apache.poi.ss.usermodel.BaseTestSheetShiftRows;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Comment;
import org.apache.poi.ss.usermodel.CreationHelper;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.Hyperlink;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
@ -399,106 +394,4 @@ public final class TestXSSFSheetShiftRows extends BaseTestSheetShiftRows {
wb.close();
}
@Test
public void testBug46742_shiftHyperlinks() throws IOException {
XSSFWorkbook wb = new XSSFWorkbook();
Sheet sheet = wb.createSheet("test");
Row row = sheet.createRow(0);
// How to create hyperlinks
// https://poi.apache.org/spreadsheet/quick-guide.html#Hyperlinks
XSSFCreationHelper helper = wb.getCreationHelper();
CellStyle hlinkStyle = wb.createCellStyle();
Font hlinkFont = wb.createFont();
hlinkFont.setUnderline(Font.U_SINGLE);
hlinkFont.setColor(IndexedColors.BLUE.getIndex());
hlinkStyle.setFont(hlinkFont);
// 3D relative document link
Cell cell = row.createCell(0);
cell.setCellStyle(hlinkStyle);
createHyperlink(helper, cell, Hyperlink.LINK_DOCUMENT, "test!E1");
// URL
cell = row.createCell(1);
cell.setCellStyle(hlinkStyle);
createHyperlink(helper, cell, Hyperlink.LINK_URL, "http://poi.apache.org/");
// row0 will be shifted on top of row1, so this URL should be removed from the workbook
Row overwrittenRow = sheet.createRow(3);
cell = overwrittenRow.createCell(2);
cell.setCellStyle(hlinkStyle);
createHyperlink(helper, cell, Hyperlink.LINK_EMAIL, "mailto:poi@apache.org");
// hyperlinks on this row are unaffected by the row shifting, so the hyperlinks should not move
Row unaffectedRow = sheet.createRow(20);
cell = unaffectedRow.createCell(3);
cell.setCellStyle(hlinkStyle);
createHyperlink(helper, cell, Hyperlink.LINK_FILE, "54524.xlsx");
cell = wb.createSheet("other").createRow(0).createCell(0);
cell.setCellStyle(hlinkStyle);
createHyperlink(helper, cell, Hyperlink.LINK_URL, "http://apache.org/");
int startRow = 0;
int endRow = 0;
int n = 3;
sheet.shiftRows(startRow, endRow, n);
XSSFWorkbook read = XSSFTestDataSamples.writeOutAndReadBack(wb);
wb.close();
XSSFSheet sh = read.getSheet("test");
Row shiftedRow = sh.getRow(3);
// document link anchored on a shifted cell should be moved
// Note that hyperlinks do not track what they point to, so this hyperlink should still refer to test!E1
verifyHyperlink(shiftedRow.getCell(0), Hyperlink.LINK_DOCUMENT, "test!E1");
// URL, EMAIL, and FILE links anchored on a shifted cell should be moved
verifyHyperlink(shiftedRow.getCell(1), Hyperlink.LINK_URL, "http://poi.apache.org/");
// Make sure hyperlinks were moved and not copied
assertNull(sh.getRow(0));
// Make sure hyperlink in overwritten row is deleted
assertEquals(3, sh.getHyperlinkList().size());
for (XSSFHyperlink link : sh.getHyperlinkList()) {
if ("C4".equals(link.getCellRef())) {
fail("Row 4, including the hyperlink at C4, should have been deleted when Row 1 was shifted on top of it.");
}
}
// Make sure unaffected rows are not shifted
Cell unaffectedCell = sh.getRow(20).getCell(3);
assertTrue(cellHasHyperlink(unaffectedCell));
verifyHyperlink(unaffectedCell, Hyperlink.LINK_FILE, "54524.xlsx");
// Make sure cells on other sheets are not affected
unaffectedCell = read.getSheet("other").getRow(0).getCell(0);
assertTrue(cellHasHyperlink(unaffectedCell));
verifyHyperlink(unaffectedCell, Hyperlink.LINK_URL, "http://apache.org/");
read.close();
}
private void createHyperlink(CreationHelper helper, Cell cell, int linkType, String ref) {
cell.setCellValue(ref);
Hyperlink link = helper.createHyperlink(linkType);
link.setAddress(ref);
cell.setHyperlink(link);
}
private void verifyHyperlink(Cell cell, int linkType, String ref) {
assertTrue(cellHasHyperlink(cell));
Hyperlink link = cell.getHyperlink();
assertEquals(linkType, link.getType());
assertEquals(ref, link.getAddress());
}
private boolean cellHasHyperlink(Cell cell) {
return (cell != null) && (cell.getHyperlink() != null);
}
}

View File

@ -21,11 +21,13 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.IOException;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.ITestDataProvider;
import org.apache.poi.ss.util.CellAddress;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellReference;
import org.junit.Test;
@ -456,4 +458,124 @@ public abstract class BaseTestSheetShiftRows {
wb.close();
}
/**
* Unified test for:
* bug 46742: XSSFSheet.shiftRows should shift hyperlinks
* bug 52903: HSSFSheet.shiftRows should shift hyperlinks
*
* @throws IOException
*/
@Test
public void testBug46742_52903_shiftHyperlinks() throws IOException {
Workbook wb = _testDataProvider.createWorkbook();
Sheet sheet = wb.createSheet("test");
Row row = sheet.createRow(0);
// How to create hyperlinks
// https://poi.apache.org/spreadsheet/quick-guide.html#Hyperlinks
CreationHelper helper = wb.getCreationHelper();
CellStyle hlinkStyle = wb.createCellStyle();
Font hlinkFont = wb.createFont();
hlinkFont.setUnderline(Font.U_SINGLE);
hlinkFont.setColor(IndexedColors.BLUE.getIndex());
hlinkStyle.setFont(hlinkFont);
// 3D relative document link
// CellAddress=A1, shifted to A4
Cell cell = row.createCell(0);
cell.setCellStyle(hlinkStyle);
createHyperlink(helper, cell, Hyperlink.LINK_DOCUMENT, "test!E1");
// URL
cell = row.createCell(1);
// CellAddress=B1, shifted to B4
cell.setCellStyle(hlinkStyle);
createHyperlink(helper, cell, Hyperlink.LINK_URL, "http://poi.apache.org/");
// row0 will be shifted on top of row1, so this URL should be removed from the workbook
Row overwrittenRow = sheet.createRow(3);
cell = overwrittenRow.createCell(2);
// CellAddress=C4, will be overwritten (deleted)
cell.setCellStyle(hlinkStyle);
createHyperlink(helper, cell, Hyperlink.LINK_EMAIL, "mailto:poi@apache.org");
// hyperlinks on this row are unaffected by the row shifting, so the hyperlinks should not move
Row unaffectedRow = sheet.createRow(20);
cell = unaffectedRow.createCell(3);
// CellAddress=D21, will be unaffected
cell.setCellStyle(hlinkStyle);
createHyperlink(helper, cell, Hyperlink.LINK_FILE, "54524.xlsx");
cell = wb.createSheet("other").createRow(0).createCell(0);
// CellAddress=Other!A1, will be unaffected
cell.setCellStyle(hlinkStyle);
createHyperlink(helper, cell, Hyperlink.LINK_URL, "http://apache.org/");
int startRow = 0;
int endRow = 0;
int n = 3;
sheet.shiftRows(startRow, endRow, n);
Workbook read = _testDataProvider.writeOutAndReadBack(wb);
wb.close();
Sheet sh = read.getSheet("test");
Row shiftedRow = sh.getRow(3);
// document link anchored on a shifted cell should be moved
// Note that hyperlinks do not track what they point to, so this hyperlink should still refer to test!E1
verifyHyperlink(shiftedRow.getCell(0), Hyperlink.LINK_DOCUMENT, "test!E1");
// URL, EMAIL, and FILE links anchored on a shifted cell should be moved
verifyHyperlink(shiftedRow.getCell(1), Hyperlink.LINK_URL, "http://poi.apache.org/");
// Make sure hyperlinks were moved and not copied
assertNull("Document hyperlink should be moved, not copied", sh.getHyperlink(0, 0));
assertNull("URL hyperlink should be moved, not copied", sh.getHyperlink(0, 1));
// Make sure hyperlink in overwritten row is deleted
System.out.println(sh.getHyperlinkList());
assertEquals(3, sh.getHyperlinkList().size());
CellAddress unexpectedLinkAddress = new CellAddress("C4");
for (Hyperlink link : sh.getHyperlinkList()) {
final CellAddress linkAddress = new CellAddress(link.getFirstRow(), link.getFirstColumn());
System.out.println(linkAddress.formatAsString());
if (linkAddress.equals(unexpectedLinkAddress)) {
fail("Row 4, including the hyperlink at C4, should have " +
"been deleted when Row 1 was shifted on top of it.");
}
}
// Make sure unaffected rows are not shifted
Cell unaffectedCell = sh.getRow(20).getCell(3);
assertTrue(cellHasHyperlink(unaffectedCell));
verifyHyperlink(unaffectedCell, Hyperlink.LINK_FILE, "54524.xlsx");
// Make sure cells on other sheets are not affected
unaffectedCell = read.getSheet("other").getRow(0).getCell(0);
assertTrue(cellHasHyperlink(unaffectedCell));
verifyHyperlink(unaffectedCell, Hyperlink.LINK_URL, "http://apache.org/");
read.close();
}
private void createHyperlink(CreationHelper helper, Cell cell, int linkType, String ref) {
cell.setCellValue(ref);
Hyperlink link = helper.createHyperlink(linkType);
link.setAddress(ref);
cell.setHyperlink(link);
}
private void verifyHyperlink(Cell cell, int linkType, String ref) {
assertTrue(cellHasHyperlink(cell));
Hyperlink link = cell.getHyperlink();
assertEquals(linkType, link.getType());
assertEquals(ref, link.getAddress());
}
private boolean cellHasHyperlink(Cell cell) {
return (cell != null) && (cell.getHyperlink() != null);
}
}