git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1842548 13f79535-47bb-0310-9956-ffa450edef68tags/REL_4_0_1
@@ -23,4 +23,23 @@ import org.apache.poi.util.Beta; | |||
@Beta | |||
public interface XDDFCategoryDataSource extends XDDFDataSource<String> { | |||
@Override | |||
default int getColIndex() { | |||
return 0; | |||
} | |||
@Override | |||
default boolean isNumeric() { | |||
return false; | |||
} | |||
@Override | |||
default boolean isReference() { | |||
return true; | |||
} | |||
@Override | |||
default String getDataRangeReference() { | |||
return getFormula(); | |||
} | |||
} |
@@ -34,4 +34,6 @@ public interface XDDFDataSource<T> { | |||
int getColIndex(); | |||
String getDataRangeReference(); | |||
String getFormula(); | |||
} |
@@ -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(); | |||
@Override | |||
public boolean isNumeric() { | |||
return false; | |||
} | |||
@Override | |||
public boolean isReference() { | |||
return true; | |||
} | |||
@Override | |||
public int getPointCount() { | |||
return (int) category.getPtCount().getVal(); | |||
} | |||
@Override | |||
public String getPointAt(int index) { | |||
return category.getPtArray(index).getV(); | |||
} | |||
@Override | |||
public String getDataRangeReference() { | |||
return categoryDS.getStrRef().getF(); | |||
} | |||
@Override | |||
public int getColIndex() { | |||
return 0; | |||
} | |||
}; | |||
if (categoryDS.getStrRef() == null) { | |||
return new XDDFCategoryDataSource() { | |||
private CTNumData category = (CTNumData) categoryDS.getNumRef().getNumCache().copy(); | |||
@Override | |||
public boolean isNumeric() { | |||
return true; | |||
} | |||
@Override | |||
public String getFormula() { | |||
return categoryDS.getNumRef().getF(); | |||
} | |||
@Override | |||
public int getPointCount() { | |||
return (int) category.getPtCount().getVal(); | |||
} | |||
@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 getFormula() { | |||
return categoryDS.getStrRef().getF(); | |||
} | |||
@Override | |||
public int getPointCount() { | |||
return (int) category.getPtCount().getVal(); | |||
} | |||
@Override | |||
public String getPointAt(int index) { | |||
return category.getPtArray(index).getV(); | |||
} | |||
}; | |||
} | |||
} | |||
public static XDDFNumericalDataSource<Double> 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 <T extends Number> XDDFNumericalDataSource<T> fromArray(T[] elements, String dataRange) { | |||
return new NumericalArrayDataSource<T>(elements, dataRange); | |||
return new NumericalArrayDataSource<>(elements, dataRange); | |||
} | |||
public static XDDFCategoryDataSource fromArray(String[] elements, String dataRange) { | |||
@@ -132,7 +148,7 @@ public class XDDFDataSourcesFactory { | |||
} | |||
public static <T extends Number> XDDFNumericalDataSource<T> fromArray(T[] elements, String dataRange, int col) { | |||
return new NumericalArrayDataSource<T>(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<T> implements XDDFDataSource<T> { | |||
@@ -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); |
@@ -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)} | |||
* <p> | |||
* <p> | |||
@@ -81,6 +89,20 @@ public final class XSSFFormulaUtils { | |||
} | |||
} | |||
} | |||
// update charts | |||
List<POIXMLDocumentPart> rels = _wb.getSheetAt(sheetIndex).getRelations(); | |||
for (POIXMLDocumentPart r : rels) { | |||
if (r instanceof XSSFDrawing) { | |||
XSSFDrawing dg = (XSSFDrawing) r; | |||
Iterator<XSSFChart> 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: <a href="https://bz.apache.org/bugzilla/show_bug.cgi?id=54470">Bug 54470</a> | |||
*/ | |||
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); | |||
} | |||
} | |||
} |
@@ -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"); |