diff --git a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFCategoryDataSource.java b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFCategoryDataSource.java index a65db8409..a21852b62 100644 --- a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFCategoryDataSource.java +++ b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFCategoryDataSource.java @@ -23,4 +23,23 @@ import org.apache.poi.util.Beta; @Beta public interface XDDFCategoryDataSource extends XDDFDataSource { + @Override + default int getColIndex() { + return 0; + } + + @Override + default boolean isNumeric() { + return false; + } + + @Override + default boolean isReference() { + return true; + } + + @Override + default String getDataRangeReference() { + return getFormula(); + } } diff --git a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFDataSource.java b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFDataSource.java index 1ab617571..d64f62bfb 100644 --- a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFDataSource.java +++ b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFDataSource.java @@ -34,4 +34,6 @@ public interface XDDFDataSource { int getColIndex(); String getDataRangeReference(); + + String getFormula(); } diff --git a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFDataSourcesFactory.java b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFDataSourcesFactory.java index be8a34ad2..a09c1f920 100644 --- a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFDataSourcesFactory.java +++ b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFDataSourcesFactory.java @@ -41,39 +41,50 @@ public class XDDFDataSourcesFactory { } public static XDDFCategoryDataSource fromDataSource(final CTAxDataSource categoryDS) { - return new XDDFCategoryDataSource() { - private CTStrData category = (CTStrData) categoryDS.getStrRef().getStrCache().copy(); + if (categoryDS.getStrRef() == null) { + return new XDDFCategoryDataSource() { + private CTNumData category = (CTNumData) categoryDS.getNumRef().getNumCache().copy(); - @Override - public boolean isNumeric() { - return false; - } + @Override + public boolean isNumeric() { + return true; + } - @Override - public boolean isReference() { - return true; - } + @Override + public String getFormula() { + return categoryDS.getNumRef().getF(); + } - @Override - public int getPointCount() { - return (int) category.getPtCount().getVal(); - } + @Override + public int getPointCount() { + return (int) category.getPtCount().getVal(); + } - @Override - public String getPointAt(int index) { - return category.getPtArray(index).getV(); - } + @Override + public String getPointAt(int index) { + return category.getPtArray(index).getV(); + } + }; + } else { + return new XDDFCategoryDataSource() { + private CTStrData category = (CTStrData) categoryDS.getStrRef().getStrCache().copy(); - @Override - public String getDataRangeReference() { - return categoryDS.getStrRef().getF(); - } + @Override + public String getFormula() { + return categoryDS.getStrRef().getF(); + } - @Override - public int getColIndex() { - return 0; - } - }; + @Override + public int getPointCount() { + return (int) category.getPtCount().getVal(); + } + + @Override + public String getPointAt(int index) { + return category.getPtArray(index).getV(); + } + }; + } } public static XDDFNumericalDataSource fromDataSource(final CTNumDataSource valuesDS) { @@ -81,6 +92,11 @@ public class XDDFDataSourcesFactory { private CTNumData values = (CTNumData) valuesDS.getNumRef().getNumCache().copy(); private String formatCode = values.isSetFormatCode() ? values.getFormatCode() : null; + @Override + public String getFormula() { + return valuesDS.getNumRef().getF(); + } + @Override public String getFormatCode() { return formatCode; @@ -124,7 +140,7 @@ public class XDDFDataSourcesFactory { } public static XDDFNumericalDataSource fromArray(T[] elements, String dataRange) { - return new NumericalArrayDataSource(elements, dataRange); + return new NumericalArrayDataSource<>(elements, dataRange); } public static XDDFCategoryDataSource fromArray(String[] elements, String dataRange) { @@ -132,7 +148,7 @@ public class XDDFDataSourcesFactory { } public static XDDFNumericalDataSource fromArray(T[] elements, String dataRange, int col) { - return new NumericalArrayDataSource(elements, dataRange, col); + return new NumericalArrayDataSource<>(elements, dataRange, col); } public static XDDFCategoryDataSource fromArray(String[] elements, String dataRange, int col) { @@ -212,6 +228,11 @@ public class XDDFDataSourcesFactory { super(elements, dataRange, col); } + @Override + public String getFormula() { + return getDataRangeReference(); + } + @Override public String getFormatCode() { return formatCode; @@ -232,6 +253,11 @@ public class XDDFDataSourcesFactory { public StringArrayDataSource(String[] elements, String dataRange, int col) { super(elements, dataRange, col); } + + @Override + public String getFormula() { + return getDataRangeReference(); + } } private abstract static class AbstractCellRangeDataSource implements XDDFDataSource { @@ -290,6 +316,11 @@ public class XDDFDataSourcesFactory { super(sheet, cellRangeAddress); } + @Override + public String getFormula() { + return getDataRangeReference(); + } + private String formatCode; @Override @@ -324,6 +355,11 @@ public class XDDFDataSourcesFactory { super(sheet, cellRangeAddress); } + @Override + public String getFormula() { + return getDataRangeReference(); + } + @Override public String getPointAt(int index) { CellValue cellValue = getCellValueAt(index); diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/helpers/XSSFFormulaUtils.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/helpers/XSSFFormulaUtils.java index df3cfb383..926e2a2d8 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/helpers/XSSFFormulaUtils.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/helpers/XSSFFormulaUtils.java @@ -19,6 +19,10 @@ package org.apache.poi.xssf.usermodel.helpers; +import java.util.Iterator; +import java.util.List; + +import org.apache.poi.ooxml.POIXMLDocumentPart; import org.apache.poi.ss.formula.FormulaParser; import org.apache.poi.ss.formula.FormulaRenderer; import org.apache.poi.ss.formula.FormulaType; @@ -30,10 +34,14 @@ import org.apache.poi.ss.usermodel.CellType; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.xssf.usermodel.XSSFCell; +import org.apache.poi.xssf.usermodel.XSSFChart; +import org.apache.poi.xssf.usermodel.XSSFDrawing; import org.apache.poi.xssf.usermodel.XSSFEvaluationWorkbook; import org.apache.poi.xssf.usermodel.XSSFName; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCellFormula; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; /** * Utility to update formulas and named ranges when a sheet name was changed @@ -50,7 +58,7 @@ public final class XSSFFormulaUtils { } /** - * Update sheet name in all formulas and named ranges. + * Update sheet name in all charts, formulas and named ranges. * Called from {@link XSSFWorkbook#setSheetName(int, String)} *

*

@@ -81,6 +89,20 @@ public final class XSSFFormulaUtils { } } } + + // update charts + List rels = _wb.getSheetAt(sheetIndex).getRelations(); + for (POIXMLDocumentPart r : rels) { + if (r instanceof XSSFDrawing) { + XSSFDrawing dg = (XSSFDrawing) r; + Iterator it = dg.getCharts().iterator(); + while (it.hasNext()) { + XSSFChart chart = it.next(); + Node dom = chart.getCTChartSpace().getDomNode(); + updateDomSheetReference(dom, oldName, newName); + } + } + } } /** @@ -99,7 +121,9 @@ public final class XSSFFormulaUtils { updatePtg(ptg, oldName, newName); } String updatedFormula = FormulaRenderer.toFormulaString(_fpwb, ptgs); - if (!formula.equals(updatedFormula)) f.setStringValue(updatedFormula); + if (!formula.equals(updatedFormula)) { + f.setStringValue(updatedFormula); + } } } } @@ -119,10 +143,12 @@ public final class XSSFFormulaUtils { updatePtg(ptg, oldName, newName); } String updatedFormula = FormulaRenderer.toFormulaString(_fpwb, ptgs); - if (!formula.equals(updatedFormula)) name.setRefersToFormula(updatedFormula); + if (!formula.equals(updatedFormula)) { + name.setRefersToFormula(updatedFormula); + } } } - + private void updatePtg(Ptg ptg, String oldName, String newName) { if (ptg instanceof Pxg) { Pxg pxg = (Pxg)ptg; @@ -141,4 +167,32 @@ public final class XSSFFormulaUtils { } } } + + + /** + * Parse the DOM tree recursively searching for text containing reference to the old sheet name and replacing it. + * + * @param dom the XML node in which to perform the replacement. + * + * Code extracted from: Bug 54470 + */ + private void updateDomSheetReference(Node dom, final String oldName, final String newName) { + String value = dom.getNodeValue(); + if (value != null) { + // make sure the value contains the old sheet and not a similar sheet + // (ex: Valid: 'Sheet1'! or Sheet1! ; NotValid: 'Sheet1Test'! or Sheet1Test!) + if (value.contains(oldName+"!") || value.contains(oldName+"'!")) { + XSSFName temporary = _wb.createName(); + temporary.setRefersToFormula(value); + updateName(temporary, oldName, newName); + dom.setNodeValue(temporary.getRefersToFormula()); + _wb.removeName(temporary); + } + } + NodeList nl = dom.getChildNodes(); + for (int i = 0; i < nl.getLength(); i++) { + updateDomSheetReference(nl.item(i), oldName, newName); + } + } + } diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFWorkbook.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFWorkbook.java index 0c6a0adef..20dad4b84 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFWorkbook.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFWorkbook.java @@ -39,8 +39,8 @@ import java.util.List; import java.util.zip.CRC32; import org.apache.poi.POIDataSamples; -import org.apache.poi.ooxml.POIXMLProperties; import org.apache.poi.hssf.HSSFTestDataSamples; +import org.apache.poi.ooxml.POIXMLProperties; import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.openxml4j.opc.ContentTypes; import org.apache.poi.openxml4j.opc.OPCPackage; @@ -67,6 +67,8 @@ import org.apache.poi.ss.util.CellReference; import org.apache.poi.util.IOUtils; import org.apache.poi.util.LocaleUtil; import org.apache.poi.util.TempFile; +import org.apache.poi.xddf.usermodel.chart.XDDFBarChartData; +import org.apache.poi.xddf.usermodel.chart.XDDFChartData; import org.apache.poi.xssf.XSSFITestDataProvider; import org.apache.poi.xssf.XSSFTestDataSamples; import org.apache.poi.xssf.model.StylesTable; @@ -553,7 +555,9 @@ public final class TestXSSFWorkbook extends BaseTestXWorkbook { Sheet sheet = wb.getSheetAt(0); sheet.shiftRows(2, sheet.getLastRowNum(), 1, true, false); Row newRow = sheet.getRow(2); - if (newRow == null) newRow = sheet.createRow(2); + if (newRow == null) { + newRow = sheet.createRow(2); + } newRow.createCell(0).setCellValue(" Another Header"); wb.cloneSheet(0); @@ -667,8 +671,8 @@ public final class TestXSSFWorkbook extends BaseTestXWorkbook { XSSFWorkbook wb3 = XSSFTestDataSamples.writeOutAndReadBack(wb2); assertNotNull(wb3); sheet = wb3.getSheetAt(0); - row = sheet.getRow(2); - + row = sheet.getRow(2); + assertEquals("test1", row.getCell(3).getStringCellValue()); assertEquals("test2", row.getCell(4).getStringCellValue()); wb3.close(); @@ -700,6 +704,24 @@ public final class TestXSSFWorkbook extends BaseTestXWorkbook { } } + @Test + public void bug60509() throws Exception { + XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("60509.xlsx"); + assertSheetOrder(wb, "Sheet1", "Sheet2", "Sheet3"); + int sheetIndex = wb.getSheetIndex("Sheet1"); + wb.setSheetName(sheetIndex, "Sheet1-Renamed"); + Workbook read = XSSFTestDataSamples.writeOutAndReadBack(wb); + assertNotNull(read); + assertSheetOrder(read, "Sheet1-Renamed", "Sheet2", "Sheet3"); + XSSFSheet sheet = (XSSFSheet) read.getSheet("Sheet1-Renamed"); + XDDFChartData.Series series = sheet.getDrawingPatriarch().getCharts().get(0).getChartSeries().get(0).getSeries().get(0); + assertTrue("should be a bar chart data series", series instanceof XDDFBarChartData.Series); + String formula = ((XDDFBarChartData.Series) series).getCategoryData().getFormula(); + assertTrue("should contain new sheet name", formula.startsWith("'Sheet1-Renamed'!")); + read.close(); + wb.close(); + } + private static final int INDEX_NOT_FOUND = -1; private static boolean isEmpty(CharSequence cs) { @@ -1009,22 +1031,22 @@ public final class TestXSSFWorkbook extends BaseTestXWorkbook { final String filename = "SampleSS.xlsx"; final File file = POIDataSamples.getSpreadSheetInstance().getFile(filename); Workbook wb; - + // Some tests commented out because close() modifies the file // See bug 58779 - + // String //wb = new XSSFWorkbook(file.getPath()); //assertCloseDoesNotModifyFile(filename, wb); - + // File //wb = new XSSFWorkbook(file); //assertCloseDoesNotModifyFile(filename, wb); - + // InputStream wb = new XSSFWorkbook(new FileInputStream(file)); assertCloseDoesNotModifyFile(filename, wb); - + // OPCPackage //wb = new XSSFWorkbook(OPCPackage.open(file)); //assertCloseDoesNotModifyFile(filename, wb); @@ -1070,7 +1092,7 @@ public final class TestXSSFWorkbook extends BaseTestXWorkbook { XSSFTable table2 = wb.getSheet("Foglio2").createTable(); table2.setName("Table2"); assertSame("Did not find Table2", table2, wb.getTable("Table2")); - + // If table name is modified after getTable is called, the table can only be found by its new name // This test makes sure that if any caching is done that getTable never uses a stale cache table1.setName("Table1"); diff --git a/test-data/spreadsheet/60509.xlsx b/test-data/spreadsheet/60509.xlsx new file mode 100755 index 000000000..835d57c09 Binary files /dev/null and b/test-data/spreadsheet/60509.xlsx differ