mirror of https://github.com/apache/poi.git
bug 58557: fix from Alessandro Guarascio, shift hyperlinks when shifting rows on an XSSFSheet
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1711185 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
eb1e1a28e8
commit
2535839b70
|
@ -23,6 +23,7 @@ import org.apache.poi.openxml4j.opc.PackagePart;
|
|||
import org.apache.poi.openxml4j.opc.PackageRelationship;
|
||||
import org.apache.poi.ss.usermodel.Hyperlink;
|
||||
import org.apache.poi.ss.util.CellReference;
|
||||
import org.apache.poi.util.Internal;
|
||||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTHyperlink;
|
||||
|
||||
/**
|
||||
|
@ -33,8 +34,8 @@ import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTHyperlink;
|
|||
public class XSSFHyperlink implements Hyperlink {
|
||||
private int _type;
|
||||
private PackageRelationship _externalRel;
|
||||
private CTHyperlink _ctHyperlink;
|
||||
private String _location;
|
||||
private CTHyperlink _ctHyperlink; //contains a reference to the cell where the hyperlink is anchored, getRef()
|
||||
private String _location; //what the hyperlink refers to
|
||||
|
||||
/**
|
||||
* Create a new XSSFHyperlink. This method is protected to be used only by XSSFCreationHelper
|
||||
|
@ -94,6 +95,7 @@ public class XSSFHyperlink implements Hyperlink {
|
|||
/**
|
||||
* @return the underlying CTHyperlink object
|
||||
*/
|
||||
@Internal
|
||||
public CTHyperlink getCTHyperlink() {
|
||||
return _ctHyperlink;
|
||||
}
|
||||
|
@ -219,7 +221,8 @@ public class XSSFHyperlink implements Hyperlink {
|
|||
/**
|
||||
* Assigns this hyperlink to the given cell reference
|
||||
*/
|
||||
protected void setCellReference(String ref) {
|
||||
@Internal
|
||||
public void setCellReference(String ref) {
|
||||
_ctHyperlink.setRef(ref);
|
||||
}
|
||||
protected void setCellReference(CellReference ref) {
|
||||
|
|
|
@ -25,6 +25,7 @@ import java.io.InputStream;
|
|||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
|
@ -669,6 +670,13 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
|
|||
vml == null ? null : vml.findCommentShape(row, column));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a Hyperlink in this sheet anchored at row, column
|
||||
*
|
||||
* @param row
|
||||
* @param column
|
||||
* @return hyperlink if there is a hyperlink anchored at row, column; otherwise returns null
|
||||
*/
|
||||
public XSSFHyperlink getHyperlink(int row, int column) {
|
||||
String ref = new CellReference(row, column).formatAsString();
|
||||
for(XSSFHyperlink hyperlink : hyperlinks) {
|
||||
|
@ -678,6 +686,15 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
|
|||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of Hyperlinks in this sheet
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public List<XSSFHyperlink> getHyperlinkList() {
|
||||
return Collections.unmodifiableList(hyperlinks);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private int[] getBreaks(CTPageBreak ctPageBreak) {
|
||||
|
@ -2610,6 +2627,7 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
|
|||
// remove row from _rows
|
||||
it.remove();
|
||||
|
||||
// FIXME: (performance optimization) this should be moved outside the for-loop so that comments only needs to be iterated over once.
|
||||
// also remove any comments associated with this row
|
||||
if(sheetComments != null){
|
||||
CTCommentList lst = sheetComments.getCTComments().getCommentList();
|
||||
|
@ -2624,6 +2642,16 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
|
|||
}
|
||||
}
|
||||
}
|
||||
// FIXME: (performance optimization) this should be moved outside the for-loop so that hyperlinks only needs to be iterated over once.
|
||||
// also remove any hyperlinks associated with this row
|
||||
if (hyperlinks != null) {
|
||||
for (XSSFHyperlink link : new ArrayList<XSSFHyperlink>(hyperlinks)) {
|
||||
CellReference ref = new CellReference(link.getCellRef());
|
||||
if (ref.getRow() == rownum) {
|
||||
hyperlinks.remove(link);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2707,6 +2735,7 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
|
|||
rowShifter.updateFormulas(shifter);
|
||||
rowShifter.shiftMerged(startRow, endRow, n);
|
||||
rowShifter.updateConditionalFormatting(shifter);
|
||||
rowShifter.updateHyperlinks(shifter);
|
||||
|
||||
//rebuild the _rows map
|
||||
SortedMap<Integer, XSSFRow> map = new TreeMap<Integer, XSSFRow>();
|
||||
|
@ -2902,6 +2931,9 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
|
|||
*/
|
||||
@Internal
|
||||
public void removeHyperlink(int row, int column) {
|
||||
// CTHyperlinks is regenerated from scratch when writing out the spreadsheet
|
||||
// so don't worry about maintaining hyperlinks and CTHyperlinks in parallel.
|
||||
// only maintain hyperlinks
|
||||
String ref = new CellReference(row, column).formatAsString();
|
||||
for (Iterator<XSSFHyperlink> it = hyperlinks.iterator(); it.hasNext();) {
|
||||
XSSFHyperlink hyperlink = it.next();
|
||||
|
|
|
@ -22,6 +22,7 @@ import java.util.HashSet;
|
|||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.poi.common.usermodel.Hyperlink;
|
||||
import org.apache.poi.ss.formula.FormulaParseException;
|
||||
import org.apache.poi.ss.formula.FormulaParser;
|
||||
import org.apache.poi.ss.formula.FormulaRenderer;
|
||||
|
@ -38,6 +39,7 @@ import org.apache.poi.util.POILogFactory;
|
|||
import org.apache.poi.util.POILogger;
|
||||
import org.apache.poi.xssf.usermodel.XSSFCell;
|
||||
import org.apache.poi.xssf.usermodel.XSSFEvaluationWorkbook;
|
||||
import org.apache.poi.xssf.usermodel.XSSFHyperlink;
|
||||
import org.apache.poi.xssf.usermodel.XSSFName;
|
||||
import org.apache.poi.xssf.usermodel.XSSFRow;
|
||||
import org.apache.poi.xssf.usermodel.XSSFSheet;
|
||||
|
@ -280,6 +282,30 @@ public final class XSSFRowShifter {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shift the Hyperlink anchors (not the hyperlink text, even if the hyperlink
|
||||
* is of type LINK_DOCUMENT and refers to a cell that was shifted). Hyperlinks
|
||||
* do not track the content they point to.
|
||||
*
|
||||
* @param shifter
|
||||
*/
|
||||
public void updateHyperlinks(FormulaShifter shifter) {
|
||||
int sheetIndex = sheet.getWorkbook().getSheetIndex(sheet);
|
||||
List<XSSFHyperlink> hyperlinkList = sheet.getHyperlinkList();
|
||||
|
||||
for (XSSFHyperlink hyperlink : hyperlinkList) {
|
||||
String cellRef = hyperlink.getCellRef();
|
||||
CellRangeAddress cra = CellRangeAddress.valueOf(cellRef);
|
||||
CellRangeAddress shiftedRange = shiftRange(shifter, cra, sheetIndex);
|
||||
if (shiftedRange != null && shiftedRange != cra) {
|
||||
// shiftedRange should not be null. If shiftedRange is null, that means
|
||||
// that a hyperlink wasn't deleted at the beginning of shiftRows when
|
||||
// identifying rows that should be removed because they will be overwritten
|
||||
hyperlink.setCellReference(shiftedRange.formatAsString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static CellRangeAddress shiftRange(FormulaShifter shifter, CellRangeAddress cra, int currentExternSheetIx) {
|
||||
// FormulaShifter works well in terms of Ptgs - so convert CellRangeAddress to AreaPtg (and back) here
|
||||
|
|
|
@ -21,7 +21,12 @@ 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;
|
||||
|
@ -366,4 +371,105 @@ public final class TestXSSFSheetShiftRows extends BaseTestSheetShiftRows {
|
|||
|
||||
wb.close();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue