From 7bcf6d5a6d1ae77aef4e3f9ff6d6b4d23fb6a8b5 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Mon, 9 Aug 2021 21:57:40 +0000 Subject: [PATCH] add support for reading hyperlinks that have cell refs that are area references (very limited update or write support as yet) git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1892141 13f79535-47bb-0310-9956-ffa450edef68 --- .../poi/xssf/usermodel/XSSFHyperlink.java | 49 +++++++++---- .../apache/poi/xssf/usermodel/XSSFSheet.java | 24 ++----- .../poi/xssf/usermodel/TestXSSFHyperlink.java | 66 +++++++++++++++++- test-data/spreadsheet/sharedhyperlink.xlsx | Bin 0 -> 9080 bytes 4 files changed, 106 insertions(+), 33 deletions(-) create mode 100644 test-data/spreadsheet/sharedhyperlink.xlsx diff --git a/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFHyperlink.java b/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFHyperlink.java index 6c5f792065..8913859a2f 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFHyperlink.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFHyperlink.java @@ -22,7 +22,9 @@ import java.net.URISyntaxException; import org.apache.poi.common.usermodel.HyperlinkType; import org.apache.poi.openxml4j.opc.PackagePart; import org.apache.poi.openxml4j.opc.PackageRelationship; +import org.apache.poi.ss.SpreadsheetVersion; import org.apache.poi.ss.usermodel.Hyperlink; +import org.apache.poi.ss.util.AreaReference; import org.apache.poi.ss.util.CellReference; import org.apache.poi.util.Internal; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTHyperlink; @@ -264,20 +266,32 @@ public class XSSFHyperlink implements Hyperlink { public void setCellReference(String ref) { _ctHyperlink.setRef(ref); } + @Internal public void setCellReference(CellReference ref) { setCellReference(ref.formatAsString()); } - private CellReference buildCellReference() { + private CellReference buildFirstCellReference() { + return buildCellReference(false); + } + + private CellReference buildLastCellReference() { + return buildCellReference(true); + } + + private CellReference buildCellReference(boolean lastCell) { String ref = _ctHyperlink.getRef(); if (ref == null) { ref = "A1"; } + if (ref.contains(":")) { + AreaReference area = new AreaReference(ref, SpreadsheetVersion.EXCEL2007); + return lastCell ? area.getLastCell() : area.getFirstCell(); + } return new CellReference(ref); } - /** * Return the column of the first cell that contains the hyperlink * @@ -285,7 +299,7 @@ public class XSSFHyperlink implements Hyperlink { */ @Override public int getFirstColumn() { - return buildCellReference().getCol(); + return buildFirstCellReference().getCol(); } @@ -296,7 +310,7 @@ public class XSSFHyperlink implements Hyperlink { */ @Override public int getLastColumn() { - return buildCellReference().getCol(); + return buildLastCellReference().getCol(); } /** @@ -306,7 +320,7 @@ public class XSSFHyperlink implements Hyperlink { */ @Override public int getFirstRow() { - return buildCellReference().getRow(); + return buildFirstCellReference().getRow(); } @@ -317,7 +331,7 @@ public class XSSFHyperlink implements Hyperlink { */ @Override public int getLastRow() { - return buildCellReference().getRow(); + return buildLastCellReference().getRow(); } /** @@ -327,18 +341,19 @@ public class XSSFHyperlink implements Hyperlink { */ @Override public void setFirstColumn(int col) { - setCellReference(new CellReference( getFirstRow(), col )); + String firstCellRef = CellReference.convertNumToColString(col) + (getFirstRow() + 1); + setCellRange(firstCellRef + ":" + buildLastCellReference().formatAsString()); } /** * Set the column of the last cell that contains the hyperlink. - * For XSSF, a Hyperlink may only reference one cell * * @param col the 0-based column of the last cell that contains the hyperlink */ @Override public void setLastColumn(int col) { - setFirstColumn(col); + String lastCellRef = CellReference.convertNumToColString(col) + (getLastRow() + 1); + setCellRange(buildFirstCellReference().formatAsString() + ":" + lastCellRef); } /** @@ -348,18 +363,28 @@ public class XSSFHyperlink implements Hyperlink { */ @Override public void setFirstRow(int row) { - setCellReference(new CellReference( row, getFirstColumn() )); + String firstCellRef = CellReference.convertNumToColString(getFirstColumn()) + (row + 1); + setCellRange(firstCellRef + ":" + buildLastCellReference().formatAsString()); } /** * Set the row of the last cell that contains the hyperlink. - * For XSSF, a Hyperlink may only reference one cell * * @param row the 0-based row of the last cell that contains the hyperlink */ @Override public void setLastRow(int row) { - setFirstRow(row); + String lastCellRef = CellReference.convertNumToColString(getLastColumn()) + (row + 1); + setCellRange(buildFirstCellReference().formatAsString() + ":" + lastCellRef); + } + + private void setCellRange(String range) { + AreaReference ref = new AreaReference(range, SpreadsheetVersion.EXCEL2007); + if(ref.isSingleCell()) { + setCellReference(ref.getFirstCell()); + } else { + setCellReference(ref.formatAsString()); + } } /** diff --git a/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFSheet.java b/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFSheet.java index 27ad23611e..bf3e7396d3 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFSheet.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFSheet.java @@ -55,23 +55,7 @@ import org.apache.poi.poifs.crypt.HashAlgorithm; import org.apache.poi.ss.SpreadsheetVersion; import org.apache.poi.ss.formula.FormulaShifter; import org.apache.poi.ss.formula.SheetNameFormatter; -import org.apache.poi.ss.usermodel.Cell; -import org.apache.poi.ss.usermodel.CellCopyPolicy; -import org.apache.poi.ss.usermodel.CellRange; -import org.apache.poi.ss.usermodel.CellStyle; -import org.apache.poi.ss.usermodel.CellType; -import org.apache.poi.ss.usermodel.DataValidation; -import org.apache.poi.ss.usermodel.DataValidationHelper; -import org.apache.poi.ss.usermodel.Font; -import org.apache.poi.ss.usermodel.Footer; -import org.apache.poi.ss.usermodel.FormulaEvaluator; -import org.apache.poi.ss.usermodel.Header; -import org.apache.poi.ss.usermodel.IgnoredErrorType; -import org.apache.poi.ss.usermodel.Name; -import org.apache.poi.ss.usermodel.Row; -import org.apache.poi.ss.usermodel.Sheet; -import org.apache.poi.ss.usermodel.Table; -import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.usermodel.*; import org.apache.poi.ss.util.AreaReference; import org.apache.poi.ss.util.CellAddress; import org.apache.poi.ss.util.CellRangeAddress; @@ -846,9 +830,9 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet { */ @Override public XSSFHyperlink getHyperlink(CellAddress addr) { - String ref = addr.formatAsString(); - for(XSSFHyperlink hyperlink : hyperlinks) { - if(hyperlink.getCellRef().equals(ref)) { + for (XSSFHyperlink hyperlink : getHyperlinkList()) { + if (addr.getRow() >= hyperlink.getFirstRow() && addr.getRow() <= hyperlink.getLastRow() + && addr.getColumn() >= hyperlink.getFirstColumn() && addr.getColumn() <= hyperlink.getLastColumn()) { return hyperlink; } } diff --git a/poi-ooxml/src/test/java/org/apache/poi/xssf/usermodel/TestXSSFHyperlink.java b/poi-ooxml/src/test/java/org/apache/poi/xssf/usermodel/TestXSSFHyperlink.java index 203fbba80c..e95a07d20d 100644 --- a/poi-ooxml/src/test/java/org/apache/poi/xssf/usermodel/TestXSSFHyperlink.java +++ b/poi-ooxml/src/test/java/org/apache/poi/xssf/usermodel/TestXSSFHyperlink.java @@ -51,6 +51,27 @@ public final class TestXSSFHyperlink extends BaseTestHyperlink { } } + + @Test + public void readSharedHyperlink() throws IOException { + try (XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("sharedhyperlink.xlsx")) { + XSSFSheet sheet = wb.getSheetAt(0); + + XSSFHyperlink hyperlink3 = sheet.getHyperlink(new CellAddress("A3")); + XSSFHyperlink hyperlink4 = sheet.getHyperlink(new CellAddress("A4")); + XSSFHyperlink hyperlink5 = sheet.getHyperlink(new CellAddress("A5")); + assertNotNull(hyperlink3, "hyperlink found?"); + assertEquals(hyperlink3, hyperlink4); + assertEquals(hyperlink3, hyperlink5); + + assertEquals("A3:A5", hyperlink3.getCellRef()); + assertEquals(0, hyperlink3.getFirstColumn()); + assertEquals(0, hyperlink3.getLastColumn()); + assertEquals(2, hyperlink3.getFirstRow()); + assertEquals(4, hyperlink3.getLastRow()); + } + } + @Test void testCreate() throws Exception { XSSFWorkbook workbook = new XSSFWorkbook(); @@ -331,7 +352,7 @@ public final class TestXSSFHyperlink extends BaseTestHyperlink { } @Test - void test() throws IOException { + void testCellStyle() throws IOException { XSSFWorkbook wb = new XSSFWorkbook(); CreationHelper createHelper = wb.getCreationHelper(); @@ -372,4 +393,47 @@ public final class TestXSSFHyperlink extends BaseTestHyperlink { wb.close(); wbBack.close(); } + + @Test + void testChangeReference() throws IOException { + try (XSSFWorkbook wb = new XSSFWorkbook()) { + XSSFHyperlink hyperlink = new XSSFHyperlink(HyperlinkType.URL); + hyperlink.setCellReference("B2"); + assertEquals(1, hyperlink.getFirstRow()); + assertEquals(1, hyperlink.getLastRow()); + assertEquals(1, hyperlink.getFirstColumn()); + assertEquals(1, hyperlink.getLastColumn()); + hyperlink.setFirstRow(0); + assertEquals("B1:B2", hyperlink.getCellRef()); + assertEquals(0, hyperlink.getFirstRow()); + assertEquals(1, hyperlink.getLastRow()); + assertEquals(1, hyperlink.getFirstColumn()); + assertEquals(1, hyperlink.getLastColumn()); + hyperlink.setLastRow(2); + assertEquals("B1:B3", hyperlink.getCellRef()); + assertEquals(0, hyperlink.getFirstRow()); + assertEquals(2, hyperlink.getLastRow()); + assertEquals(1, hyperlink.getFirstColumn()); + assertEquals(1, hyperlink.getLastColumn()); + hyperlink.setFirstColumn(0); + assertEquals("A1:B3", hyperlink.getCellRef()); + assertEquals(0, hyperlink.getFirstRow()); + assertEquals(2, hyperlink.getLastRow()); + assertEquals(0, hyperlink.getFirstColumn()); + assertEquals(1, hyperlink.getLastColumn()); + hyperlink.setLastColumn(2); + assertEquals("A1:C3", hyperlink.getCellRef()); + assertEquals(0, hyperlink.getFirstRow()); + assertEquals(2, hyperlink.getLastRow()); + assertEquals(0, hyperlink.getFirstColumn()); + assertEquals(2, hyperlink.getLastColumn()); + hyperlink.setFirstColumn(2); + hyperlink.setFirstRow(2); + assertEquals("C3", hyperlink.getCellRef()); + assertEquals(2, hyperlink.getFirstRow()); + assertEquals(2, hyperlink.getLastRow()); + assertEquals(2, hyperlink.getFirstColumn()); + assertEquals(2, hyperlink.getLastColumn()); + } + } } diff --git a/test-data/spreadsheet/sharedhyperlink.xlsx b/test-data/spreadsheet/sharedhyperlink.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..d3149977072ae44db59fd6be33a8a7ab64aaa58f GIT binary patch literal 9080 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 PqXEXDvLK%F$G`srtJrd; literal 0 HcmV?d00001